/*
*  This program was created by AXELL CORPORATION.
*  Copyright (C) 2017 AXELL CORPORATION, all rights reserved.
*/
#include <AG903_UC3.h>
#include "ffsys.h"
#include "kernel.h"

#include "int/intmgr.h"
#include "sample_common.h"
#include "gvd/gvd_extern.h"
#include "gvd_common.h"


gvdError gvdFileInitialize(gvdFile gvdfile)
{
  gvdfile->error = 0;
  gvdfile->fd = 0;
  gvdfile->pos = 0;
  gvdfile->size = 0;
  return GVD_ERR_SUCCESS;
}

gvdError gvdFileOpen(gvdFile gvdfile, const char *filename)
{
  gvdfile->fd = fopen(filename, "r");
  if(gvdfile->fd == 0)
    return GVD_ERR_FILE_OPERATION_FAILURE;

  gvdfile->pos = 0;
  {
      struct stat stbuf;
      if (fstat((FILE *)(gvdfile->fd), &stbuf) != 0) {
          fclose((FILE *)(gvdfile->fd));
          return GVD_ERR_FILE_OPERATION_FAILURE;
      }
      gvdfile->size = stbuf.st_size;
  }

  return GVD_ERR_SUCCESS;
}

gvdError gvdFileRead(gvdFile gvdfile, void *buf, size_t rdsize, size_t *actual_rdsize)
{
  size_t res = 0;

  if(rdsize > 0)
  {
    res = fread(buf, 1, rdsize, (FILE *)gvdfile->fd);
    if(res == 0)
    {
      int err = ferror((FILE *)gvdfile->fd);
      if(err == E_TMOUT)
        return GVD_ERR_FILE_OPERATION_TIMEDOUT;
      else if(err != E_OK)
        return GVD_ERR_FILE_OPERATION_FAILURE;
    }
    gvdfile->pos += (gvdFilePos)res;
  }
  if(actual_rdsize != NULL)
    *actual_rdsize = (size_t)res;

  return GVD_ERR_SUCCESS;
}

bool gvdFileIsEOF(gvdFile gvdfile)
{
  if (gvdfile->error)
      return true;

  if (gvdfile->pos == gvdfile->size)
      return true;
  else
      return false;
}

gvdError gvdFileSeekAbs(gvdFile gvdfile, gvdFilePos pos)
{
  if (gvdfile->error) {
      return GVD_ERR_FILE_OPERATION_FAILURE;
  }
  if (pos < 0) {
      pos = 0;
  }
  else {
    pos &= (0xFFFFFFFF - (32 - 1)); // Force alignment
  }

  if (pos >= gvdfile->size) {
      pos = gvdfile->size;
  }
  if (fseek((FILE *)(gvdfile->fd), (long)pos, SEEK_SET) != 0) {
      return GVD_ERR_FILE_OPERATION_FAILURE;
  }
  gvdfile->pos = pos;

  return GVD_ERR_SUCCESS;
}

gvdError gvdFileClose(gvdFile gvdfile)
{
  if (gvdfile->error) {
      return GVD_ERR_FILE_OPERATION_FAILURE;
  }
  if (fclose((FILE *)(gvdfile->fd)) == -1) {
      return GVD_ERR_FILE_OPERATION_FAILURE;
  }
  gvdfile->fd = 0;
  gvdfile->size = 0;
  gvdfile->pos = 0;
  return GVD_ERR_SUCCESS;
}

gvdError gvdFileFinalize(gvdFile gvdfile)
{
  if ((gvdfile)->fd != NULL) {
    fclose((FILE *)(gvdfile->fd)); /* 最後なのでエラーの検査はしない */
  }

  gvdFileInitialize(gvdfile);
  return GVD_ERR_SUCCESS;
}

void* gvdMemAlloc(size_t size)
{
  return sys_malloc(size);
}

gvdError gvdMemFree(void *blk)
{
	gvdError	retval = GVD_ERR_SUCCESS;
	int32_t		rc;

	rc = sys_free(blk);
	if(AG903_ENONE != rc) {
		retval = GVD_ERR_UNKNOWN;
	}

	 return retval;
}

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 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;
#ifndef AG903_TKERNEL
		hdrprm.exinf = &gSysIntNo[sysno];
#else
		hdrprm.exinf = NULL; /* T-KernelではGVDライブラリで上記の拡張情報相当を実装 */
#endif
		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;
}


/*-- Event --*/
#define	SYS_EVENT_NUM	(4)
static volatile ER_ID		SyncEventID[SYS_EVENT_NUM] = { 0 };

