#include <stdio.h>
#include "kernel.h"
#include "AG903_UC3.h"
#include "AG903_intno.h"
#include "AG903_eventno.h"
#include "int/intmgr.h"
#include "gvd/gvd.h"
#include "gvd/gvd_extern.h"
#include "gvd/gvd_error.h"
#include "mps/mps_api.h"
#include "com.h"
#include "sample_common.h"
#include "mps_common.h"

bool gFlagMPSDone = false;
ER_ID gSemID_BufSync; /* フレームバッファ用セマフォ */
gvdFatalErrorInfo gFatal = {0, 0};

/*----------------------------
  メモリ確保関連
  ----------------------------*/
void* MPS_Memalign(size_t size, uint32_t align)
{
	void *addr;
	addr = sys_memalign(size, align);

	if(addr == NULL)
	    MPS_ErrPrintf("ERROR: [MPS_Memalign] allocate failed!\n");
	if(size >= 4096 && addr != NULL)
	    MPS_Printf("Allocated %u [Byte] @ 0x%08X\n", size, addr);
	return addr;
}
void* MPS_Malloc(size_t size)	{ return MPS_Memalign(size, 8); }
int32_t MPS_Free(void *blk)		{ return (int32_t)sys_free(blk); }
void* gvdMemAlloc(size_t size)	{ return MPS_Malloc(size); }
gvdError gvdMemFree(void *blk)	{ return (gvdError)MPS_Free(blk); }


/*----------------------------
  ログ出力
  ----------------------------*/

int32_t MPS_Printf(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	int32_t res = MPS_Stdout(0, fmt, ap);
	va_end(ap);

	return res;
}
int32_t MPS_ErrPrintf(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	int32_t res = MPS_Stdout(1, fmt, ap);
	va_end(ap);

	return res;
}

int32_t MPS_Stdout(int32_t cond, const char *fmt, va_list arg)
{
	char *str = sys_malloc(AG903_SAMPLE_SYS_PRINT_MAX+1);
	if(str == NULL) {
		return -AG903_ENOMEM;
	} else {
		sys_memset(str, 0, AG903_SAMPLE_SYS_PRINT_MAX+1);
	}

	int32_t size = vsprintf(str, fmt, arg);

	if(size > 0) {
		COM_PutStr(str);
	}

	sys_free(str);

	if(cond == 1) {
		sys_dlytsk(AG903_SAMPLE_DLY_ERROUT);
	}

	return size;
}


/*----------------------------
  タスク
  ----------------------------*/

void async_tsk(VP_INT exinf)
{
	((void)exinf);
	while(!gFlagMPSDone)
	{
		if(gFatal.cause != 0) break;

		ERRCHK(GVD_ERR_SUCCESS, MPS_Async());
	}

	MPS_Printf("Async task done.\r\n");
	ext_tsk();
}

/*----------------------------
  OSレイヤ
  ----------------------------*/
gvdError gvdOsMutexInit(int32_t *pMutex)
{
    ER_ID		mtxid;
    T_CMTX		pk_cmtx;
    gvdError	retval;

    pk_cmtx.mtxatr  = TA_TPRI;
    pk_cmtx.ceilpri = 0;
    pk_cmtx.name    = "gvdMutex";
    mtxid = acre_mtx(&pk_cmtx);
    if (mtxid <= 0) {
        retval 	  = GVD_ERR_RESOURCE_NOT_ENOUGH;
        (*pMutex) = 0;
    }
    else {
    	retval    = GVD_ERR_SUCCESS;
    	(*pMutex) = (int32_t)mtxid;
	}
    return retval;
}

gvdError gvdOsMutexLock(int32_t *pMutex)
{
    ER 			ercd;
	gvdError	retval = GVD_ERR_SUCCESS;

    ercd = loc_mtx((ID)(*pMutex));
    if (E_OK != ercd) {
        retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
    }
    return retval;
}

gvdError gvdOsMutexTryLock(int32_t *pMutex)
{
    ER 			ercd;
	gvdError	retval = GVD_ERR_SUCCESS;

    ercd = tloc_mtx((ID)(*pMutex), 0);
    if (E_OK != ercd) {
        retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
    }
    return retval;
}

gvdError gvdOsMutexUnlock(int32_t *pMutex)
{
    ER 			ercd;
	gvdError	retval = GVD_ERR_SUCCESS;

    ercd = unl_mtx((ID)(*pMutex));
    if (E_OK != ercd) {
        retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
    }
    return retval;
}

gvdError gvdOsMutexDestroy(int32_t *pMutex)
{
    ER 			ercd;
	gvdError	retval = GVD_ERR_SUCCESS;

    ercd = del_mtx((ID)(*pMutex));
    if (E_OK != ercd) {
        retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
    }
    return retval;
}


#define GVD_EVENT_FLGPTN	(0x00000001)

gvdError gvdOsEventInit(int32_t *pEvent)
{
	ER_ID 		flgid;
	T_CFLG		pk_cflg;
	gvdError	retval;

	pk_cflg.flgatr  = (TA_TFIFO|TA_WMUL|TA_CLR);
	pk_cflg.iflgptn = 0;
	pk_cflg.name 	= "gvdEvent";
	flgid = acre_flg(&pk_cflg);
	if(flgid <= 0) {
        retval 	  = GVD_ERR_RESOURCE_NOT_ENOUGH;
        (*pEvent) = 0;
	}
	else {
		retval    = GVD_ERR_SUCCESS;
		(*pEvent) = (int32_t)flgid;
	}
    return retval;
}

