/**
* Copyright(c) 2011 Sunmedia Technologies - All Rights Reserved.
*
* @file
*
* @brief The souce file of al time module
*
*/

#include "gl_mutex.h"
#include "gl_timer.h"
#include "ai_debug_config.h"
#include "time.h"
#include "al_fw.h"
#include "ai_time.h"

#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include "ap_extern.h"
#include <sys/ioctl.h>
#include <linux/rtc.h>

//#include "filesystem.h"

/**
* @brief define the debug message for time moudle
*
*/
#if TIME_DEBUG_FLAG
	#define TIME_DEBUG_MESSAGE(fmt, arg...) \
	do { \
		UMFDBG(0,"[Debug]%s: %d Line ", __FUNCTION__, __LINE__); \
		UMFDBG(0,fmt, ##arg); \
	} while(0)
#else
	#define TIME_DEBUG_MESSAGE(fmt, arg...);
#endif

/**
* @brief define the error message for time moudle
*
*/
#if TIME_ERROR_FLAG
	#define TIME_ERROR_MESSAGE(fmt, arg...) \
	do { \
		UMFDBG(0,"[Error]%s: %d Line ", __FUNCTION__, __LINE__); \
		UMFDBG(0,fmt, ##arg); \
	} while(0)
#else
	#define TIME_ERROR_MESSAGE(fmt, arg...);
#endif

#define YEAR0				(1900)
#define EPOCH_YR			(1970)
#define SECS_DAY			(24 * 60 * 60)
#define LEAPYEAR(year)		(!((year) % 4) && (((year) % 100) || !((year) % 400)))
#define YEARSIZE(year)		(LEAPYEAR(year) ? 366 : 365)

#define NTP_TIMESTAMP_DELTA 2208988800ull
#define LI 0
#define VN 3
#define MODE 3
#define STRATUM 0
#define POLL 4
#define PREC -6
#define NTPFRAC(x) (4294*(x) + ((1981*(x))>>11))
#define USEC(x) (((x)>>12) - 759 *((((x) >> 10 ) +32768) >> 16))


/**
* @brief define the mutex for time moudle
*
*/
#define TIME_MUTEX_LOCK()	{if(g_pstTimeMutex) {GL_MutexLock(g_pstTimeMutex);}}
#define TIME_MUTEX_UNLOCK()	{if(g_pstTimeMutex) {GL_MutexUnlock(g_pstTimeMutex);}}

#define Time_SEND_MSG(CallBk, Msg, Parm)		\
{\
	if(CallBk != al_null) \
	{\
		CallBk(Msg, Parm);\
	}\
}

/** List static varables */
static GL_Mutex_t 		g_pstTimeMutex = al_null;
static al_bool			g_bTimeInited = al_false;
static al_uint32 		g_dUpdateRTC = 0;
static al_uint32 		g_dUpdateSec = 0;
static al_uint32 		g_dTimeMjd = 0; /*for get year/month/day*/
static al_uint32 		g_dTimeSec = 0; /*for get hour/min/sec*/
static al_uint32 		g_dTimeDST = AL_TIME_DST_OFF;
static al_int32 		g_sdTimeOffset = 0;
static al_int32 		g_sdOldGMTOffset = ~0;
static al_bool 			g_bDateUpdated = al_false;

static al_int32 		g_sdTimeDiff = 0;

/**The system is first set up the system time when first received tot/tdt or from manual time after start-up.. */
static al_bool			g_bFirstPowerON = al_false;

/** for TOT daylight time saving*/
static al_bool			g_bTimeSync = al_false;
static al_int32 		g_sdNextTimeOffset = 0;
static al_uint32 		g_dNextTimeMjd = 0;
static al_uint32 		g_dNextTimeSec = 0;
static al_uint8 		g_bForceUpdate = al_false;

/** for System time is obtained from where?*/
static AL_Time_SyncMode_e g_SyncMode = AL_TIME_SyncMode_Auto;
static AL_Time_t g_timebyManul;
static AI_pfTimeCallback g_pfTimeCallbks = al_null;

static al_void AI_Time_SaveCurrentTimeOffset(al_int32 TimeOffset)
{
	frmwrk_pfOpConext_t pfPSIMonGetContext = al_null;

	AL_FW_GetContextCb(&pfPSIMonGetContext);

	if (pfPSIMonGetContext != al_null)
	{
		/* set user setting time offset value */
		pfPSIMonGetContext(FW_CONTEXT_TYPE_TIME_DIFFER , FW_CONTEXT_OP_TYPE_WRITE,
			(al_void *)&TimeOffset, 0);
	}
}

#if 0 /* jun.luo mask @ 2013/07/12*/
static void AI_Time_GetCurrentTimeOffset(al_int32 *TimeOffset)
{
	frmwrk_pfOpConext_t pfPSIMonGetContext = al_null;

	AL_FW_GetContextCb(&pfPSIMonGetContext);

	if (pfPSIMonGetContext != al_null)
	{
		/* set user setting time offset value */
		pfPSIMonGetContext(FW_CONTEXT_TYPE_TIME_DIFFER , FW_CONTEXT_OP_TYPE_READ,
			(al_void *)TimeOffset, 0);
	}
}
#endif

/**
* @brief To un initialise the time module
*
* @note This API is used to uninit all resources such as semaphore and mutex
* used by systime module
*
* @param none
*
* @retval  None
*/
static void _AI_Time_UninitAllResources(void)
{
	if (g_pstTimeMutex)
	{
		GL_MutexDelete(g_pstTimeMutex);
		g_pstTimeMutex = al_null;
	}
}

/**
* @brief To initialise the time module
*
* @note This API is used to init all resources such as semaphore and mutex
* used by systime module
*
* @param none
*
* @retval  None
*/
static al_bool _AI_Time_InitAllResources(void)
{
	GL_Status_t retGL = GL_FAILURE;
	// create TIME mutex semaphore
	retGL = GL_MutexCreate("TimeMutex", &g_pstTimeMutex);
	if (retGL != GL_SUCCESS)
	{
		TIME_ERROR_MESSAGE("Error %d: %s call GL_MutexCreate for mutex failed!\n", retGL, __FUNCTION__);
		goto TIME_INIT_RESOURCES_FAILED;
	}

	return al_true;

 TIME_INIT_RESOURCES_FAILED:

	_AI_Time_UninitAllResources();
	return al_false;
}

/**
* @brief This API is used to set time dst
*
* @param dTimeDST time dst applied to
*
* @retval  None
*/
void AL_Time_SetTimeDST(al_uint32 dTimeDST)
{
	TIME_MUTEX_LOCK();
	if (g_dTimeDST != dTimeDST)
	{
		g_dTimeDST = dTimeDST;
	}
	TIME_MUTEX_UNLOCK();
}

/**
* @brief This API is used to set time offset
*
* @param iTimeOffset time offset applied to
*
* @retval  None
*/
void AL_Time_SetTimeOffset(INT32 s32TimeOffset)
{
	TIME_MUTEX_LOCK();
	if (g_sdTimeOffset != s32TimeOffset) // skypine 20100108 added for mantis 76817
	{
		g_sdTimeOffset = s32TimeOffset;
		AI_Time_SaveCurrentTimeOffset(g_sdTimeOffset);
	}

	TIME_MUTEX_UNLOCK();
}

//zhongbaoxing added for mantis 0165101 @20120130 +++
/**
* @brief Called by AL_Time_Timeout to update filesystem time periodically.
*
* This API set filesystem time for fat_set_dentry_date_time while create or
* modify the file or dir
*
* @param none
*
* @retval  None
*/
static void AL_Time_SetFSDateTime(void)
{
	al_uint16 year = 0;
	al_uint8 month = 0;
	al_uint8 day = 0;
	al_uint8 hour = 0;
	al_uint8 min = 0;
	al_uint8 sec = 0;

	AL_Time_GetDate(&year, &month, &day);
	//fs_setdate((al_uint32)year, (al_uint32)month, (al_uint32)day);mark for s2tek
	AL_Time_GetTime(&hour, &min, &sec);
	//fs_settime((al_uint32)hour, (al_uint32)min, (al_uint32)sec);mark for s2tek
}
//zhongbaoxing added for mantis 0165101 @20120130 ---

/**
* @brief Called to update system time periodically.
*
* This API is used to update the system time every second, and check
* the available timer and post message to its SM if timeout happened
*
* @param none
*
* @retval  None
*/
void AL_Time_Timeout(void)
{
	al_uint32 dCurrRTC = 0;
	al_uint32 dRTCDiff = 0;
	al_uint32 dRunRtc = 0;
	al_uint32 dIncRtc = 0;

	if (!g_bTimeInited)
	{
		return;
	}

	TIME_MUTEX_LOCK();


	dCurrRTC = GL_GetRtc32();
	if (dCurrRTC >= g_dUpdateRTC)
	{
		dRunRtc = dCurrRTC - g_dUpdateRTC;
		dRTCDiff = dRunRtc / 100;
		if (dRTCDiff < 10)
		{
			if ((g_dNextTimeMjd > 0) && ((g_dTimeMjd > g_dNextTimeMjd)
				||((g_dTimeMjd == g_dNextTimeMjd) && (g_dTimeSec >= g_dNextTimeSec))))
			{
				if (g_sdTimeOffset != g_sdNextTimeOffset)
				{
					g_sdTimeOffset = g_sdNextTimeOffset;
					AI_Time_SaveCurrentTimeOffset(g_sdTimeOffset);
				}

			}
			TIME_MUTEX_UNLOCK();
            AL_Time_SetFSDateTime();
			return;
		}
		g_dTimeSec = g_dUpdateSec + dRTCDiff / 10;
	}
	else
	{
		TIME_DEBUG_MESSAGE("NOW RTC LOOPED!!! \n");
		dRunRtc = 0xffffffff - g_dUpdateRTC;
		dRunRtc += dCurrRTC;
		dRTCDiff = dRunRtc / 100;

		if(dRTCDiff < 10)
		{
			TIME_MUTEX_UNLOCK();
            AL_Time_SetFSDateTime();
			return;
		}
		g_dTimeSec = g_dUpdateSec + dRTCDiff / 10;
		TIME_DEBUG_MESSAGE("*******************g_dTimeSec++;**************************\n");
	}

	dIncRtc = (dRunRtc / 1000) * 1000;

	if (g_dTimeSec >= 86400)
	{
		g_dTimeSec = 0;
		g_dTimeMjd++;
		if (g_dTimeMjd >= 2973544)
			g_dTimeMjd = 54832;
	}

	g_dUpdateRTC += dIncRtc;
	g_dUpdateSec = g_dTimeSec;
	TIME_DEBUG_MESSAGE("[Dvb Systemtime] g_dTimeSec = %d (%d:%d:%d)\n",
		g_dTimeSec, g_dTimeSec / 3600,
		(g_dTimeSec % 3600) / 60, g_dTimeSec % 60);

	if ((g_dNextTimeMjd > 0)
		&& ((g_dTimeMjd > g_dNextTimeMjd)
		|| ((g_dTimeMjd == g_dNextTimeMjd)
		&& (g_dTimeSec >= g_dNextTimeSec))))
	{
		if (g_sdTimeOffset != g_sdNextTimeOffset)
		{
			g_sdTimeOffset = g_sdNextTimeOffset;
			AI_Time_SaveCurrentTimeOffset(g_sdTimeOffset);
		}

	}

	TIME_MUTEX_UNLOCK();
    AL_Time_SetFSDateTime();
}

/**
* @brief Init al time module
*
* @param none
*
* @retval  AL_SUCCESS means succss. AL_INITIALIZE_ALREADY means already initialised.
* AL_FAILURE means failed
*/
AL_Return_t AI_Time_Init(AI_pfTimeCallback pfTimeCallbks)
{
	if (g_bTimeInited)
	{
		return AL_INITIALIZE_ALREADY;
	}

	if (_AI_Time_InitAllResources() == al_false)
	{
		TIME_ERROR_MESSAGE("Error: %s call _TimeCreateAllResource failed!\n", __FUNCTION__);
		goto TIME_INIT_FAILED;
	}

	g_bDateUpdated = al_false;
	g_dTimeMjd = AL_Time_YmdToMjd(DEFAULT_YEAR, DEFAULT_MONTH, DEFAULT_DAY);

	g_bTimeInited = al_true;

	
	g_pfTimeCallbks = pfTimeCallbks;

	#if 0 /* jun.luo mask @ 2013/07/12*/
	AI_Time_GetCurrentTimeOffset(&g_sdTimeOffset);
	#endif

	return AL_SUCCESS;
  TIME_INIT_FAILED:

	_AI_Time_UninitAllResources();

	return AL_FAILURE;

}

/**
* @brief Terminate al time module
*
* @param none
*
* @retval  AL_SUCCESS means succss. lised. AL_FAILURE means failed
*
*/
AL_Return_t AI_Time_Term(al_void)
{

	if (!g_bTimeInited)
	{
		return AL_SUCCESS;
	}

	_AI_Time_UninitAllResources();

	
	g_bTimeInited = al_false;

	g_pfTimeCallbks = al_null;

	return AL_SUCCESS;
}

/**
* @brief This API is used to get system date in the form of Y,M,D, this date is affected by GMT offset
*
* @param pusYear MJD
*
* @param pucMonth year
*
* @param pucDay month
*
* @retval  None
*/
void AL_Time_GetDate(al_uint16 * pu16Year, al_uint8 * pu8Month, al_uint8 * pu8Day)
{
	al_uint32 u32Mjd = 0;
	al_bool value = al_false;
	if (pu16Year == al_null || pu8Month == al_null || pu8Day == al_null)
		return;

	u32Mjd = AL_Time_GetMjd();
	TIME_MUTEX_LOCK();

	value= AL_Time_MjdToYmd(u32Mjd, pu16Year, pu8Month, pu8Day);
	TIME_DEBUG_MESSAGE("Al_Time_GetDate\n");
	TIME_DEBUG_MESSAGE("year=%d, month=%d, day=%d\n",*pu16Year,*pu8Month,*pu8Day);
	if (value == al_false)
	{
		*pu16Year = 0;
		*pu8Month = 0;
		*pu8Day = 0;
	}
	TIME_MUTEX_UNLOCK();
}

/**
* @brief :This API is used to get system date in the form of MJD, this date is affected by GMT offset
*
* @param None
*
* @retval  MJD
*/
al_uint32 AL_Time_GetMjd(void)
{
	al_uint32 u32Mjd = 0;
	al_int32 iSecond = 0;
	TIME_MUTEX_LOCK();
	
	iSecond = (int)g_dTimeSec + AL_Time_GetGMTOffset();
	
	if (iSecond < 0)
	{
		u32Mjd = g_dTimeMjd - 1;
	}
	else if (iSecond >= 86400)
	{
		u32Mjd = g_dTimeMjd + 1;
	}
	else
	{
		u32Mjd = g_dTimeMjd;
	}

	TIME_MUTEX_UNLOCK();
	return u32Mjd;
}

/**
* @brief :This API is used to get system date in the form of MJD, this date is not affected by GMT offset
*
* @param None
*
* @retval  None
*/
al_uint32 AL_Time_GetRawMjd(void)
{
	return g_dTimeMjd;
}

/**
* @brief This API is used to get nex raw MJD
*
* @param None
*
* @retval  next raw MJD
*/
al_uint32 AI_Time_GetNextRawMjd(void)
{
	return g_dNextTimeMjd;
}

/**
* @brief :This API is used to get system time in seconds, this value should be less
* then 86400 (= 60*60*24), this value is not affected by GMT offset
*
* @param None
*
* @retval  Raw second
*/
al_uint32 AL_Time_GetRawSecond(void)
{
	return g_dTimeSec;
}

/**
* @brief This API is used to get system time in the form of Hour,Min,Second
*
* @param pucHour Hour
*
* @param pucMin Minite
*
* @param pucSec Second
*
* @retval  None
*/
void AL_Time_GetRawTime(al_uint8 * pu8Hour, al_uint8 * pu8Min, al_uint8 * pu8Sec)
{
	int iTimeSec = 0;

	TIME_MUTEX_LOCK();

	iTimeSec = (int)g_dTimeSec;

	if (iTimeSec < 0)
	{
		iTimeSec += 86400;
	}
	else if (iTimeSec >= 86400)
	{
		iTimeSec = (iTimeSec % 86400);
	}

	if (pu8Hour)
	{
		*pu8Hour = (al_uint8) (iTimeSec / 3600);
	}
	if (pu8Min)
	{
		*pu8Min = (al_uint8) (((al_uint32) (iTimeSec / 60)) % 60);
	}
	if (pu8Sec)
	{
		*pu8Sec = (al_uint8) (iTimeSec % 60);
	}
	TIME_DEBUG_MESSAGE("AL_Time_GetRawTime\n");
	if (pu8Hour && pu8Min && pu8Sec)
	{
		TIME_DEBUG_MESSAGE("hour=%d, min=%d, sec=%d\n",*pu8Hour, *pu8Min, *pu8Sec);
	}
	TIME_MUTEX_UNLOCK();
}

/**
* @brief This API is used to get system time in the form of Hour,Min,Second, this value is affected by GMT offset
*
* @param pucHour Hour
*
* @param pucMin Minite
*
* @param pucSec Second
*
* @retval  None
*/
void AL_Time_GetTime(al_uint8 * pu8Hour, al_uint8 * pu8Min, al_uint8 * pu8Sec)
{
	int iTimeSec = 0;

	TIME_MUTEX_LOCK();

	iTimeSec = (int)g_dTimeSec + AL_Time_GetGMTOffset();

	if (iTimeSec < 0)
	{
		iTimeSec += 86400;
	}
	else if (iTimeSec >= 86400)
	{
		iTimeSec = (iTimeSec % 86400);
	}

	if (pu8Hour)
	{
		*pu8Hour = (al_uint8) (iTimeSec / 3600);
	}
	if (pu8Min)
	{
		*pu8Min = (al_uint8) (((al_uint32) (iTimeSec / 60)) % 60);
	}
	if (pu8Sec)
	{
		*pu8Sec = (al_uint8) (iTimeSec % 60);
	}
	TIME_DEBUG_MESSAGE("AL_Time_GetTime\n");
	if (pu8Hour && pu8Min && pu8Sec)
	{
		TIME_DEBUG_MESSAGE("hour=%d, min=%d, sec=%d\n",*pu8Hour, *pu8Min, *pu8Sec);
	}
	TIME_MUTEX_UNLOCK();
}

/**
* @brief This API is used to store next time offset, and it will be applied when time of change is met
*
* @param usMjd MJD
*
* @param ucHour Hour
*
* @param ucMin Minute
*
* @param ucSec Second
*
* @retval  None
*/
void AI_Time_UpdateNextTime(al_uint32 u32Mjd, al_uint8 u8Hour, al_uint8 u8Min, al_uint8 u8Sec)
{
	if ((u8Hour > 24) || (u8Min > 60) || (u8Sec > 60))
	{
		return;
	}

	TIME_MUTEX_LOCK();
	g_dNextTimeMjd = u32Mjd;
	g_dNextTimeSec = u8Hour * 3600 + u8Min * 60 + u8Sec;
	TIME_MUTEX_UNLOCK();
}

/**
* @brief This API is used to get current system time has been updated by ts or not after power on
*
* @retval  g_bDateUpdated
*/
al_bool AL_Time_GetDateBeUpdated(void)
{
	return g_bDateUpdated;
}
/**
* @brief This API is used to set current system time, it's applied right after this API is called
*
* @param usMjd MJD
*
* @param ucHour Hour
*
* @param ucMin Minute
*
* @param ucSec Second
*
* @retval  None
*/
void AI_Time_UpdateTime(al_uint32 u32Mjd, al_uint8 u8Hour, al_uint8 u8Min, al_uint8 u8Sec)
{
	al_uint32 dTimerSec = 0;

	if ((u8Hour > 24) || (u8Min > 60) || (u8Sec > 60))
	{
		return;
	}

	TIME_MUTEX_LOCK();

	dTimerSec = u8Hour * 3600 + u8Min * 60 + u8Sec;

	//dTimerDiff = (u32Mjd - g_dTimeMjd) * 86400 + dTimerSec - g_dTimeSec;
	g_dTimeSec = dTimerSec;
	g_dTimeMjd = u32Mjd;
	g_bDateUpdated = al_true;

	g_dUpdateRTC = GL_GetRtc32();
	g_dUpdateSec = g_dTimeSec;

	TIME_MUTEX_UNLOCK();

}

void AI_Time_SetForceUpdateOffset(al_uint8 bForceUpdate)
{
	g_bForceUpdate = bForceUpdate;
}

/**
* @brief This API is used to set current system time when boot up
*
* @param None
*
* @retval  None
*/
void AL_Time_BootOnUpdateTime(void)
{
    frmwrk_pfOpConext_t pfPSIMonGetContext = al_null;
    AL_FW_GetContextCb(&pfPSIMonGetContext);
    al_uint32 u32StandbySaveTime = 0;
    al_uint32 u32UpdateMJD = 0;
    al_uint8 u8IopSleepTime[4] = {0};
    AL_Time_t stRTCDate;
    memset(&stRTCDate, 0, sizeof(AL_Time_t));

    if (pfPSIMonGetContext != al_null)
    {
        /* Get user setting time offset value */
        pfPSIMonGetContext(FW_CONTEXT_TYPE_GET_IOP_TIME , FW_CONTEXT_OP_TYPE_READ,
            (al_void *)u8IopSleepTime, 0);
        pfPSIMonGetContext(FW_CONTEXT_TYPE_GET_SAVE_TIME , FW_CONTEXT_OP_TYPE_READ,
            (al_void *)&u32StandbySaveTime, 0);
        if (u32StandbySaveTime != 0)
        {
            u32StandbySaveTime += u8IopSleepTime[0]         //SEC
                                + u8IopSleepTime[1]*60      //MIN
                                + u8IopSleepTime[2]*3600    //HOUR
                                + u8IopSleepTime[3]*86400;  //DAY
           AL_Time_Secs2Date(&u32StandbySaveTime, &stRTCDate);

           u32UpdateMJD = AL_Time_YmdToMjd(stRTCDate.Year, stRTCDate.Month, stRTCDate.Day);
           AI_Time_UpdateTime(u32UpdateMJD, stRTCDate.Hour, stRTCDate.Min, stRTCDate.Sec);
        }
    }
	
	al_bool bForceDST = al_false;
	if (pfPSIMonGetContext)
	{
		pfPSIMonGetContext(FW_CONTEXT_TYPE_CHECK_FORCE_DST , FW_CONTEXT_OP_TYPE_READ,
			(al_void *)&bForceDST, 0);
	}
	if(bForceDST)
	{
		AL_Time_SetTimeDST(AL_TIME_DST_ON);
	}
	else
	{
		AL_Time_SetTimeDST(AL_TIME_DST_OFF);
	}
}

/**
* @brief This API is used to translate date from MJD format to YMD format
*
* @param uiMjd MJD
*
* @param pusYear year
*
* @param pucMonth month
*
* @param pucDay day
*
* @retval  al_true means succss.  al_false means failed.
*/
al_bool AL_Time_MjdToYmd(al_uint32 u32Mjd, al_uint16 * pu16Year, al_uint8 * pu8Month, al_uint8 * pu8Day)
{
	if (u32Mjd < 14956 || pu16Year == NULL || pu8Month == NULL || pu8Day == NULL)
	{
		return al_false;
	}
	*pu16Year = (u32Mjd * 100 - 1507820) / 36525;
	*pu8Month = (u32Mjd * 10000 - 149561000 - ((al_uint32) ((*pu16Year) * 36525 / 100) * 10000)) / 306001;

	*pu8Day = (u32Mjd - 14956 - ((*pu16Year) * 36525) / 100 - ((*pu8Month) * 306001) / 10000);

	if (*pu8Month == 14 || *pu8Month == 15)
	{
		(*pu16Year) += 1901;
		(*pu8Month) -= 13;
	}
	else
	{
		(*pu16Year) += 1900;
		(*pu8Month) -= 1;
	}
	return al_true;
}

/**
* @brief This API is used to translate date from YMD format to MJD format
*
* @param usYear Year
*
* @param ucMonth Month
*
* @param ucDay Day
*
* @retval  MJD
*/
al_uint32 AL_Time_YmdToMjd(al_uint16 u16Year, al_uint8 u8Month, al_uint8 u8Day)
{
	al_uint32 u32MJD = 0;
	al_uint32 u32Count = 0;
	if (u16Year < 1900)
		return 0;
	u16Year = u16Year - 1900;

	if (u8Month == 1 || u8Month == 2)
	{
		u32Count = 1;
	}
	u32MJD = 14956 + u8Day + (al_uint32) ((u16Year - u32Count) * 36525 / 100) + (al_uint32) ((u8Month + 1 + u32Count * 12) * 306001 / 10000);

	return u32MJD;
}

/**
* @brief This API is used to set next time offset value which is used for tot update
*
* @param iTimeOffset time offset applied to
*
* @retval  None
*/
void AI_Time_SetNextTimeOffset(INT32 s32TimeOffset)
{
	TIME_MUTEX_LOCK();
	if (g_sdNextTimeOffset != s32TimeOffset)
	{
		g_sdNextTimeOffset = s32TimeOffset;
	}
	TIME_MUTEX_UNLOCK();
}

/**
* @brief This API is used to get time offset
*
* @param pTimeOffset Time offset
*
* @retval  None
*/
void AL_Time_GetTimeOffset(INT32 * ps32TimeOffset)
{
	*ps32TimeOffset = g_sdTimeOffset;
}

int AL_Time_GetTimeZoneOffset()
{
	return g_sdTimeOffset;
}

/**
* @brief This API is used to get GMT time offset, which is also affected by DST
*
* @param None
*
* @retval  AL_SUCCESS means succss.  AL_FAILURE means failed.
*/
int AL_Time_GetGMTOffset(void)
{
	al_uint8 u8TimeZone = 0;
	al_int32 i32TimeVal = 0;
	frmwrk_pfOpConext_t pfTimeGetContext = al_null;

	if (g_SyncMode != AL_TIME_SyncMode_Manual)
	{
		AL_FW_GetContextCb(&pfTimeGetContext);

		if (pfTimeGetContext != al_null)
		{
			pfTimeGetContext(FW_CONTEXT_TYPE_TIME_ZONE , FW_CONTEXT_OP_TYPE_READ,
				(al_void *)&u8TimeZone, 0);
			pfTimeGetContext(FW_CONTEXT_TYPE_TIME_OFFSET , FW_CONTEXT_OP_TYPE_READ,
				(al_void *)&i32TimeVal, 0);
		}
		else
		{
			u8TimeZone = AL_TIME_GMT_OFFSET_AUTO;
		}
		//printf("[%s:%d] i32TimeVal ================= %d, g_bForceUpdate: %d, g_dTimeDST: %d, g_sdTimeOffset: %d\n",__FUNCTION__,__LINE__,i32TimeVal,g_bForceUpdate,g_dTimeDST,g_sdTimeOffset);
		
		if (
#ifndef TIMEZONE_BY_COUNTRY
			u8TimeZone == AL_TIME_GMT_OFFSET_AUTO || 
#endif
			g_bForceUpdate)	//GMT Offset Auto Mode
		{
			return g_sdTimeOffset;
		}
		else
		{
			if (g_dTimeDST == AL_TIME_DST_ON)
			{
				i32TimeVal += 60 * 60;
			}
			return i32TimeVal;
		}
	}
	else
	{
		return 0;
	}
}

/**
* @brief This API is used to set old time offset
*
* @param iOffset offset defined by time zone
*
* @retval  none
*/
void AI_Time_SetOldTimeOffset(al_int32 iOffset)
{
	TIME_MUTEX_LOCK();
#if 0
	if (iOffset >= -720*60 && iOffset <= 780*60)
	{
		g_sdOldGMTOffset = iOffset;
	}
	else if(iOffset == ~0)
	{
		g_sdOldGMTOffset = ~0;
	}
#else
	g_sdOldGMTOffset = iOffset;
#endif
	TIME_MUTEX_UNLOCK();
}


/**
* @brief This API is used to record the difference of time changing
*        in TOD/TDT callback.
*
* @param iOffset offset get from TOT callback
*
* @retval  none
*/
al_void AI_Time_SetTimeDiff(al_int32 diff)
{
	g_sdTimeDiff = diff;
}

al_int32 AI_Time_GetTimeDiff(void)
{
	return g_sdTimeDiff;
}

/**
* @brief This API is used to set time sync status if TDT or TOT is recieved
*
* @param bTimeSync al_true mean set, and al_false mean unset
*
* @retval  None
*/
al_int32 AI_Time_GetOldTimeOffset(void)
{
	return g_sdOldGMTOffset;
}

/**
* @brief This API is used to set next time offset value which is used for tot update
*
* @param None
*
* @retval  AL_SUCCESS means succss.  AL_FAILURE means failed.
*/
int AL_Time_GetNextGMTOffset(void)
{
	al_uint8 u8TimeZone = 0;
	al_int32 i32TimeVal = 0;
	frmwrk_pfOpConext_t pfTimeGetContext = al_null;

	AL_FW_GetContextCb(&pfTimeGetContext);

	if (pfTimeGetContext != al_null)
	{
		pfTimeGetContext(FW_CONTEXT_TYPE_TIME_ZONE , FW_CONTEXT_OP_TYPE_READ,
			(al_void *)&u8TimeZone, 0);
		pfTimeGetContext(FW_CONTEXT_TYPE_TIME_OFFSET , FW_CONTEXT_OP_TYPE_READ,
			(al_void *)&i32TimeVal, 0);
	}
	else
	{
		u8TimeZone = AL_TIME_GMT_OFFSET_AUTO;
	}
	
	if (u8TimeZone == AL_TIME_GMT_OFFSET_AUTO || g_bForceUpdate)	//GMT Offset Auto Mode
	{
		return g_sdNextTimeOffset;
	}
	else
	{
		if (g_dTimeDST == AL_TIME_DST_ON)
		{
			i32TimeVal += 60 * 60;
		}		
		return i32TimeVal;
	}

}

al_uint32 AI_Time_GetNextTimeSec(void)
{
	return g_dNextTimeSec;
}

/**
* @brief This API is used to get days by the requested month and year
*
* @param u16Year year
*
* @param u8Month Month
*
* @retval  None
*/
al_uint8 AL_Time_GetDayByMonth(al_uint16 u16Year, al_uint8 u8Month)
{
	al_bool iFlag = al_false;
	if ((!(u16Year % 4) && (u16Year % 100)) || !(u16Year % 400))
	{
		iFlag = al_true;
	}
	switch (u8Month)
	{
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			return 31;
		case 4:
		case 6:
		case 9:
		case 11:
			return 30;
		case 2:
			if (iFlag)
			{
				return 29;
			}
			else
			{
				return 28;
			}
		default:
			break;
	}
	return 0;
}

/**
* @brief :This API is used to get time sync status which indicates if TDT or TOT is received
*      
* @param None
*
* @retval  al_true means TOT or TDT is received and al_false otherwise.
*/
al_bool AI_Time_GetTimeSync(void)
{
	return g_bTimeSync;
}

/**
* @brief This API is used to set time sync status if TDT or TOT is recieved
*
* @param bTimeSync al_true mean set, and al_false mean unset
*
* @retval  None
*/
void AI_Time_SetTimeSync(al_bool bTimeSync)
{
	TIME_MUTEX_LOCK();
	g_bTimeSync = bTimeSync;
	TIME_MUTEX_UNLOCK();
}
 
 /**
 * @brief :This API is used to get time sync status which indicates if (TDT or TOT is received/get system time by use set )from boot.
 * System time may vary depending on the stream of time, can vary depending on the manual setting. 
 * @param None
 *
 * @retval	al_true means TOT or TDT first is received and al_false otherwise..
 */
 al_bool AI_Time_GetTimeFristPowerON(void)
 {
	 return g_bFirstPowerON;
 }
 
 /**
 * @brief This API is used to set time sync status if (TDT or TOT is recieved/get system time by use set) from boot.
 *System time may vary depending on the stream of time, can vary depending on the manual setting.
 * @param bTimeSync al_true mean TOT or TDT first is received from boot  , and al_false mean unset
 *
 * @retval	None
 */
 void AI_Time_SetTimeFristPowerON(al_bool bTimeSync)
 {
	 TIME_MUTEX_LOCK();
	 g_bFirstPowerON = bTimeSync;
	 TIME_MUTEX_UNLOCK();
 }

 /**
* @brief Translate date to secs(from 1970/01/01)
*
* @param p_RTCDate Date
*
* @param p_u32secs Total Seconds
*
* @retval  AL_SUCCESS means success
*/
AL_Return_t AL_Time_DateToSecs(const AL_Time_t *p_RTCDate, al_uint32*p_u32secs)
 {
 	al_uint32 days;
	/*end*/
	al_uint16 DMonth[] =
	{
		0,
		31,
		59,
		90,
		120,
		151,
		181,
		212,
		243,
		273,
		304,
		334,
		365, /* Dummy */
	};

	/**
	* Secs: 0~59
	* Minutes: 0~59
	* Hours: 0~23
	* Day: 1~31
	* Month: 0~12
	* Year: starting from 1900
	*/

	if (p_RTCDate->Year>=1970 && p_RTCDate->Sec< 60 && p_RTCDate->Min< 60 &&
		p_RTCDate->Hour< 24 && p_RTCDate->Day< 32 && p_RTCDate->Month < 13)
	{
		days = (365 * (p_RTCDate->Year - 1970)) + DMonth[p_RTCDate->Month - 1] +
			p_RTCDate->Day- 1;

		/** The number of leap years passed */
		days += (p_RTCDate->Year - 1970)/ 4;

		/** Current year is leap? */
		if ((p_RTCDate->Year)% 4 != 0) /**< not leap year */
		{
			if (((p_RTCDate->Year - 1970) - ((p_RTCDate->Year - 1970)/4)*4) >=2 )
			{
				days++;
			}
		}
		else
		{
			/** past 2/29 */
			if (p_RTCDate->Month> 2) /**< leap year */
			{
				days++;
			}
		}

		/** Seconds since 00:00:00 1970/1/1 */
		*p_u32secs =(al_uint32) (p_RTCDate->Sec+60 *
			(p_RTCDate->Min+60 * (p_RTCDate->Hour+ 24 * days)));
		return AL_SUCCESS;

	}

	return AL_FAILURE;
}

 /**
* @brief Translate date to secs(from 1970/01/01)
*
* @param p_RTCDate Date
*
* @param p_u32secs Total Seconds from 1970/01/01
*
* @retval  AL_SUCCESS means success
*/
AL_Return_t AL_Time_Secs2Date(const al_uint32 *p_u32secs, AL_Time_t *p_RTCDate)
{
	static const al_uint8 _ytab[2][12] =
	{
		{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
		{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
	};
	al_uint32 time = (al_uint32)*p_u32secs;
	al_uint32 dayclock, dayno;
	al_uint32 year = EPOCH_YR;

	if  (p_RTCDate == al_null)
	{
		return AL_FAILURE;
	}

	dayclock = (al_uint32)time % SECS_DAY;
	dayno = (al_uint32)time / SECS_DAY;

	p_RTCDate->Sec = dayclock % 60;
	p_RTCDate->Min = (dayclock % 3600) / 60;
	p_RTCDate->Hour = dayclock / 3600;
	while (dayno >= (al_uint32)YEARSIZE(year))
	{
		dayno -= YEARSIZE(year);
		year++;
	}
	p_RTCDate->Year= year;

	p_RTCDate->Month= 0;
	while (dayno >= (al_uint32)_ytab[LEAPYEAR(year)][p_RTCDate->Month])
	{
		dayno -= _ytab[LEAPYEAR(year)][p_RTCDate->Month];
		p_RTCDate->Month++;
	}
	p_RTCDate->Month+=1;
	p_RTCDate->Day= dayno + 1;
	return AL_SUCCESS;
}
 
 /**
* @brief Get total secs from 1970/01/01
*
* @param CurTimeInSec Get total secs from 1970/01/01
*
* @retval  AL_SUCCESS means success
*/
AL_Return_t AL_Time_GetCurTimeInSecs(al_uint32 *CurTimeInSec)
{
	AL_Time_t CurTime;
	 AL_Return_t ret;

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

	AL_Time_GetDate(&CurTime.Year, &CurTime.Month, &CurTime.Day);
 	AL_Time_GetTime(&CurTime.Hour, &CurTime.Min, &CurTime.Sec);
 	AL_Time_DateToSecs(&CurTime, CurTimeInSec);
	if (g_bTimeSync)
	{
 		ret = AL_SUCCESS;
	}
	else
	{
		ret = AL_TIME_NOT_SET;
	}

	return ret;
}

/**
* @brief To reset system time when user change country.
*
* @note This API is used to reset system time when change country.
*
* @param none
*
* @retval  AL_SUCCESS means success
*/
AL_Return_t AL_Time_Reset(void)
{
	frmwrk_pfOpConext_t pfTimeGetContext = al_null;
	al_uint8 u8TimeOffset = 0;

	TIME_MUTEX_LOCK();

	AL_FW_GetContextCb(&pfTimeGetContext);

	if (pfTimeGetContext != al_null)
	{
		pfTimeGetContext(FW_CONTEXT_TYPE_TIME_ZONE , FW_CONTEXT_OP_TYPE_READ,
			(al_void *)&u8TimeOffset, 0);

		if (u8TimeOffset == AL_TIME_GMT_OFFSET_AUTO || g_bForceUpdate)
		{
			g_dNextTimeMjd = 0;
			g_dNextTimeSec = 0;
			g_sdNextTimeOffset = 0;
#ifndef TIMEZONE_BY_COUNTRY
			g_bForceUpdate = FALSE;
#endif
			if (g_sdTimeOffset != 0)
			{
				g_sdTimeOffset = 0;
				AI_Time_SaveCurrentTimeOffset(g_sdTimeOffset);
			}
		}
	}

	TIME_MUTEX_UNLOCK();

	return AL_SUCCESS;
}

/**
* @brief This API is used to update RTC
*
* @param None
*
* @retval  None
*/
void AL_Time_UpdateRTC(void)
{
	g_dUpdateRTC = GL_GetRtc32();
}

/**
* @brief This API is get Sync mode 
*
* @param AL_TIME_SyncMode_Manual - system time from menu
*             AL_TIME_SyncMode_Auto - system time from TOT/TDT
* @retval  None
*/
AL_Return_t AL_Time_SetSyncMode(AL_Time_SyncMode_e mode)
{
	if (mode > AL_TIME_SyncMode_MAX)
	{
		TIME_DEBUG_MESSAGE("set Sync mode error:%d\n",mode);
		return AL_FAILURE;
	}
	if (mode == AL_TIME_SyncMode_Auto)
	{
		al_uint32 u32OldTimeInSec = 0;
		AL_Time_GetCurTimeInSecs(&u32OldTimeInSec);
		AI_Time_SetTimeSync(al_false);	
		g_SyncMode = mode;
		Time_SEND_MSG(g_pfTimeCallbks, AI_TIME_UPDATE, u32OldTimeInSec);
	}
	else
	{
		g_SyncMode = mode;
	}
	return AL_SUCCESS;
}

/**
* @brief This API is used to update RTC
*
* @param None
*
* @retval  g_SyncMode
*/
AL_Time_SyncMode_e AL_Time_GetSyncMode(void)
{
	return g_SyncMode;
}

/**
* @brief This API is set system time which user set by manual 
*
* @param stTime: real time set by manual
*		  bSync : if true, systime will be real time set by manual + GMT offset
*
* @retval  None
*/
AL_Return_t AL_Time_SetSystimefromManual(AL_Time_t* stTime, al_bool bSync)
{
	al_uint32 u32NewMjd;
	al_uint32 u32OldTimeInSec = 0;

	if (stTime == NULL)
	{
		return AL_FAILURE;
	}

	if (stTime->Year < 2000
		|| stTime->Month <1 || stTime->Month > 12
		|| stTime->Day > 31
		|| stTime->Hour > 23
		|| stTime->Min > 59
		|| stTime->Sec > 59)
	{
		printf("stTime time is out of range !!!!!!\n");
		printf("stTime Year = %d\n", stTime->Year);
		printf("stTime Month = %d\n", stTime->Month);
		printf("stTime Day = %d\n", stTime->Day);
		printf("stTime Hour = %d\n", stTime->Hour);
		printf("stTime Min = %d\n", stTime->Min);
		printf("stTime Second = %d\n", stTime->Sec);
		return AL_FAILURE;
	}

	AL_Time_GetCurTimeInSecs(&u32OldTimeInSec);

	AL_Time_SetSyncMode(AL_TIME_SyncMode_Manual);
	g_timebyManul.Year = stTime->Year;
	g_timebyManul.Month = stTime->Month;
	g_timebyManul.Day = stTime->Day;
	g_timebyManul.Hour = stTime->Hour;
	g_timebyManul.Min = stTime->Min;
	g_timebyManul.Sec = stTime->Sec;

	u32NewMjd = AL_Time_YmdToMjd(g_timebyManul.Year, g_timebyManul.Month, g_timebyManul.Day);

	AI_Time_UpdateTime(u32NewMjd,g_timebyManul.Hour,g_timebyManul.Min, g_timebyManul.Sec);

	
	if (bSync) //If true, current time will be influented by current GMT offset.
	{
		Time_SEND_MSG(g_pfTimeCallbks, AI_TIME_UPDATE, u32OldTimeInSec);
	}

	return AL_SUCCESS;
}

/**
* @brief This API is get system time which user set by manual 
*
* @param None
*
* @retval  None
*/
AL_Return_t AL_Time_GetSystimefromManual(AL_Time_t* stTime)
{
	if (stTime == NULL)
	{
		return AL_FAILURE;
	}
	stTime->Year = g_timebyManul.Year;
	stTime->Month = g_timebyManul.Month;
	stTime->Day = g_timebyManul.Day;
	stTime->Hour = g_timebyManul.Hour;
	stTime->Min = g_timebyManul.Min;
	stTime->Sec = g_timebyManul.Sec;
	
	return AL_SUCCESS;
}

 AL_Return_t AL_Time_GetRawTimeInSecs(al_uint32 *CurTimeInSec)
{
	AL_Time_t CurTime;
	AL_Return_t ret;

 	if (CurTimeInSec == al_null)
 	{
  		return AL_INVALID_PARAM;
 	}
	memset(&CurTime, 0, sizeof(AL_Time_t));
	AL_Time_MjdToYmd(AL_Time_GetRawMjd(), &CurTime.Year, &CurTime.Month, &CurTime.Day);
 	AL_Time_GetRawTime(&CurTime.Hour, &CurTime.Min, &CurTime.Sec);
 	AL_Time_DateToSecs(&CurTime, CurTimeInSec);
	
	ret = AL_SUCCESS;
	return ret;
}

AL_Return_t AL_Time_Set_TimeFromNTP(al_uint32 *data)
{
	struct timeval tv_set;
	al_uint32 coarse;
	al_uint32 fine;
	//al_uint32 dCurrRTC = 0;
	AL_Time_t stRTCDate;
	al_uint32 u32UpdateMJD = 0;
	time_t t;
	struct tm *timeinfo;
	
#define Data(i) ntohl(((al_uint32 *)data)[i])
	coarse = Data(10);
	fine = Data(11);
#undef Data
	tv_set.tv_sec = coarse - NTP_TIMESTAMP_DELTA;
	tv_set.tv_usec = USEC(fine);
	if(settimeofday(&tv_set,NULL)<0)
		return AL_FAILURE;

	AL_Time_UpdateRTC();
	
	t = tv_set.tv_sec;
	timeinfo = localtime(&t);

	stRTCDate.Year = timeinfo->tm_year+1900;
	stRTCDate.Month = timeinfo->tm_mon+1;
	stRTCDate.Day = timeinfo->tm_mday;
	stRTCDate.Hour = timeinfo->tm_hour;
	stRTCDate.Min = timeinfo->tm_min;
	stRTCDate.Sec = timeinfo->tm_sec;
	
	u32UpdateMJD = AL_Time_YmdToMjd(stRTCDate.Year, stRTCDate.Month, stRTCDate.Day);
       AI_Time_UpdateTime(u32UpdateMJD, stRTCDate.Hour, stRTCDate.Min, stRTCDate.Sec);
	ioctl(rtcdev, RTC_SET_TIME, (struct rtc_time*)timeinfo);
			
	return AL_SUCCESS;
}
 
AL_Return_t AL_Time_Get_NTPTime()
{
	al_int32 sockfd, n, maxfd1; // Socket file descriptor and the n return result from writing/reading from the socket.
	al_int32 portno = 123; // NTP UDP port number.
	al_int32 rcvr_portno = 5301; // receive port number.	
	struct timeval timeout;
	fd_set readfds;
	al_uint32 buf[12];
	al_uint32 incoming_word[20];
	size_t nbytes;
	char* host_name = "watch.stdtime.gov.tw"; // NTP server host-name.
	struct sockaddr_in serv_addr; // Server address data structure.
	struct sockaddr_in rcvr_addr;
	struct sockaddr sa_xmit;
	socklen_t sa_xmit_len = sizeof(sa_xmit);
	al_int32 pack_len;
	struct hostent *server;      // Server data structure.
	struct timeval now;
	
	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Create a UDP socket.

	if (sockfd < 0)
	{
		TIME_ERROR_MESSAGE("ERROR opening socket\n");
		return AL_FAILURE;
	}

	bzero((char*) &rcvr_addr, sizeof(rcvr_addr));
	rcvr_addr.sin_family = AF_INET;
	rcvr_addr.sin_port = htons(rcvr_portno);
	if(bind(sockfd, (struct sockaddr *) &rcvr_addr, sizeof(rcvr_addr)) == -1)
	{
		TIME_ERROR_MESSAGE("could not bind to udp port\n");
		close(sockfd);
		return AL_FAILURE;
	}
	listen(sockfd, 3);

	server = gethostbyname(host_name); // Convert URL to IP.
	if (server == NULL)
	{
		TIME_ERROR_MESSAGE("ERROR, no such host\n");
		close(sockfd);
		return AL_FAILURE;
	}

	// Zero out the server address structure.
	bzero((char*) &serv_addr, sizeof(serv_addr));

	serv_addr.sin_family = AF_INET;

	// Copy the server's IP address to the server address structure.
	bcopy((char*)server->h_addr, (char*) &serv_addr.sin_addr.s_addr, server->h_length);

	// Convert the port number integer to network big-endian style and save it to the server address structure.
	serv_addr.sin_port = htons(portno);

	// Call up the server using its IP address and port number.
	if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
	{
		TIME_ERROR_MESSAGE("ERROR connecting\n");
		close(sockfd);
		return AL_FAILURE;
	}

	nbytes = 48;
	if(sizeof(buf) != 48)
	{
		TIME_ERROR_MESSAGE("ERROR SIZE\n");
		close(sockfd);
		return AL_FAILURE;
	}
	bzero((char*) buf, sizeof(buf));
	buf[0] = htonl((LI << 30) | (VN << 27) | (MODE << 24) | (STRATUM << 16) | (POLL << 8) | (PREC & 0xff));
	buf[1] = htonl(1<<16);
	buf[2] = htonl(1<<16);
	gettimeofday(&now,NULL);
	buf[10] = htonl(now.tv_sec + NTP_TIMESTAMP_DELTA);
	buf[11] = htonl(NTPFRAC(now.tv_usec));
	send(sockfd, buf, nbytes, 0);
	
	timeout.tv_sec = 20;
	timeout.tv_usec = 0;
	for(;;){
		FD_ZERO(&readfds);
		FD_SET(sockfd, &readfds);
		maxfd1 = sockfd + 1;
		
		n = select(maxfd1, &readfds, NULL, NULL, &timeout);
		if((n!=1)||(!FD_ISSET(sockfd, &readfds)))
		{
			if(errno==EINTR)
			{
				continue;
			}
			if(n<0)
			{
				close(sockfd);
				return AL_FAILURE;
			}
			close(sockfd);
			return AL_FAILURE;
		}
		pack_len = recvfrom(sockfd, ((char *)incoming_word),(sizeof (incoming_word)*sizeof(uint32_t)),0,&sa_xmit,&sa_xmit_len);
		if(pack_len<0)
		{
			close(sockfd);
			return AL_FAILURE;
		}
		else if(pack_len>0)
		{
			close(sockfd);
			return AL_Time_Set_TimeFromNTP(incoming_word);
		}
		close(sockfd);
		return AL_FAILURE;
	}
	close(sockfd);
	return AL_SUCCESS;
}
