/*
 *  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,arg2,arg3,arg4)                  \
	do{                                                    \
		syslog(prio,"%s() on line %d of the %s",           \
			__func__,__LINE__,__FILE__);                   \
        syslog(prio,arg2,arg3,arg4);                       \
	}while(0)
#else
#define DEBUG_SYSLOG(prio,arg2,arg3,arg4)
#endif /* DEBUG_UART */

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

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

/*
 *  Timer Task
 */
int timer = 0;
ST_TIMER st_timer = ST_TIMER_OFF;

static void
StOffEvSw1Off(void){
	T_EVENT *psnd_data;

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

	psnd_data -> event =  EV_BLINK_OFF;
    psnd_data -> test[0] = 123;	/* test */
    psnd_data -> test[31] = 234;	/* test */
    DEBUG_SYSLOG(LOG_NOTICE, " Timer_task send data           = 0x%p", psnd_data," ");
	SVC_PERROR(snd_dtq(BLINK_DTQ, (intptr_t)psnd_data));
	DEBUG_SYSLOG(LOG_NOTICE, " Timer stop!"," "," ");
}

static void
StOnEvSw1Off(void){
	T_EVENT *psnd_data;

	st_timer = ST_TIMER_OFF;

	/* 固定長メモリブロックの獲得 */
	SVC_PERROR(get_mpf(EVENT_MPF, (void**)&psnd_data));
	psnd_data -> event = EV_BLINK_OFF;
    psnd_data -> test[0] = 222;	/* test */
    psnd_data -> test[31] = 111;	/* test */

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

static void
StOffEvSw1On(void){
	T_EVENT *psnd_data;

	st_timer = ST_TIMER_ON;
	timer = INIT_TIME;

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

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

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

static void
StOnEvBaseTime(void){
	T_EVENT *psnd_data;

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

		st_timer = ST_TIMER_OFF;

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

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

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

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

/* タイマタスクのジャンプテーブルの定義 */
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)
{
	T_EVENT *prcv_data;
	const CALLBACK_TM *pFunc;

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

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

		pFunc = &TMStateTable[st_timer][(prcv_data -> event)];
		if(NULL != *pFunc){
			(*pFunc)();
		} else {
			DEBUG_SYSLOG(LOG_NOTICE, " Timer_Task ignore event. event = %d ",(prcv_data -> event)," ");
        }
		SVC_PERROR(rel_mpf(EVENT_MPF, prcv_data));
	}
}

/*
 *  LED4 Blink Task
 */
int bcount = 0;
ST_BLINK st_blink = ST_BLINK_IDLE;

static void
StIdleEvTimeOut(void){
	st_blink = ST_BLINK_LED_OFF;
	bcount = TIMEOUT_BLINK;
	sta_cyc(LED4_BLINK_TIME_HANDLER);
}

static void
StIdleEvAvtive(void){
	st_blink = ST_BLINK_LED_OFF;
	bcount = ACT_BLINK_TIME;
	sta_cyc(LED4_BLINK_TIME_HANDLER);
}

static void
StLedOffEvOff(void){
	set_led4_state(OFF);
	st_blink = ST_BLINK_IDLE;
	bcount = 0;
	stp_cyc(LED4_BLINK_TIME_HANDLER);
}

#define StLedOnEvOff StLedOffEvOff

static void
StLedOffEvBlink(void){
	bcount--;
	if(bcount == 0){
		st_blink = ST_BLINK_IDLE;
		stp_cyc(LED4_BLINK_TIME_HANDLER);
	} else {
		set_led4_state(ON);
		st_blink = ST_BLINK_LED_ON;
	}
}

static void
StLedOnEvBlink(void){
	set_led4_state(OFF);
	bcount--;
	if(bcount == 0){
		st_blink = ST_BLINK_IDLE;
		stp_cyc(LED4_BLINK_TIME_HANDLER);
	} else {
		st_blink = ST_BLINK_LED_OFF;
	}
}

/* ブリンクタスクのジャンプテーブルの定義 */
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)
{
	T_EVENT *prcv_data;
	const CALLBACK_BL *pFunc;

	for (;;) {
        SVC_PERROR(rcv_dtq(BLINK_DTQ, (intptr_t *)&prcv_data));
		pFunc = &BLStateTable[st_blink][(prcv_data -> event)];

		DEBUG_SYSLOG(LOG_NOTICE, "timer task messege = %d ",(prcv_data -> test[0])," ");
		DEBUG_SYSLOG(LOG_NOTICE, "timer task messege = %d ",(prcv_data -> test[31])," ");

		if(NULL != *pFunc){
			(*pFunc)();
		} else {
			DEBUG_SYSLOG(LOG_NOTICE, " Blink_Task ignore event. event = %d ",(prcv_data -> event)," ");
        }
		SVC_PERROR(rel_mpf(EVENT_MPF, prcv_data));
	}
}

/*
 *  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));
	}
}
