/*****************************************************************************
** al_datasaving.c: Internal NVM Storage Thread
**
** Description: This file is only used by AL Layor and the user can't access
** directly.
**
** Copyright(c) 2011 Sunmedia Technologies - All Rights Reserved
**
** Author : anlzhao
**
** $Id: ai_datasaving.c 1901 2011-01-22 05:47:03Z liang.wu_c1 $
*****************************************************************************/
#include "gl_task.h"
#include "gl_queue.h"
#include "gl_sema.h"
#include "gl_timer.h"
#include "ai_debug_config.h"
#include "al_basictypes.h"
#include "al_setting.h"
#include "ai_datasaving.h"
#if defined (CONFIG_DTV_SUPPORT) || defined (CONFIG_ATV_SUPPORT)
#include "ai_database.h"
#endif

/* debug and error print */
#if DATASAVING_DBG_FLAG
	#define DATASAVING_DEBUG_MESSAGE(fmt, arg...) \
	do { \
		UMFDBG(0,"[Debug]%s: %d Line ", __FUNCTION__, __LINE__); \
		UMFDBG(0,fmt, ##arg); \
	} while(0)
#else
	#define DATASAVING_DEBUG_MESSAGE(fmt, arg...);
#endif

#if DATASAVING_ERROR_FLAG
	#define DATASAVING_ERROR_MESSAGE(fmt, arg...) \
	do { \
		UMFDBG(0,"[Error]%s: %d Line ", __FUNCTION__, __LINE__); \
		UMFDBG(0,fmt, ##arg); \
	} while(0)
#else
	#define DATASAVING_ERROR_MESSAGE(fmt, arg...);
#endif

/* Static variables */
static al_bool u8NvmStoreInProgress = al_false;
static GL_Task_t _nvm_storethread_id;
static GL_Queue_t _hnvm_store_qhdl = al_null;
static ALDataSavingCallback g_fpCallback;
static al_bool u8DatasavingInit = al_false;
static GL_Semaphore_t TermSem = 0;
static GL_Semaphore_t ReqSem = 0;
static al_uint32 req_tick = 0;
static al_uint32 req_count = 0;
/*****************************************************************************
** FUNCTION : DATASAVING_NvmStore_Thread
**
** DESCRIPTION :
**				This function is used to store data into flash in background.
**				any store process to flash only needs to pass message to
**				this thread, and this thread will take care all of process
**				with nvm memory in background w/o stuck of main procedure
**				during erasing or any heavy operation of flash.
**
** PARAMETERS :
**				pThreadData	- Thread Private Data
**
** RETURN VALUES:
**				None
*****************************************************************************/
static al_void DATASAVING_NvmStore_Thread(al_void *pThreadData)
{
	AI_NvmStore_CMD_e eInnerEventType;
	AI_NvmStore_CMD_t event;
	al_bool exsit_flag = al_false;

	while (!exsit_flag)
	{
		memset((void *)&event,0,sizeof(AI_NvmStore_CMD_t));
		GL_QueueReceive(_hnvm_store_qhdl, &event,
			sizeof(AI_NvmStore_CMD_t), GL_INFINITE_WAIT);

		/*
		** when system application is expected to be very busy and may lock
		** in certain thread, the nvm store procedure is not a major task to
		** be done immediately, the waiting is necessary. Such as scan
		** finish, there are many modules are begining and PE thread has
		** no resource to run. PE thread will lock the system application
		** thread. it's better to wait for all of them finish.
		*/
		u8NvmStoreInProgress = al_true;
		if (event.dEventType & AI_NVMSTORE_BUSY_WAIT)
		{
			GL_TaskSleep(600);
		}

		DATASAVING_DEBUG_MESSAGE("In app nvmsotre thread: request starts\n");
        if ((event.dEventType & (~AI_NVMSTORE_BUSY_WAIT)) > AI_NVMSTORE_CMD_TERM)
        {
            eInnerEventType = AI_NVMSTORE_BUSY_WAIT;
        }
        else
        {
		    eInnerEventType = event.dEventType & (~AI_NVMSTORE_BUSY_WAIT);
        }
		switch(eInnerEventType)
		{
			case AI_NVMSTORE_CMD_ATV_STORE:
				if (g_fpCallback)
				{
					(g_fpCallback)(AI_DATASAVING_MSG_ATVDB_STORE_START, NULL);
				}

				AI_DB_Store_ATV((AL_DB_EDBType_t)event.eventData);

				if (g_fpCallback)
				{
					(g_fpCallback)(AI_DATASAVING_MSG_ATVDB_STORE_END, NULL);
				}
				break;
			case AI_NVMSTORE_CMD_ATV_RST:
				AI_DB_SetDefault_ATV((AL_DB_EDBType_t)event.eventData);
				break;
			case AI_NVMSTORE_CMD_ATV_EMPTY:
				AI_DB_Empty_ATV((AL_DB_EDBType_t)event.eventData);
				break;
			case AI_NVMSTORE_CMD_DTV_STORE_ALL:
			case AI_NVMSTORE_CMD_DTV_STORE_SINGLE:
					if (g_fpCallback)
					{
						(g_fpCallback)(AI_DATASAVING_MSG_DVBDB_STORE_START, NULL);
					}

					AI_DB_Store_DTV((AL_DB_EDBType_t)event.eventData);

					if (g_fpCallback)
					{
						(g_fpCallback)(AI_DATASAVING_MSG_DVBDB_STORE_END, NULL);
					}
					break;
			case AI_NVMSTORE_CMD_DTV_RST_ALL:
			case AI_NVMSTORE_CMD_DTV_RST_SINGLE:
					AI_DB_SetDefault_DTV((AL_DB_EDBType_t)event.eventData);
					break;
			case AI_NVMSTORE_CMD_DTV_EMPTY_ALL:
			case AI_NVMSTORE_CMD_DTV_EMPTY_SINGLE:
					AI_DB_Empty_DTV((AL_DB_EDBType_t)event.eventData);
					break;
			case AI_NVMSTORE_CMD_ATV_DTV_RST:
				AI_DB_SetDefault_ATV(AL_DBTYPE_DVB_ATV);
				AI_DB_SetDefault_DTV(AL_DBTYPE_DVB_DTVALL);
				GL_TaskSleep(SLEEP_FOR_PE_FINISH_TIME);
				break;
			case AI_NVMSTORE_CMD_TERM:
				if (event.eventData == al_true)
				{
					AI_DB_Store_ATV(AL_DBTYPE_DVB_ATV);
					AI_DB_Store_DTV(AL_DBTYPE_DVB_DTVALL);
				}
				while(1)
				{
					if (GL_QUEUE_EMPTY == GL_QueueReceive(_hnvm_store_qhdl, &event, sizeof(AI_NvmStore_CMD_t), GL_NO_WAIT))
					{
						DATASAVING_DEBUG_MESSAGE("NVM queue is clean\n");
						break;
					}
				}
				exsit_flag = al_true;
				GL_SemaphoreGive(TermSem);
				break;

			default:
				break;
		}

		req_tick = event.req_tick;

		u8NvmStoreInProgress = al_false;
		DATASAVING_DEBUG_MESSAGE("In app nvmsotre thread: request ends\n");
	}

	u8NvmStoreInProgress = al_false;
	GL_TaskSelfDelete();
}

/*****************************************************************************
** FUNCTION : AI_DataSaving_AnyStoreInProgress
**
** DESCRIPTION :
**				This function is used to check if any nvm store request
**				or process in progress.
**				Notice: This function is not thread safe and it may be
**				modified later.
**
** PARAMETERS :
**				None
**
** RETURN VALUES:
**				al_true is storing is in process
**				al_false if storing is not in process
*****************************************************************************/
al_bool AI_DataSaving_AnyStoreInProgress(al_void)
{
	al_uint32 NonUseNumber =
		(NVMSTORE_NORMAL_Q_SIZE + NVMSTORE_URGENT_Q_SIZE);

	if (u8NvmStoreInProgress == al_true)
	{
		return al_true;
	}

	GL_QueuePeek(_hnvm_store_qhdl, &NonUseNumber);
	if (NonUseNumber != (NVMSTORE_NORMAL_Q_SIZE+NVMSTORE_URGENT_Q_SIZE))
	{
		return al_true;
	}

	return al_false;
}

/*****************************************************************************
** FUNCTION : AI_DataSaving_IsFinished
**
** DESCRIPTION :
**				This function is to check whether saving request is handled by Datasaving
**				module.
**
** PARAMETERS :
**				req_id - the id of the request
**
** RETURN VALUES:
**				al_true is storing is in process
**				al_false if storing is not in process
*****************************************************************************/
al_bool AI_DataSaving_IsFinished(al_uint32 req_id)
{
	al_bool ret = al_false;

	if (req_tick != 0 && req_tick >= req_id)
	{
		ret = al_true;
	}
	else
	{
		DATASAVING_ERROR_MESSAGE("\n req_tick: %d, req_id: %d &&&&&&&&&&\n", req_tick, req_id);
	}

	return ret;
}

/*****************************************************************************
** FUNCTION : AI_SETTING_Request
**
** DESCRIPTION :
**				This method sends a request for storage.
**
** PARAMETERS :
**				storetype	- Storage type
**				data		- message data
**				req_id 	- the id of request
**
** RETURN VALUES:
**				AL_SUCCESS
*****************************************************************************/
AL_Return_t AI_DataSaving_Request(al_uint32 storetype, al_uint32 data, al_uint32 *req_id)
{
	AI_NvmStore_CMD_t event;

	if (req_id == al_null)
	{
		return AL_INVALID_PARAM;
	}

	GL_SemaphoreTake(ReqSem, GL_INFINITE_WAIT);

	*req_id = ++req_count;
	event.dEventType = storetype;
	event.eventData = data;
	event.req_tick = *req_id;

	if(GL_QueueSend(_hnvm_store_qhdl, &event, sizeof(AI_NvmStore_CMD_t)) != GL_SUCCESS)
	{
		*req_id = --req_count;
		DATASAVING_ERROR_MESSAGE("\n\n========================================\n\n");
		DATASAVING_ERROR_MESSAGE("\n Request to Nvm is full!!, req_id: %d, req_count: %d\n", *req_id, req_count);
		DATASAVING_ERROR_MESSAGE("\n\n========================================\n\n");
		GL_SemaphoreGive(ReqSem);
		return AL_FAILURE;
	}
	
	GL_SemaphoreGive(ReqSem);

	return AL_SUCCESS;
}

/***************************************************************************
** FUNCTION :      AI_SETTING_Init
**
** DESCRIPTION :
**                 initialize the data saving thread.
** PARAMETERS :
**                 none
** RETURN VALUES :
**				AL_SUCCESS
**				AL_FAILURE
***************************************************************************/
AL_Return_t AI_DataSaving_Init(ALDataSavingCallback fpFunc)
{
	GL_Status_t retGL = GL_FAILURE;

	if (u8DatasavingInit == al_false)
	{
		retGL = GL_SemaphoreCreate("DataReqSem", 1, 1, &ReqSem);

		if (retGL != GL_SUCCESS)
		{
			DATASAVING_DEBUG_MESSAGE("GL_SemaphoreCreate ReqSem Sem failed\n");
			goto _NEXT;
		}

		retGL = GL_SemaphoreCreate("DataSaveSem", 1, 0, &TermSem);

		if (retGL != GL_SUCCESS)
		{
			DATASAVING_DEBUG_MESSAGE("GL_SemaphoreCreate Terminate Sem failed\n");
			goto _NEXT;
		}

		retGL = GL_QueueCreate("AppNvmStoreQueueHdl",
			sizeof(AI_NvmStore_CMD_t),
			NVMSTORE_NORMAL_Q_SIZE,
			NVMSTORE_URGENT_Q_SIZE,
			&_hnvm_store_qhdl);

		if (retGL != GL_SUCCESS)
		{
			DATASAVING_DEBUG_MESSAGE("GL_QueueCreate: AppNvmStoreQueueHdl Error!!\n");
			goto _NEXT;
		}

		memset(&_nvm_storethread_id, 0, sizeof(GL_Task_t));
		retGL = GL_TaskCreate("APP_NvmStore_Thread", DATASAVING_NvmStore_Thread,
			NULL, 4, 4096, FALSE, &_nvm_storethread_id);
		if (retGL != GL_SUCCESS)
		{
			DATASAVING_ERROR_MESSAGE("GL_TaskCreate: DATASAVING_NvmStore_Thread Error!!\n");
			goto _NEXT;
		}

		retGL = GL_TaskActivate(_nvm_storethread_id);
		if (retGL != GL_SUCCESS)
		{
			DATASAVING_ERROR_MESSAGE("GL_TaskActivate: DATASAVING_NvmStore_Thread Error!!\n");
			goto _NEXT;
		}
		u8DatasavingInit = al_true;
	}

	if (fpFunc != al_null)
	{
		g_fpCallback = fpFunc;
	}

	req_tick = 0;
	req_count = 0;

	return AL_SUCCESS;
_NEXT:
	if (ReqSem != 0)
	{
		GL_SemaphoreDelete(ReqSem);
		ReqSem = 0;
	}
	if (_hnvm_store_qhdl != 0)
	{
		GL_QueueDelete(_hnvm_store_qhdl);
		_hnvm_store_qhdl = 0;
	}
	if (_nvm_storethread_id != 0)
	{
		GL_TaskDestroyOne(_nvm_storethread_id);
		_nvm_storethread_id = 0;
	}
	return AL_FAILURE;
}

/*****************************************************************************
** FUNCTION : AI_DataSaving_Term
**
** DESCRIPTION :
**				This function is used to terminate datasaving task.
**				Notice: This function will not return until task is terminated.
**
** PARAMETERS :
**				None
**
** RETURN VALUES:
**				al_true is storing is in process
**				al_false if storing is not in process
*****************************************************************************/
AL_Return_t AI_DataSaving_Term(al_bool IsSync)
{
	if (u8DatasavingInit)
	{
		al_uint32 req_id;

		while (AL_SUCCESS != AI_DataSaving_Request(AI_NVMSTORE_CMD_TERM, IsSync, &req_id))
		{
			DATASAVING_ERROR_MESSAGE("AI_DataSaving_Request send error\n");
			GL_TaskSleep(50);
		}

		/* Wait NVM thread to teminate */
		GL_SemaphoreTake(TermSem, GL_INFINITE_WAIT);

		if (_hnvm_store_qhdl != 0)
		{
			GL_QueueDelete(_hnvm_store_qhdl);
			_hnvm_store_qhdl = 0;
		}
		if (ReqSem != 0)
		{
			GL_SemaphoreDelete(ReqSem);
			ReqSem = 0;
		}
		u8DatasavingInit = al_false;
		req_tick = 0;
		req_count = 0;
		GL_SemaphoreDelete(TermSem);
	}
	return AL_SUCCESS;
}

/*****************************************************************************
** $Rev: 1901 $
**
*****************************************************************************/
