#include "ikk-relay.h"
#include "ikk-button.h"
#include "ikk-led.h"
#include "ikk-timer.h"
#include "drv_gpio.h"
#include "../general/ikk-tick-handler.h"
#include "../general/ikk-debug.h"

#define RELAY_CHNN_UNUSED_ID		RELAY_CHNN_UNKNOW_ID

//RELAY ZERO DECTED
#define ZERO_TIMEOUT_MS				200


static RelayChannelConfSt g_stRelayChannelConfList[RELAY_OBJS_SUPPORT_MAXN];
static RelayStatusSt g_stRelayStatusList[RELAY_OBJS_SUPPORT_MAXN] = {{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT}};
static RelayStatusSt g_stRelayStatusListBkp[RELAY_OBJS_SUPPORT_MAXN] = {{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT},{RELAY_CHNN_UNKNOW_ID, EZAO_DEFAULT}};

static uint32_t g_u32CheckedZeroCount = 0;  //检测到零点后计数
static bool g_bIsCheckedZeroFlg = false;// 刚上电10S内或没有接220交流电为false,不必检测过零即可触发继电器开关 ,否则true
static uint8_t  g_u8RelayChannelNum = 0;
static uint32_t g_u32RelayDelayOnTimeUS = 0;
static uint32_t g_u32RelayDelayOffTimeUS = 0;

pRelayOptLedCallback g_pRelayOptLedCallback = NULL;

//EmberEventControl kRelayOnOffEventControl;


static uint8_t kRelayGetChannelIndexByID(uint8_t channel_id );
void kRelayIRQCallback(void);
static void kRelayGpioIRQInit( uint32_t pin, RelayPolarityEnum ePolarity);
static void kRelayGpioInit(uint32_t pin, bool out);

static void judgeCheckZeroIsUsefullPoll(uint16_t poll_time_ms, uint16_t time_out_ms);
static void kRelayGpioControl( uint32_t pin, bool status);
static void kRelayChannelControl(uint8_t channel, bool status);

int kRelayUserCallback(void *p);
/*	DESP: get the channel index by channel_id.
 * 	Auth: dingmz_frc.20191107.
 * */
static uint8_t kRelayGetChannelIndexByID(uint8_t channel_id )
{
	for(int Relay_index = 0; Relay_index < RELAY_OBJS_SUPPORT_MAXN; ++Relay_index ) {
		if( g_stRelayChannelConfList[Relay_index].u8RelayChannelId != RELAY_CHNN_UNUSED_ID ) {
			if( g_stRelayChannelConfList[Relay_index].u8RelayChannelId == channel_id ) {
				return Relay_index;
			}
		}else break;
	}

	return 0XFF;
}


/*	DESP: Zero-Access channel operate interface.
	Auth: dingmz_frc.20191108.
*/
void kRelayChannelOpt(uint8_t channel_id, RelayOptEnum opt )
{
	uint8_t chnn_index = kRelayGetChannelIndexByID(channel_id);

	if( chnn_index  == 0xFF) {
		iKonkeAfSelfPrint("Err:Relay OPT\r\n");
		return ;
	}
	g_stRelayStatusList[chnn_index].opt = opt;
	g_stRelayStatusList[chnn_index].u8RelayChannelId = channel_id;
	if (g_bIsCheckedZeroFlg == true){
		//delay on off relay when  zero detected, on or off delay time is different.
		//emberEventControlSetDelayMS(kRelayOnOffEventControl, ZERO_TIMEOUT_MS);//add by maozj 20200314
		iKonkeAfSelfPrint("####Relay Module Checked Zero Gpio count(%d)\r\n", g_u32CheckedZeroCount);
	}else {
		//direct on off relay when not detect zero interrupt
		iKonkeAfSelfPrint("####Relay Modul Not Checked Zero Gpio count(%d)\r\n", g_u32CheckedZeroCount);
		memcpy(&g_stRelayStatusListBkp[chnn_index], &g_stRelayStatusList[chnn_index], sizeof(RelayStatusSt));
		if( (g_stRelayStatusListBkp[chnn_index].opt ==g_stRelayStatusList[chnn_index].opt)
		   &&(g_stRelayStatusListBkp[chnn_index].u8RelayChannelId ==g_stRelayStatusList[chnn_index].u8RelayChannelId))
			iKonkeAfSelfPrint("####memcpy success\r\n");
		g_stRelayStatusList[chnn_index].opt = EZAO_DEFAULT;
#if 0
		emberEventControlSetDelayMS(kRelayOnOffEventControl, 10);
#else
		if (kTimerIsGoing(SELF_TIMER1) != true){
			kTimerStart(SELF_TIMER1,(g_stRelayStatusListBkp[chnn_index].opt ==  EZAO_OFF)?g_u32RelayDelayOffTimeUS:g_u32RelayDelayOnTimeUS,kRelayUserCallback);
		}
#endif
	}
}
int kRelayUserCallback(void *p)
{
	uint8_t *timer = (uint8_t *)p;
	kTimerStop((TIMER_TypeEnum)*timer);
	//iKonkeAfSelfPrint("\r\n####kRelayUserCallback!!\r\n" );
	for (uint8_t i = 0; i < g_u8RelayChannelNum; i++){
		if (g_stRelayStatusListBkp[i].opt == EZAO_ON || g_stRelayStatusListBkp[i].opt == EZAO_OFF){
//			iKonkeAfSelfPrint("\r\n####Relay-OPT 44444: channel(%d), operate(%d) count(%d)\r\n" \
//					, g_stRelayStatusListBkp[i].u8RelayChannelId, g_stRelayStatusListBkp[i].opt, g_u32CheckedZeroCount);
			RelayOptEnum opt = g_stRelayStatusListBkp[i].opt;
			kRelayChannelControl(g_stRelayStatusListBkp[i].u8RelayChannelId, opt ==  EZAO_OFF?false:true);
			//operate led status
			if (g_pRelayOptLedCallback){
				g_pRelayOptLedCallback(g_stRelayStatusListBkp[i]);
			}

			g_stRelayStatusListBkp[i].opt = EZAO_DEFAULT;
		}
	}
	return 0;
}

