#include <math.h>
#include "bsp/bsp_driver.h"
#include "foc/motor/current.h"
#include "foc/core/PMSM_FOC_Core.h"
#include "foc/mc_error.h"
#include "libs/utils.h"
#include "libs/logger.h"
#include "math/fast_math.h"
current_samp_t g_cs;

#define NB_OFFSET_SAMPLES 32
#define SENSOR_SAMPLES 10000

void phase_current_init(void) {
	current_samp_t *cs = &g_cs;
	cs->sample_count = NB_OFFSET_SAMPLES + 1;
	cs->adc_ia = 0;
	cs->adc_ib = 0;
	cs->adc_ic = 0;
}

void phase_current_offset_calibrate(void){
	g_cs.adc_offset_a = 0;
	g_cs.adc_offset_b = 0;
	g_cs.adc_offset_c = 0;

	phase_current_init();
	g_cs.c_phases = PHASE_BC;
	g_cs.c_ignore_phase = IGNORE_NONE;
	adc_current_sample_config(g_cs.c_phases);
	g_cs.is_calibrating_offset = true;
}

void phase_current_calibrate_wait(void) {
	while(g_cs.is_calibrating_offset || g_cs.is_calibrating_sensor) {
		wdog_reload();
	}
	sys_debug("offset %d, %d, %d\n", g_cs.adc_offset_a, g_cs.adc_offset_b, g_cs.adc_offset_c);
}

void phase_current_sensor_start_calibrate(float calibrate_current) {
	bool calibrate = false;
	if (calibrate_current > 0) {
		calibrate = true;
		g_cs.sensor_samples_1 = 0;
		g_cs.sensor_samples_2 = 0;
		g_cs.sample_count = SENSOR_SAMPLES + 1;
		g_cs.calibrate_current = calibrate_current;
	}
	
	g_cs.is_calibrating_sensor = calibrate;
}

bool phase_current_sensor_do_calibrate(void) {
	current_samp_t *cs = &g_cs;
	if (!cs->is_calibrating_sensor) {
		return false;
	}
	s32 phase_current1, phase_current2;
	adc_phase_current_read(cs->c_phases, &phase_current1, &phase_current2);
	if(cs->c_phases == PHASE_BC) {
		if (cs->sample_count > 0) {
			cs->sample_count--;
			if (cs->sample_count <= SENSOR_SAMPLES) {
				cs->sensor_samples_1 += (phase_current1 - cs->adc_offset_b);
				cs->sensor_samples_2 += (phase_current2 - cs->adc_offset_c);
			}
		}else {
			cs->sensor_samples_1 = cs->sensor_samples_1 / (float)SENSOR_SAMPLES;
			cs->sensor_samples_2 = cs->sensor_samples_2 / (float)SENSOR_SAMPLES;
			cs->sensor_k1 = g_cs.calibrate_current/cs->sensor_samples_1;
			cs->sensor_k2 = g_cs.calibrate_current/cs->sensor_samples_2;
			cs->sensor_k1 = ABS(cs->sensor_k1);
			cs->sensor_k2 = ABS(cs->sensor_k2);
			cs->is_calibrating_sensor = false;
		}
	}
	return cs->is_calibrating_sensor;
}

bool phase_current_offset(void) {
	current_samp_t *cs = &g_cs;
	if (!cs->is_calibrating_offset) {
		return false;
	}
	s32 phase_current1 = 0 , phase_current2 = 0;
	adc_phase_current_read(cs->c_phases, &phase_current1, &phase_current2);
	if (cs->sample_count == (NB_OFFSET_SAMPLES + 1)) {
		cs->sample_count --;
		return true;
	}
	if (cs->sample_count > 0) {
		cs->sample_count--;
		if (cs->c_phases == PHASE_AB && cs->sample_count >= 0) {
			cs->adc_offset_a += phase_current1;
			cs->adc_offset_b += phase_current2;
			if (cs->sample_count == 0) {
				cs->adc_offset_a = cs->adc_offset_a / NB_OFFSET_SAMPLES;
				cs->adc_offset_b = cs->adc_offset_b / NB_OFFSET_SAMPLES;
			}
		}
		if (cs->c_phases == PHASE_BC && cs->sample_count >= 0) {
			cs->adc_offset_c += phase_current2;
			cs->adc_offset_b += phase_current1;
			if (cs->sample_count == 0) {
				cs->adc_offset_c = cs->adc_offset_c / NB_OFFSET_SAMPLES;
				cs->adc_offset_b = cs->adc_offset_b / NB_OFFSET_SAMPLES;
			}
		}
	}else {
		if (cs->c_phases == PHASE_AB) {
			cs->c_phases = PHASE_BC;
			phase_current_init();
			adc_current_sample_config(cs->c_phases);
		}else {
			cs->is_calibrating_offset = false;
		}

	}
	return true;
}


