#include "foc/limit.h"
#include "foc/core/controller.h"
#include "foc/motor/motor.h"
#include "foc/motor/motor_param.h"
#include "foc/samples.h"
#include "foc/mc_error.h"
#include "libs/logger.h"

static temp_lim_t motor_tl;
static temp_lim_t mosfet_t;
static vol_limt_t vbus;
static bool _inited = false;
static bool _can_recovery = true;

static void limiter_init(void) {
	if (_inited) {
		return;
	}
	motor_tl.high = mc_conf()->p_mot[0].enter_pointer;
	motor_tl.high_lim = mc_conf()->p_mot[0].limit_value;
	motor_tl.mid = mc_conf()->p_mot[1].enter_pointer;
	motor_tl.mid_lim = mc_conf()->p_mot[1].limit_value;
	motor_tl.start = mc_conf()->p_mot[2].enter_pointer;
	motor_tl.start_lim = mc_conf()->p_mot[2].limit_value;
	motor_tl.curr_lim = 1.0f;
	motor_tl.temp = sample_motor_temp();
	sys_debug("mot: %d-%d, %d-%d, %d-%d\n", motor_tl.high, motor_tl.high_lim, motor_tl.mid, motor_tl.mid_lim, motor_tl.start, motor_tl.start_lim);
	mosfet_t.high = mc_conf()->p_mos[0].enter_pointer;
	mosfet_t.high_lim = mc_conf()->p_mos[0].limit_value;
	mosfet_t.mid = mc_conf()->p_mos[1].enter_pointer;
	mosfet_t.mid_lim = mc_conf()->p_mos[1].limit_value;
	mosfet_t.start = mc_conf()->p_mos[2].enter_pointer;
	mosfet_t.start_lim = mc_conf()->p_mos[2].limit_value;
	mosfet_t.curr_lim = 1.0f;
	mosfet_t.temp = sample_mos_temp();
	sys_debug("mos: %d-%d, %d-%d, %d-%d\n", mosfet_t.high, mosfet_t.high_lim, mosfet_t.mid, mosfet_t.mid_lim, mosfet_t.start, mosfet_t.start_lim);
	vbus.crit_low = mc_conf()->c.min_dc_vol;
	vbus.lower = mc_conf()->p_vol.enter_pointer;
	vbus.lower_lim = mc_conf()->p_vol.limit_value;
	sys_debug("vbus: %d, %d-%d\n", vbus.crit_low, vbus.lower, vbus.lower_lim);
	_inited = true;
}

static bool temp_limit_check(temp_lim_t *limit, s16 temp) {
	if (!limit->is_limit) {
		if (temp < limit->start) {
			limit->lim_ticks = 0;
		}else {
			if (++limit->lim_ticks >= 1) {
				limit->is_limit = true;
				limit->lim_ticks = 0;
			}
		}
	}
	if (limit->is_limit) {
		if (temp < limit->start) {
			if (++limit->lim_ticks >= 1) {
				limit->is_limit = false;
				limit->lim_ticks = 0;
			}
		}else {
			limit->lim_ticks = 0;
		}
	}

	return limit->is_limit;
}

#define TEMP_SENSOR_ERR_CNT 20

float get_temp_limit_value(temp_lim_t *limit, s16 temp, s16 err_temp) {
	if ((temp == err_temp) || ABS(temp - limit->temp) >= 20) {
		limit->temp = temp;
		if (limit->temp_sensor_err < TEMP_SENSOR_ERR_CNT) {
			limit->temp_sensor_err++;
			return limit->curr_lim;
		}else {
			return min(limit->curr_lim, 0.5f); //温度传感器异常，返回上次的限流
		}
	}else {
		limit->temp_sensor_err = 0;
	}
	limit->temp = temp;
	if (!temp_limit_check(limit, temp)) {
		limit->curr_lim = 1.0f;
	}else {
		if (temp < limit->start) {
			return limit->curr_lim; //keep prev limit value
		}else if (temp > limit->high) {
			limit->curr_lim = 0;
		}else {
			if (temp >= limit->start && temp <= limit->mid) {
				limit->curr_lim = f_map(temp, limit->start, limit->mid, limit->start_lim, limit->mid_lim)/100.0f;
			}else {
				limit->curr_lim = f_map(temp, limit->mid, limit->high, limit->mid_lim, limit->high_lim)/100.0f;
			}
		}
	}

	return limit->curr_lim;
}

/* this maybe limit power or torque, based on the current power */
float motor_temp_high_limit(void) {
	limiter_init();
	s16 temp = get_motor_temp_raw();
	float value = get_temp_limit_value(&motor_tl, temp, 300);
	if (motor_tl.temp_sensor_err == TEMP_SENSOR_ERR_CNT) {
		if (mc_set_critical_error(FOC_CRIT_MOT_TEMP_Sensor)) {
			mc_crit_err_add(FOC_CRIT_MOT_TEMP_Sensor, temp, motor_tl.temp);
		}
	}
	if (value == 0.0f) {
		if (mc_set_critical_error(FOC_CRIT_MOTOR_TEMP_Lim)) {
			mc_crit_err_add(FOC_CRIT_MOTOR_TEMP_Lim, temp, (s16)mot_contrl_get_speed(mot_contrl()));
		}
	}else if (_can_recovery){
		mc_clr_critical_error(FOC_CRIT_MOTOR_TEMP_Lim);
	}

	return value;
}

