/*
 *  Copyright (C) 2006 by Nagoya University Extension Course for
 *                       Embedded Software Specialists(NEXCESS), JAPAN
 *                2014 by Nagoya University
 *                        Graduate School of Information Science
 *                        Center for Embedded Computing Systems(NCES), JAPAN
 *                2014 by Mitsuhiro Matsuura
 */

/*
 * カップラーメンタイマ
 */
#include <kernel.h>
#include "kernel_cfg.h"
#include <t_syslog.h>
#include <t_stdlib.h>
#include "cup_timer.h"
#include "device.h"
#include "limits.h"

/*
 *  API Error Log Output Function
 */
Inline void
svc_perror(const char *file, int_t line, const char *expr, ER ercd)
{
	if (ercd < 0) {
		t_perror(LOG_ERROR, file, line, expr, ercd);
	}
}

#define SVC_PERROR(expr)	svc_perror(__FILE__, __LINE__, #expr, (expr))

#define DEBUG_UART  /* シリアルポートにデバッグログを出力 */
#ifdef DEBUG_UART
#define DEBUG_SYSLOG(prio, ...)                            \
	do {                                                   \
		syslog(prio,"%s() on line %d of the %s",           \
			__func__,__LINE__,__FILE__);                   \
        syslog(prio, ##__VA_ARGS__);                       \
	} while (0)
#else
#define DEBUG_SYSLOG(prio, ...)
#endif /* DEBUG_UART */

/*
 *  Glboal Variable
 */
unsigned char sw1;
unsigned char sw8;

/* システム時刻による時間の測定(デバッグ用) */
static SYSUTM gStartTime;
static SYSUTM gCurrentTime;

/*
 *  Timer Task
 */
TTM_CTL		gtm_ctl;

void
timer_task_init(intptr_t exinf){
	GTM_CTLF = 0;
	GTM_REMTM = 0;
	GTM_NOWSTS = ST_TIMER_OFF;
	GTM_NEXTSTS = ST_TIMER_OFF;
}

static void
StOffEvSw1Off(void){
	/* メモリプール解放禁止を設定 */
	GTM_CTLF |= DTM_NOTREL;

	/* 受信したメッセージのイベントを書き換えて転送 */
	GTM_RCVEV =  EV_BLINK_OFF;
    DEBUG_SYSLOG(LOG_NOTICE, " Timer_task send data           = 0x%p", GTM_RCVAD);
	SVC_PERROR(snd_dtq(BLINK_DTQ, (intptr_t)GTM_RCVAD));
	DEBUG_SYSLOG(LOG_NOTICE, " Timer stop!");
}

static void
StOnEvSw1Off(void){
	/* 状態遷移有り,メモリプール解放禁止を設定 */
	GTM_CTLF |= (DTM_STSCHG | DTM_NOTREL);

	GTM_NEXTSTS = ST_TIMER_OFF;	/* 次状態を「タイマOFF」状態に設定 	*/

	/* 受信したメッセージのイベントを書き換えて転送 */
	GTM_RCVEV = EV_BLINK_OFF;

    DEBUG_SYSLOG(LOG_NOTICE, " Timer_task send data           = 0x%p", GTM_RCVAD);
	SVC_PERROR(snd_dtq(BLINK_DTQ, (intptr_t)GTM_RCVAD));
	DEBUG_SYSLOG(LOG_NOTICE, " Timer stop!");
	GTM_REMTM = 0;
	stp_cyc(BASE_TIME_HANDLER);
}

static void
StOffEvSw1On(void){
	/* 状態遷移有り,メモリプール解放禁止を設定 */
	GTM_CTLF |= (DTM_STSCHG | DTM_NOTREL);

	GTM_NEXTSTS = ST_TIMER_ON;	/* 次状態を「タイマON」状態に設定 	*/

	GTM_REMTM = INIT_TIME;		/* タイムアウト時間を初期値に設定 	*/

	/* 受信したメッセージのイベントを書き換えて転送 */
	GTM_RCVEV = EV_BLINK_ACTIVE;

    DEBUG_SYSLOG(LOG_NOTICE, " Timer_task send data           = 0x%p", GTM_RCVAD);
	SVC_PERROR(snd_dtq(BLINK_DTQ, (intptr_t)GTM_RCVAD));
	DEBUG_SYSLOG(LOG_NOTICE, " Timer start!");
	SVC_PERROR(get_utm(&gStartTime)); /* スタート時刻を記憶 */
	sta_cyc(BASE_TIME_HANDLER);
}

static void
StOnEvPush1On(void){
	GTM_REMTM = GTM_REMTM + EXTRA_UNIT;
	DEBUG_SYSLOG(LOG_NOTICE, " Extend %d sec. Remaining  %d sec",
	EXTRA_UNIT, GTM_REMTM);
}

static void
StOnEvBaseTime(void){
	GTM_REMTM = GTM_REMTM - 1;
	if (GTM_REMTM == 0) {
		SVC_PERROR(get_utm(&gCurrentTime));
		DEBUG_SYSLOG(LOG_NOTICE, " System measurement time : %lu%s", gCurrentTime - gStartTime,"us");

		/* 状態遷移有り,メモリプール解放禁止を設定 */
		GTM_CTLF |= (DTM_STSCHG | DTM_NOTREL);
		GTM_NEXTSTS = ST_TIMER_OFF;	/* 次状態を「タイマOFF」状態に設定 	*/

		/* 受信したメッセージのイベントを書き換えて転送 */
		GTM_RCVEV = EV_BLINK_TIMEOUT;

        DEBUG_SYSLOG(LOG_NOTICE, " Timer_task send data           = 0x%p", GTM_RCVAD);
		SVC_PERROR(snd_dtq(BLINK_DTQ, (intptr_t)GTM_RCVAD));
		DEBUG_SYSLOG(LOG_NOTICE, " Timer timeout");
		stp_cyc(BASE_TIME_HANDLER);
	} else if ((GTM_REMTM % ACT_INTERVAL) == 0) {
		SVC_PERROR(get_utm(&gCurrentTime));
		DEBUG_SYSLOG(LOG_NOTICE, " System measurement time : %lu%s", gCurrentTime - gStartTime,"us");

		/* メモリプール解放禁止を設定 */
		GTM_CTLF |= DTM_NOTREL;

		/* 受信したメッセージのイベントを書き換えて転送 */
		GTM_RCVEV = EV_BLINK_ACTIVE;

        DEBUG_SYSLOG(LOG_NOTICE, " Timer_task send data           = 0x%p", GTM_RCVAD);
		SVC_PERROR(snd_dtq(BLINK_DTQ, (intptr_t)GTM_RCVAD));
    	DEBUG_SYSLOG(LOG_NOTICE, " Remaining  %d sec", GTM_REMTM);
    }
}

/* タイマタスクのジャンプテーブルの定義 */
const CALLBACK_TM TMStateTable[ST_TIMER_NUM][EV_TIMER_NUM]={
/*   SW1_OFF,           SW1_ON,          PUSH1_ON,         1秒経過 */
	{&StOffEvSw1Off,	&StOffEvSw1On, 	 NULL,			   NULL				},/* タイマOFF状態	*/
    {&StOnEvSw1Off,		NULL,			 &StOnEvPush1On,   &StOnEvBaseTime	},/* タイマON状態	*/
};

void
timer_task(intptr_t exinf)
{
	const CALLBACK_TM *pFunc;

	DEBUG_SYSLOG(LOG_NOTICE, " Cup Timer 6 start!");

	for (;;) {
        SVC_PERROR(rcv_dtq(TIMER_DTQ, (intptr_t *)&GTM_RCVAD));
		DEBUG_SYSLOG(LOG_NOTICE, " Timer_task recive data         = 0x%p", GTM_RCVAD);

		pFunc = &TMStateTable[GTM_NOWSTS][GTM_RCVEV];
		if(NULL != *pFunc){
			(*pFunc)();
		} else {
			DEBUG_SYSLOG(LOG_NOTICE, " Timer_Task ignore event. event = %d ",GTM_RCVEV);
        }
		if(GTM_CTLF & DTM_STSCHG) {		/* 状態遷移要求有り? */
			GTM_CTLF &= ~DTM_STSCHG;	/* 要求クリア		 */
			GTM_NOWSTS = GTM_NEXTSTS;   /* 状態更新 		 */
		}
		if(GTM_CTLF & DTM_NOTREL){		/* メモリプール解放禁止? */
			GTM_CTLF &= ~DTM_NOTREL;	/* 要求クリア		 */
			DEBUG_SYSLOG(LOG_NOTICE, " Timer_Task is not call rel_mpf.");
		}
		else{
			SVC_PERROR(rel_mpf(EVENT_MPF, GTM_RCVAD));
		}
	}
}

/*
 *  LED4 Blink Task
 */
TBL_CTL		gbl_ctl;

void
blink_task_init(intptr_t exinf){
	GBL_CTLF = 0;
	GBL_BCOUNT = 0;
	GBL_NOWSTS = ST_BLINK_IDLE;
	GBL_NEXTSTS = ST_BLINK_IDLE;
}

static void
StIdleEvTimeOut(void){
	GBL_NEXTSTS = ST_BLINK_LED_OFF;
	GBL_CTLF |= DBL_STSCHG;		/* 状態遷移有りを設定			 	*/

	GBL_BCOUNT = TIMEOUT_BLINK;
	sta_cyc(LED4_BLINK_TIME_HANDLER);
}

static void
StIdleEvAvtive(void){
	GBL_NEXTSTS = ST_BLINK_LED_OFF;
	GBL_CTLF |= DBL_STSCHG;		/* 状態遷移有りを設定			 	*/

	GBL_BCOUNT = ACT_BLINK_TIME;
	sta_cyc(LED4_BLINK_TIME_HANDLER);
}

static void
StLedOffEvOff(void){
	set_led4_state(OFF);
	GBL_NEXTSTS = ST_BLINK_IDLE;
	GBL_CTLF |= DBL_STSCHG;		/* 状態遷移有りを設定			 	*/

	GBL_BCOUNT = 0;
	stp_cyc(LED4_BLINK_TIME_HANDLER);
}

#define StLedOnEvOff StLedOffEvOff

static void
StLedOffEvBlink(void){
	GBL_BCOUNT--;
	if(GBL_BCOUNT == 0){
		GBL_NEXTSTS = ST_BLINK_IDLE;
		stp_cyc(LED4_BLINK_TIME_HANDLER);
	} else {
		set_led4_state(ON);
		GBL_NEXTSTS = ST_BLINK_LED_ON;
	}
	GBL_CTLF |= DBL_STSCHG;		/* 状態遷移有りを設定			 	*/
}

static void
StLedOnEvBlink(void){
	set_led4_state(OFF);
	GBL_BCOUNT--;
	if(GBL_BCOUNT == 0){
		GBL_NEXTSTS = ST_BLINK_IDLE;
		stp_cyc(LED4_BLINK_TIME_HANDLER);
	} else {
		GBL_NEXTSTS = ST_BLINK_LED_OFF;
	}
	GBL_CTLF |= DBL_STSCHG;		/* 状態遷移有りを設定			 	*/
}

/* ブリンクタスクのジャンプテーブルの定義 */
const CALLBACK_BL BLStateTable[ST_BLINK_NUM][EV_BLINK_NUM]={
/*   TIMEOUT通知,       ACTIVE通知,      OFF通知,          0.25秒経過 */
	{&StIdleEvTimeOut,  &StIdleEvAvtive, NULL,			   NULL				},/* 休止状態 		*/
    {NULL,				NULL,			 &StLedOffEvOff,   &StLedOffEvBlink	},/* 点滅状態(消灯)	*/
    {NULL,				NULL,			 &StLedOnEvOff,    &StLedOnEvBlink	},/* 点滅状態(点灯)	*/
};

void
blink_task(intptr_t exinf)
{
	const CALLBACK_BL *pFunc;

	for (;;) {
        SVC_PERROR(rcv_dtq(BLINK_DTQ, (intptr_t *)&GBL_RCVAD));
		DEBUG_SYSLOG(LOG_NOTICE, " Blink_Task recive data         = 0x%p", GBL_RCVAD);

		pFunc = &BLStateTable[GBL_NOWSTS][GBL_RCVEV];
		if(NULL != *pFunc){
			(*pFunc)();
		} else {
			DEBUG_SYSLOG(LOG_NOTICE, " Blink_Task ignore event. event = %d ",GBL_RCVEV);
        }
		if(GBL_CTLF & DBL_STSCHG) {		/* 状態遷移要求有り? */
			GBL_CTLF &= ~DBL_STSCHG;	/* 要求クリア		 */
			GBL_NOWSTS = GBL_NEXTSTS;   /* 状態更新 		 */
		}
		SVC_PERROR(rel_mpf(EVENT_MPF, GBL_RCVAD));
	}
}

/*
 *  Initialization Routine
 */
void
cup_timer_init(intptr_t exinf)
{
	sw8 = get_push1_state();
	sw1 = get_sw1_state();
}

/*
 *  LED1 Blink Cyclic Handler
 */
void
led1_blink_handler(intptr_t exinf){
	static unsigned char sta_led = OFF;

	if (sta_led == ON) {
		sta_led = OFF;
	} else {
		sta_led = ON;
	}

	set_led1_state(sta_led);
}

/*
 *  LED4 Blink Time Generate Cyclic Handler
 */
void
led4_blink_time_handler(intptr_t exinf){
	SVC_PERROR(iwup_tsk(LED4_BLINK_TIME_TASK));
}

void
led4_blink_time_task(intptr_t exinf){
	T_EVENT *psnd_data;
	for(;;){
		SVC_PERROR(slp_tsk());          /* ハンドラから起床されるまでスリープ */

		/* 固定長メモリブロックの獲得 */
		SVC_PERROR(get_mpf(EVENT_MPF, (void**)&psnd_data));

		psnd_data -> event = EV_BLINK_BLINK;
		DEBUG_SYSLOG(LOG_NOTICE, " led4_blink_time_task send data = 0x%p", psnd_data);
	    SVC_PERROR(psnd_dtq(BLINK_DTQ, (intptr_t)psnd_data));
	}
}

/*
 *  Switch State Scan Cyclic Handler
 */
void
sw_scan_handler(intptr_t exinf){
	SVC_PERROR(iwup_tsk(SW_SCAN_TASK));
}

void
sw_scan_task(intptr_t exinf){
	unsigned char sw;
	T_EVENT *psnd_data;

	for(;;){
		SVC_PERROR(slp_tsk());          /* ハンドラから起床されるまでスリープ */

		sw = get_sw1_state();
		if (sw1 != sw) {
			/* 固定長メモリブロックの獲得 */
			SVC_PERROR(get_mpf(EVENT_MPF, (void**)&psnd_data));
			if (sw == ON) {
				psnd_data -> event = EV_TIMER_SW1_ON;
			} else {
				psnd_data -> event = EV_TIMER_SW1_OFF;
			}
			DEBUG_SYSLOG(LOG_NOTICE, " sw_scan_task send data         = 0x%p", psnd_data);
        	SVC_PERROR(psnd_dtq(TIMER_DTQ, (intptr_t)psnd_data));
			sw1 = sw;
		}

		sw = get_push1_state();
		if (sw8 != sw) {
			if (sw == ON) {
				/* 固定長メモリブロックの獲得 */
				SVC_PERROR(get_mpf(EVENT_MPF, (void**)&psnd_data));
                psnd_data -> event = EV_TIMER_PUSH1_ON;
				DEBUG_SYSLOG(LOG_NOTICE, " sw_scan_task send data         = 0x%p", psnd_data);
	        	SVC_PERROR(psnd_dtq(TIMER_DTQ, (intptr_t)psnd_data));
				set_led2_state(ON);
			} else {
				set_led2_state(OFF);
			}
			sw8 = sw;
		}
	}
}

/*
 *  Base Time Generate Cyclic Handler
 */
void
base_time_handler(intptr_t exinf){
	SVC_PERROR(iwup_tsk(BASE_TIME_TASK));
}

void
base_time_task(intptr_t exinf){
	T_EVENT *psnd_data;

	for(;;){
		SVC_PERROR(slp_tsk());          /* ハンドラから起床されるまでスリープ */

		/* 固定長メモリブロックの獲得 */
		SVC_PERROR(get_mpf(EVENT_MPF, (void**)&psnd_data));

		psnd_data -> event = EV_TIMER_BASE_TIME;
		DEBUG_SYSLOG(LOG_NOTICE, " base_time_task send data       = 0x%p", psnd_data);
	    SVC_PERROR(psnd_dtq(TIMER_DTQ, (intptr_t)psnd_data));
	}
}