void kRelayIRQCallback(void )
{
	if (g_u32CheckedZeroCount++ > 0xFFFFFFF){
		g_u32CheckedZeroCount = 0;
	}
#if 1
	for (uint8_t i = 0; i < g_u8RelayChannelNum; i++)
	{
		if (g_stRelayStatusList[i].opt == EZAO_ON || g_stRelayStatusList[i].opt == EZAO_OFF){
			//CORE_irqState_t state = CORE_EnterCritical();
			memcpy(&g_stRelayStatusListBkp, &g_stRelayStatusList, sizeof(g_stRelayStatusListBkp));
			RelayOptEnum opt = g_stRelayStatusListBkp[i].opt;
			g_stRelayStatusList[i].opt = EZAO_DEFAULT;

			if (kTimerIsGoing(SELF_TIMER1) != true){
				kTimerStart(SELF_TIMER1,(opt ==  EZAO_OFF)?g_u32RelayDelayOffTimeUS:g_u32RelayDelayOnTimeUS,kRelayUserCallback);
			}
			break;
		}
	}
#endif
}
/*	DESP: zero detect gpio init interface.
 * 	Auth: maozj.20191108.
 * */
static void kRelayGpioIRQInit( uint32_t pin, RelayPolarityEnum ePolarity)
{
	if (pin == PIN_UNKNOW){
		iKonkeAfSelfPrint("###Erro:kRelayGpioIRQInit Pin\r\n");
		return;
	}
	GPIO_INPUT_SET(pin,(ePolarity == EZAP_HIGH)?0:1);
	/* Register callbacks before setting up and enabling pin interrupt.  ef32mg21 max pin is 7*/

	if(ePolarity == EZAP_HIGH ) {
		/* Set rising and falling edge interrupts */
		drv_gpio_irq_config(GPIO_IRQ_MODE,pin,RISING_EDGE,kRelayIRQCallback);
	}else  if (ePolarity == EZAP_LOW) {
		/* Set rising and falling edge interrupts */
		drv_gpio_irq_config(GPIO_IRQ_MODE,pin,FALLING_EDGE,kRelayIRQCallback);
	}
	drv_gpio_irq_en(pin);
}

static void kRelayGpioInit(uint32_t pin, bool out)
{
	GPIO_OUTPUT_SET(pin,out);
}

static void kRelayGpioControl( uint32_t pin, bool status)
{
	GPIO_OUTPUT_SET(pin,status);
}

static void kRelayChannelControl(uint8_t channel, bool status)
{
	uint8_t channelIndex = kRelayGetChannelIndexByID(channel);
	if (channelIndex == 0xFF){
		iKonkeAfSelfPrint("Erro:Get Channel Index\r\n");
		return;
	}

	RelayPolarityEnum ePolarity = g_stRelayChannelConfList[channelIndex].eActionPolarity;
	if (ePolarity == EZAP_LOW){
		status = !status;
	}

	for (uint8_t i = 0; i < g_stRelayChannelConfList[channelIndex].u8GpioMappNum; i++){
		if (g_stRelayChannelConfList[channelIndex].pin[i] != PIN_UNKNOW){
			iKonkeAfSelfPrint("Control Relay:Channel = %d, OPT = %d\r\n",channel,status);
			kRelayGpioControl(g_stRelayChannelConfList[channelIndex].pin[i], status);
		}
	}
}
/*	DESP: Relay control module Initialize configuration interface.alse zero gpio is not necessary
 * 	PARAM[port]: the GPIO port for Zero-Access detection.
 * 	PARAM[pin]: the GPIO pin for Zero-Access detection.
 *  PARAM[zero_on_time]: relay delay on time when get zero interrupt
 *  PARAM[zero_off_time]: relay delay off time when get zero interrupt
 *  PARAM[ePolarity]: the Zero GPIO pin interrupt edge
 * 	PARAM[list]: the config list of all relay dection
 * 	PARAM[channel_num]: total group number of relay.
 * 	PARAM[callback]: callback function after relay operate done
 * 	Auth: maozj.20191115.
 * */