int32_t gvd_init_event(int32_t eventID, int32_t iflgptn)
{
	ER_ID	flgid;

	if (SYS_EVENT_NUM <= eventID) {
		return -AG903_EPERM;
	}
	if (0 < SyncEventID[eventID]) {
		return -AG903_ENOID;	/* 既に生成されている */
	}

	T_CFLG	cevent_sync = {
		.flgatr  = (TA_TFIFO | TA_WMUL/* | TA_CLR*/),
		.iflgptn = iflgptn,
		.name    = "SyncEvent",
	};

	flgid = acre_flg(&cevent_sync);
	if(0 >= flgid) {
		return -AG903_EPERM;
	}

	SyncEventID[eventID] = flgid;

	return AG903_ENONE;
}

int32_t gvd_set_event(int32_t eventID, int32_t iflgptn)
{
	int32_t	rc = AG903_ENONE;
	ER		ercd;

	if (SyncEventID[eventID] <= 0) {
		rc = -AG903_EPERM;
	}
	else {
		ercd = set_flg(SyncEventID[eventID], iflgptn);

		if (ercd != E_OK) {
			rc = -AG903_EPERM;
		}
	}

	return rc;
}

int32_t gvd_iset_event(int32_t eventID, int32_t iflgptn)
{
	int32_t	rc = AG903_ENONE;
	ER		ercd;

	if (SyncEventID[eventID] <= 0) {
		rc = -AG903_EPERM;
	}
	else {
		ercd = iset_flg(SyncEventID[eventID], iflgptn);

		if (ercd != E_OK) {
			rc = -AG903_EPERM;
		}
	}

	return rc;
}

int32_t gvd_wai_event(int32_t eventID, uint32_t iflgptn, uint32_t *ptn)
{
	int32_t	rc  = AG903_ENONE;
	ER		ercd;

	if (SyncEventID[eventID] <= 0) {
		rc = -AG903_EPERM;
	}
	else {
		ercd = wai_flg((ID)SyncEventID[eventID], (FLGPTN)iflgptn, TWF_ANDW, (FLGPTN*)ptn);
		clr_flg(SyncEventID[eventID], ~(iflgptn&0xFFFF));
		if (ercd != E_OK) {
			rc = -AG903_EPERM;
		}
	}

	return rc;
}

int32_t gvd_del_event(int32_t eventID)
{
	int32_t	rc = -AG903_EPERM;
	ER		ercd;

	if(0 < SyncEventID[eventID]) {
		ercd = del_flg(SyncEventID[eventID]);
		if (ercd == E_OK) {
			SyncEventID[eventID] = 0;
			rc = AG903_ENONE;
		}
	}

	return rc;
}

/*-- Semaphore --*/
#define	SYS_SEM_NUM	(4)

static volatile ER_ID		SyncSemID[SYS_SEM_NUM] = { 0 };

int32_t gvd_init_sem(int32_t semID, int32_t isemcnt, int32_t maxsem)
{
	ER_ID	smid;

	if (SYS_SEM_NUM <= semID) {
		return -AG903_EPERM;
	}
	if (0 < SyncSemID[semID]) {
		return -AG903_ENOID;	/* 既に生成されている */
	}

	T_CSEM	csem_sync = {
		.sematr  = TA_TFIFO,
		.isemcnt = isemcnt,
		.maxsem  = maxsem,
		.name    = "SyncSem",
	};

	smid = acre_sem(&csem_sync);
	if(0 >= smid) {
		return -AG903_EPERM;
	}

	SyncSemID[semID] = smid;

	return AG903_ENONE;
}

int32_t gvd_wai_sem(int32_t semID)
{
	int32_t	rc = AG903_ENONE;
	ER		ercd;

	if (SyncSemID[semID] <= 0) {
		rc = -AG903_EPERM;
	}
	else {
		ercd = wai_sem(SyncSemID[semID]);

		if (ercd != E_OK) {
			rc = -AG903_EPERM;
		}
	}

	return rc;
}

int32_t gvd_sig_sem(int32_t semID)
{
	int32_t	rc = AG903_ENONE;
	ER		ercd;

	if (SyncSemID[semID] <= 0) {
		rc = -AG903_EPERM;
	}
	else {
		ercd = sig_sem(SyncSemID[semID]);

		if (ercd != E_OK) {
			rc = -AG903_EPERM;
		}
	}

	return rc;
}

int32_t gvd_del_sem(int32_t semID)
{
	int32_t	rc = -AG903_EPERM;
	ER		ercd;

	if(0 < SyncSemID[semID]) {
		ercd = del_sem(SyncSemID[semID]);
		if (ercd == E_OK) {
			SyncSemID[semID] = 0;
			rc = AG903_ENONE;
		}
	}

	return rc;
}