gvdError gvdOsEventSignal(int32_t *pEvent)
{
	ER 			ercd;
	gvdError	retval = GVD_ERR_SUCCESS;

	ercd = set_flg((ID)(*pEvent), GVD_EVENT_FLGPTN);
	if(E_OK != ercd) {
		retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
	}
    return retval;
}

gvdError gvdOsIntEventSignal(int32_t *pEvent)
{
	ER 			ercd;
	gvdError	retval = GVD_ERR_SUCCESS;

	ercd = iset_flg((ID)(*pEvent), GVD_EVENT_FLGPTN);
	if(E_OK != ercd) {
		retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
	}
    return retval;
}

gvdError gvdOsEventWait(int32_t *pEvent)
{
	ER 			ercd;
	FLGPTN		flgptn;
	gvdError	retval = GVD_ERR_SUCCESS;

	ercd = wai_flg((ID)(*pEvent), GVD_EVENT_FLGPTN, TWF_ORW, &flgptn) ;
	if(E_OK != ercd) {
		retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
	}
    return retval;
}

gvdError gvdOsEventTimedWait(int32_t *pEvent, uint32_t msec)
{
    ER 			ercd;
    FLGPTN 		flgptn;
	gvdError	retval = GVD_ERR_SUCCESS;

    ercd = twai_flg((ID)(*pEvent), GVD_EVENT_FLGPTN, TWF_ORW, &flgptn, (TMO)msec);
	if(E_OK != ercd) {
		retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
	}
    return retval;
}

gvdError gvdOsEventDestroy(int32_t* pEvent)
{
	ER 			ercd;
	gvdError	retval = GVD_ERR_SUCCESS;

	ercd = del_flg((ID)(*pEvent)) ;
	if(E_OK != ercd) {
		retval = GVD_ERR_RESOURCE_NOT_ENOUGH;
	}
    return retval;
}


/*----------------------------
  イベント
  ----------------------------*/
#define	SYS_INTR_NUM	(4)
static int32_t		gSysIntID[SYS_INTR_NUM] = {0};
static int32_t		gSysIntNo[SYS_INTR_NUM] = {0};

void sys_init_int(int intno)
{
	if(SYS_INTR_NUM > intno) {
	    AG903_INTMgrSetPriority ((AG903_IRQ28_SSC0+intno), 8); /* 8:暫定値 */
	}
	return;
}

void sys_ena_int(int intno)
{
	if(SYS_INTR_NUM > intno) {
	    AG903_INTMgrEnableInt(AG903_IRQ28_SSC0+intno);
	}
	return;
}

void sys_dis_int(int intno)
{
	if(SYS_INTR_NUM > intno) {
	    AG903_INTMgrDisableInt(AG903_IRQ28_SSC0+intno);
	}
	return;
}

void sys_cre_hdlr(int intno, void* hdlr)
{
	AG903_INTMgrHdrPrm	hdrprm;

	if(SYS_INTR_NUM > intno) {
	    gSysIntNo[intno] = intno;
	    hdrprm.atr   = AG903_INT_HLNG;
	    hdrprm.exinf = &gSysIntNo[intno];
	    hdrprm.intno = AG903_IRQ28_SSC0+intno;
	    hdrprm.func  = hdlr;
	    gSysIntID[intno] = AG903_INTMgrSetHandler(&hdrprm);
	}
	return;
}

void sys_del_hdlr(int intno)
{
	if(SYS_INTR_NUM > intno) {
	    AG903_INTMgrDeleteHandler(gSysIntID[intno]);
	}
	return;
}

void gvdIntrInit(int intno)
{
	AG903_INTMgrSetPriority (intno, 8);		/* 8:暫定値 */
	return;
}

void gvdIntrEnable(int intno)
{
	AG903_INTMgrEnableInt(intno);
	return;
}

void gvdIntrDisable(int intno)
{
	AG903_INTMgrDisableInt(intno);
	return;
}

void gvdOsHandlerCreate(int intno, void* hdlr)
{
	AG903_INTMgrHdrPrm	hdrprm;
	uint8_t sysno;

	if( (AG903_IRQ28_SSC0 <= intno) &&
		(AG903_IRQ31_SSC3 >= intno) ) {	/* システム割込みのみ */
		sysno = intno-AG903_IRQ28_SSC0;
		gSysIntNo[sysno] = sysno;
		hdrprm.atr   = AG903_INT_HLNG;
		hdrprm.exinf = &gSysIntNo[sysno];
		hdrprm.intno = intno;
		hdrprm.func  = hdlr;
		gSysIntID[sysno] = AG903_INTMgrSetHandler(&hdrprm);
	}
	return;
}

void gvdOsHandlerDelete(int intno)
{
	if( (AG903_IRQ28_SSC0 <= intno) &&
		(AG903_IRQ31_SSC3 >= intno) ) {	/* システム割込みのみ */
		AG903_INTMgrDeleteHandler(gSysIntID[intno-AG903_IRQ28_SSC0]);
	}
	return;
}