kk_err_t kRelayModuleInit(uint32_t pin, uint32_t zero_on_time, uint32_t zero_off_time \
		, RelayPolarityEnum ePolarity,  RelayChannelConfSt *list, uint8_t channel_num,  pRelayOptLedCallback callback)
{
	kk_err_t err = KET_OK;

	memset(g_stRelayChannelConfList, RELAY_CHNN_UNUSED_ID, sizeof(g_stRelayChannelConfList));

	if( list && (channel_num > 0)) {
		g_u8RelayChannelNum = (channel_num < RELAY_OBJS_SUPPORT_MAXN)?channel_num:RELAY_OBJS_SUPPORT_MAXN;

		memcpy(g_stRelayChannelConfList, list, sizeof(RelayChannelConfSt) * g_u8RelayChannelNum);
	}else {
		err = KET_FAILED;
		iKonkeAfSelfPrint("Err: Relay Parameter\r\n");
	}
	//interrupt init zero detect gpio
	kRelayGpioIRQInit(pin, ePolarity);

	for (uint8_t i = 0; i < g_u8RelayChannelNum; i++)
	{
		iKonkeAfSelfPrint("Relai id =%d,num = %d,pin = %d,eActionPolarity = %d\r\n",g_stRelayChannelConfList[i].u8RelayChannelId,g_stRelayChannelConfList[i].u8GpioMappNum
				,g_stRelayChannelConfList[i].pin[0],g_stRelayChannelConfList[i].eActionPolarity);

		for (uint8_t j = 0; j < g_stRelayChannelConfList[i].u8GpioMappNum; j++){
//			GPIO_OUTPUT_SET(g_stRelayChannelConfList[i].pin[j],g_stRelayChannelConfList[i].eActionPolarity == EZAP_LOW?true:false);
//			kRelayGpioInit(g_stRelayChannelConfList[i].pin[j],g_stRelayChannelConfList[i].eActionPolarity == EZAP_LOW?true:false);
		}
	}
	if(pin != NULL)
		kTimerInit(SELF_TIMER1);
	g_pRelayOptLedCallback = callback;
	g_u32RelayDelayOnTimeUS = zero_on_time;
	g_u32RelayDelayOffTimeUS = zero_off_time;
	return err;
}


/*	DESP: Relay module action detection task callback interface.
 * 	Auth: maozj.20191107.
 * */
void kRelayModuleActionDetectCallback(uint8_t poll_time)
{
	judgeCheckZeroIsUsefullPoll( poll_time, ZERO_TIMEOUT_MS);
}

static void judgeCheckZeroIsUsefullPoll(uint16_t poll_time_ms, uint16_t time_out_ms)
{
	static uint32_t u32LastCheckZeroCount = 0;
	static uint8_t u8GetCheckZeroCounter = 0;
	static uint16_t u32CheckZeroTimeoutCounter = 0;
	static bool bStartRecordCountFlg = true;

	if (poll_time_ms == 0){
		return;
	}

	if (time_out_ms/poll_time_ms < 5){
		return;
	}
#if 1
	//5轮记一次过零检测计数,
	if (bStartRecordCountFlg == true && u8GetCheckZeroCounter++ > 5){
		u8GetCheckZeroCounter = 0;
		if (u32LastCheckZeroCount != g_u32CheckedZeroCount){
			u32CheckZeroTimeoutCounter = 0;
			g_bIsCheckedZeroFlg = true;
			u32LastCheckZeroCount = g_u32CheckedZeroCount;
		}
		bStartRecordCountFlg = false;
	}
	//接着200MS过后判断是否还有过零,再接着记录过零检测计数
	if (bStartRecordCountFlg == false  && u32CheckZeroTimeoutCounter++ > time_out_ms/poll_time_ms){
		u32CheckZeroTimeoutCounter = 0;
		u8GetCheckZeroCounter = 0;

		if (u32LastCheckZeroCount == g_u32CheckedZeroCount && g_bIsCheckedZeroFlg == true){
			g_bIsCheckedZeroFlg = false;

			//在过零检测超时后直接获取命令数据动作
			memcpy(&g_stRelayStatusListBkp, &g_stRelayStatusList, sizeof(g_stRelayStatusListBkp));
			memset(&g_stRelayStatusList, 0xFF, sizeof(g_stRelayStatusList));

			if (kTimerIsGoing(SELF_TIMER1) != true){

				kTimerStart(SELF_TIMER1,g_u32RelayDelayOnTimeUS,kRelayUserCallback);
			}
		}else if (u32LastCheckZeroCount != g_u32CheckedZeroCount && g_bIsCheckedZeroFlg == false){
			g_bIsCheckedZeroFlg = true;
		}
		bStartRecordCountFlg = true;
		u32LastCheckZeroCount = g_u32CheckedZeroCount;
	}
#else
	g_bIsCheckedZeroFlg = true;
#endif
}

bool kRelayIsCheckedZero(void)
{
	return g_bIsCheckedZeroFlg;
}