float motor_temp_high_limit_test(s16 temp) {
	limiter_init();
	float value = get_temp_limit_value(&motor_tl, temp, 300);
	if (motor_tl.temp_sensor_err == TEMP_SENSOR_ERR_CNT) {
		if (mc_set_critical_error(FOC_CRIT_MOT_TEMP_Sensor)) {
			mc_crit_err_add(FOC_CRIT_MOT_TEMP_Sensor, temp, motor_tl.temp);
		}
	}
	if (value == 0.0f) {
		if (mc_set_critical_error(FOC_CRIT_MOTOR_TEMP_Lim)) {
			mc_crit_err_add(FOC_CRIT_MOTOR_TEMP_Lim, temp, (s16)mot_contrl_get_speed(mot_contrl()));
		}
	}else if (_can_recovery){
		mc_clr_critical_error(FOC_CRIT_MOTOR_TEMP_Lim);
	}

	return value;
}


/* limit the max torque(max phase current) */
float mos_temp_high_limit(void) {
	limiter_init();
	s16 temp = get_mos_temp_raw();
	float value = get_temp_limit_value(&mosfet_t, temp, -40);
	if (mosfet_t.temp_sensor_err == TEMP_SENSOR_ERR_CNT) {
		if (mc_set_critical_error(FOC_CRIT_MOS_TEMP_Sensor)) {
			mc_crit_err_add(FOC_CRIT_MOS_TEMP_Sensor, temp, mosfet_t.temp);
		}
	}
	if (value == 0.0f) {
		if (mc_set_critical_error(FOC_CRIT_MOS_TEMP_Lim)) {
			mc_crit_err_add(FOC_CRIT_MOS_TEMP_Lim, temp, (s16)mot_contrl_get_speed(mot_contrl()));
		}
	}else if (_can_recovery){
		mc_clr_critical_error(FOC_CRIT_MOS_TEMP_Lim);
	}

	return value;
}

float mos_temp_high_limit_test(s16 temp) {
	limiter_init();
	float value = get_temp_limit_value(&mosfet_t, temp, -40);
	if (mosfet_t.temp_sensor_err == TEMP_SENSOR_ERR_CNT) {
		if (mc_set_critical_error(FOC_CRIT_MOS_TEMP_Sensor)) {
			mc_crit_err_add(FOC_CRIT_MOS_TEMP_Sensor, temp, mosfet_t.temp);
		}
	}
	if (value == 0.0f) {
		if (mc_set_critical_error(FOC_CRIT_MOS_TEMP_Lim)) {
			mc_crit_err_add(FOC_CRIT_MOS_TEMP_Lim, temp, (s16)mot_contrl_get_speed(mot_contrl()));
		}
	}else if (_can_recovery){
		mc_clr_critical_error(FOC_CRIT_MOS_TEMP_Lim);
	}

	return value;
}


/* limit the DC bus current */
u16 vbus_voltage_low_limit(void) {
	limiter_init();
	s16 vol = (s16)sample_vbus_raw();
	if (!vbus.is_limit && (vol <= vbus.lower)) {
		vbus.is_limit = true;
		if (mc_set_critical_error(FOC_CRIT_UN_Vol_Err)) {
			if (mot_contrl_get_speed(mot_contrl()) > CONFIG_ZERO_SPEED_RPM) {
				mc_crit_err_add(FOC_CRIT_UN_Vol_Err, vol, (s16)mot_contrl_get_speed(mot_contrl()));
			}
		}
	}else if (vbus.is_limit && (vol > vbus.lower)) {
		vbus.is_limit = false;
		if (_can_recovery) {
			mc_clr_critical_error(FOC_CRIT_UN_Vol_Err);
		}
	}
	if (vol > vbus.lower) {
		return HW_LIMIT_NONE;
	}else if (vol < vbus.crit_low) {
		return 0.0f;
	}
	return (u16)f_map(vol, vbus.crit_low, vbus.lower, 0.0f, vbus.lower_lim);
}

u16 vbus_voltage_low_limit_test(s16 vol) {
	limiter_init();
	if (!vbus.is_limit && (vol <= vbus.lower)) {
		vbus.is_limit = true;
		if (mc_set_critical_error(FOC_CRIT_UN_Vol_Err)) {
			if (mot_contrl_get_speed(mot_contrl()) > CONFIG_ZERO_SPEED_RPM) {
				mc_crit_err_add(FOC_CRIT_UN_Vol_Err, vol, (s16)mot_contrl_get_speed(mot_contrl()));
			}
		}
	}else if (vbus.is_limit && (vol > vbus.lower)) {
		vbus.is_limit = false;
		if (_can_recovery) {
			mc_clr_critical_error(FOC_CRIT_UN_Vol_Err);
		}
	}
	if (vol > vbus.lower) {
		return HW_LIMIT_NONE;
	}else if (vol < vbus.crit_low) {
		return 0.0f;
	}
	return (u16)f_map(vol, vbus.crit_low, vbus.lower, 0.0f, vbus.lower_lim);
}


bool motor_temp_limited(void) {
	return motor_tl.is_limit;
}

bool mos_temp_limited(void) {
	return mosfet_t.is_limit;
}