bool phase_curr_offset_check(void) {
	if ((g_cs.adc_offset_b > ADC_FULL_MAX/2 + 100) || (g_cs.adc_offset_c > ADC_FULL_MAX/2 + 100)) {
		err_add_record(FOC_CRIT_CURR_OFF_Err, MAX(g_cs.adc_offset_c, g_cs.adc_offset_b));
		return true;
	}
	if ((g_cs.adc_offset_b < ADC_FULL_MAX/2 - 100) || (g_cs.adc_offset_c < ADC_FULL_MAX/2 - 100)) {
		err_add_record(FOC_CRIT_CURR_OFF_Err, min(g_cs.adc_offset_c, g_cs.adc_offset_b));
		return true;
	}
	return false;
}

void phase_current_get(float *iABC){
	current_samp_t *cs = &g_cs;
	s32 phase_current1, phase_current2;

	adc_phase_current_read(cs->c_phases, &phase_current1, &phase_current2);

	cs->adc_ib = (phase_current1 - cs->adc_offset_b);
	cs->adc_ic = (phase_current2 - cs->adc_offset_c);
#if (CONFIG_HW_VERSION==3)
#ifdef CONFIG_PWM_UV_SWAP
	if (gpio_board_id() == BOARD_105_VERSION_3) {
		iABC[1] = -cs->adc_ib * ADC_TO_CURR_ceof1;
		iABC[0] = -cs->adc_ic * ADC_TO_CURR_ceof2;
		iABC[2] = -(iABC[1] + iABC[0]);
	}else {
		iABC[2] = -cs->adc_ib * ADC_TO_CURR_ceof1;
		iABC[0] = -cs->adc_ic * ADC_TO_CURR_ceof2;
		iABC[1] = -(iABC[0] + iABC[2]);
	}
#else
	if (gpio_board_id() == BOARD_105_VERSION_3) {
		iABC[1] = -cs->adc_ib * ADC_TO_CURR_ceof1;
		iABC[2] = -cs->adc_ic * ADC_TO_CURR_ceof2;
		iABC[0] = -(iABC[1] + iABC[2]);
	}else {
		iABC[0] = -cs->adc_ib * ADC_TO_CURR_ceof1;
		iABC[2] = -cs->adc_ic * ADC_TO_CURR_ceof2;
		iABC[1] = -(iABC[0] + iABC[2]);
	}
#endif
#else
#ifdef CONFIG_PWM_UV_SWAP
	iABC[1] = -cs->adc_ib * ADC_TO_CURR_ceof1;
	iABC[2] = -cs->adc_ic * ADC_TO_CURR_ceof2;
	iABC[0] = -(iABC[1] + iABC[2]);
#else
	iABC[1] = -cs->adc_ib * ADC_TO_CURR_ceof1;
	iABC[0] = -cs->adc_ic * ADC_TO_CURR_ceof2;
	iABC[2] = -(iABC[1] + iABC[0]);
#endif
#endif
}

void phase_current_point(void *p){
	FOC_OutP *out = p;
	current_samp_t *cs = &g_cs;
	
	cs->c_phases = PHASE_BC;
	out->n_Sample1 = FOC_PWM_Half_Period - 1;
	out->n_Sample2 = FOC_PWM_Half_Period + 1;
	out->n_CPhases = cs->c_phases;
}

void phase_current_adc_triger(void){
	adc_enable_ext_trigger();
}

