/**
* Copyright(c) 2011 Sunmedia Technologies - All Rights Reserved.
*
* @file al_ews_monitor.c
*
* @brief this file defines ews monitor function.
*
* The file supports the ews monitor functions by used for app layer.
* The ews monitor monitor Announcement support descriptor and Adaptation field data descriptor, 
* notify special audio monitor af flag to change audio or service
*
*/

/**<header files*/
#include <sys/ioctl.h>
#include "ai_debug_config.h"
#include "mid_siutil_13818.h"
#include "mid_siutil_dvb.h"
#include "dvb_dr_dvb.h"
#include "mid_psimon.h"
#include "mid_pmtother_mon.h"
#include "mid_common.h"
#include "dvb_dr_13818.h"
#include "al_fw.h"
#include "kmf_ioctl.h"
#ifdef __cplusplus
	extern "C"{
#endif

#ifdef SUPPORT_ASTRA_LCN_MONITOR
#include "mid_sgt_mon.h"
#include "ai_astralcn_monitor.h"


#define AL_ASTRALCN_MONITOR_DEBUG_FLAG 0
#define AL_ASTRALCN_MONITOR_ERROR_FLAG 0

/**<Macro define*/
#if AL_ASTRALCN_MONITOR_DEBUG_FLAG
	#define ASTRALCN_MONITOR_DEBUG_MSG(fmt, arg...) \
	do { \
		UMFDBG(0,"[Debug]%s: %d Line ", __FUNCTION__, __LINE__); \
		UMFDBG(0,fmt, ##arg); \
	} while(0)
#else
	#define ASTRALCN_MONITOR_DEBUG_MSG(fmt, arg...);
#endif

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

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

#define MAX_SGT_TABLE  5

static al_bool g_bAstraLcnMonInited = al_false;
static AI_pfAstraLcnMonCallback g_pfAstraLcnMonCallbks = al_null;
static al_bool g_bAstraLcnStarted = al_false;
static AstraLcnData_t *g_pAstraLcnData = al_null;

int _SGTAddTail(AstraLcnData_t ** ppHead, AstraLcnData_t * pNew)
{
	pNew->Next = al_null;
	if (ppHead == al_null)
		return 0;

	if ((*ppHead) == al_null)
	{
		(*ppHead) = pNew;
		return 1;
	}

	AstraLcnData_t *p = (*ppHead);
	while (p->Next)
		p = p->Next;

	p->Next = pNew;

	return 1;
}

al_void _SGTFreeList(AstraLcnData_t ** ppData)
{
	if (ppData == al_null || (*ppData) == al_null)
		return;

	AstraLcnData_t *pData = (*ppData);
	AstraLcnData_t *pNext = al_null;
	while (pData)
	{
		pNext = pData->Next;
		SAFE_FREE(pData);

		pData = pNext;
	}

	*ppData = al_null;
}

static al_bool _AI_AstraLcn_Monitor_FindLinkInfo(dvbpsi_LinkInfo_t stLinkInfo)
{
	AstraLcnData_t *pAstraLcnData = g_pAstraLcnData;
	while(pAstraLcnData)
	{
		if (pAstraLcnData->stLinfInfo.u16OrgNetId == stLinkInfo.u16OrgNetId 
			&& pAstraLcnData->stLinfInfo.u16TsId == stLinkInfo.u16TsId
			&& pAstraLcnData->stLinfInfo.u16ServiceId == stLinkInfo.u16ServiceId
			&& pAstraLcnData->stLinfInfo.u8LinkType == stLinkInfo.u8LinkType)
		{
			return TRUE;
		}

		pAstraLcnData = pAstraLcnData->Next;
	}

	return al_false;
}

static al_void _AI_Astralcn_Monitor_AddLinkinfo(dvbpsi_LinkInfo_t stLinkInfo)
{
	AstraLcnData_t *pAstraLcnData = (AstraLcnData_t *) zero_malloc(sizeof(AstraLcnData_t));

	pAstraLcnData->stLinfInfo.u16OrgNetId = stLinkInfo.u16OrgNetId;
	pAstraLcnData->stLinfInfo.u16TsId = stLinkInfo.u16TsId;
	pAstraLcnData->stLinfInfo.u16ServiceId = stLinkInfo.u16ServiceId;
	pAstraLcnData->stLinfInfo.u8LinkType = stLinkInfo.u8LinkType;
	
	_SGTAddTail(&g_pAstraLcnData, pAstraLcnData);
}

static al_void _AI_AstraLcn_Monitor_SGTCallback(al_void *pData)
{
	//MID_SiutilSgtInfo_t * pstSGT = (MID_SiutilSgtInfo_t *)pData;
	//void DumpSGT(MID_SiutilSgtInfo_t * pSGT);
	//DumpSGT(pstSGT);
}

al_void AI_AstraLcn_Monitor_PmtOtherClbk(al_void *pData)
{
	MID_SiutilPmtInfo_t *pstPMT = (MID_SiutilPmtInfo_t *) pData;
	MID_SiutilPmtStreamInfoDescr_t *p_comp = pstPMT->p_comp;
	int iCount;
	AstraLcnData_t *pAstraLcnData = g_pAstraLcnData;

	if (g_bAstraLcnStarted == al_false)
	{
		return;
	}
	
	while (pAstraLcnData)
	{
		if (pAstraLcnData->stLinfInfo.u16ServiceId == pstPMT->program_number)
		{
			break;
		}
		pAstraLcnData = pAstraLcnData->Next;
	}

	if (pAstraLcnData == al_null)
	{
		return;
	}
	
	for (iCount = 0; iCount < pstPMT->comp_count; iCount++)
	{
		switch (p_comp[iCount].stream_type)
		{
			case STREAM_TYPE_PRIVATE_SECTION:
				pAstraLcnData->SgtPid = p_comp[iCount].elementary_pid;
				break;

			default:
				break;
		}
	}
}

al_void AI_AstraLcn_Monitor_RcvAllPmtCK(al_void *pData)
{
	if (g_pfAstraLcnMonCallbks)
	{
		g_pfAstraLcnMonCallbks(AI_ASTRALCN_MONITOR_RECVED_PMT, 0);
	}
}

al_void AI_AstraLcn_Monitor_InNitAstraLcnDispose(al_void *pData)
{
	MID_SiutilNitInfo_t *pstNIT = (MID_SiutilNitInfo_t *) pData;
	dvbpsi_linkage_dr_t  *pstLinkage = NULL;
	dvbpsi_LinkInfo_t stLinkInfo;
	int j = 0;
	al_bool bNeedRecPmt = al_false;
	UINT8 u8NeedParse = al_false;

	for (j = 0; j< pstNIT->descLen; )
	{
		if (DESCR_LINKAGE != *(pstNIT->p_descr+j))
        {
            j += *(pstNIT->p_descr+j+1) +2;
            continue;
        }

		pstLinkage = dvbpsi_DecodeLinkDr((pstNIT->p_descr+j), pstNIT->descLen - j);
		if (pstLinkage != NULL)
		{
			j += pstLinkage->length + 9;
			if (pstLinkage->i_linkage_type == 0x04)
			{
				u8NeedParse = al_true;
				break;
			}
			SAFE_FREE(pstLinkage);
		}
	}

	if (u8NeedParse == FALSE)
	{	
		return;
	}

	for (j = 0; j< pstNIT->descLen; )
	{
		if (DESCR_LINKAGE != *(pstNIT->p_descr+j))
        {
            j += *(pstNIT->p_descr+j+1) +2;
            continue;
        }

		pstLinkage = dvbpsi_DecodeLinkDr((pstNIT->p_descr+j), pstNIT->descLen - j);
		if (pstLinkage != NULL)
		{
			j += pstLinkage->length + 9;
			if (pstLinkage->i_linkage_type == 0x90 || pstLinkage->i_linkage_type == 0x91 || pstLinkage->i_linkage_type == 0x93)
			{
				memset(&stLinkInfo, 0, sizeof(dvbpsi_LinkInfo_t));
				stLinkInfo.u16OrgNetId = pstLinkage->i_original_network_id;
				stLinkInfo.u16TsId = pstLinkage->i_transport_stream_id;
				stLinkInfo.u16ServiceId = pstLinkage->i_service_id;
				stLinkInfo.u8LinkType = pstLinkage->i_linkage_type;
				if (_AI_AstraLcn_Monitor_FindLinkInfo(stLinkInfo) == al_false)
				{
					bNeedRecPmt = al_true;
				}
			
				
			}
			SAFE_FREE(pstLinkage);
		}
	}

	if (bNeedRecPmt)
	{
		_SGTFreeList(&g_pAstraLcnData);
		
		for (j = 0; j< pstNIT->descLen; )
		{
			if (DESCR_LINKAGE != *(pstNIT->p_descr+j))
			{
				j += *(pstNIT->p_descr+j+1) +2;
				continue;
			}
		
			pstLinkage = dvbpsi_DecodeLinkDr((pstNIT->p_descr+j), pstNIT->descLen - j);
			if (pstLinkage != NULL)
			{
				j += pstLinkage->length + 9;
				if (pstLinkage->i_linkage_type == 0x90 || pstLinkage->i_linkage_type == 0x91 || pstLinkage->i_linkage_type == 0x93)
				{
					memset(&stLinkInfo, 0, sizeof(dvbpsi_LinkInfo_t));
					stLinkInfo.u16OrgNetId = pstLinkage->i_original_network_id;
					stLinkInfo.u16TsId = pstLinkage->i_transport_stream_id;
					stLinkInfo.u16ServiceId = pstLinkage->i_service_id;
					stLinkInfo.u8LinkType = pstLinkage->i_linkage_type;
					_AI_Astralcn_Monitor_AddLinkinfo(stLinkInfo);
				}

				SAFE_FREE(pstLinkage);
			}
		}

		if (g_pfAstraLcnMonCallbks)
		{
			g_pfAstraLcnMonCallbks(AI_ASTRALCN_MONITOR_START_RECVPMT, 0);
		}
	}
	
}

AL_Return_t AI_AstraLcn_Monitor_SetPid(al_void)
{
	if (MID_SUCCESS != MID_SGTAddPid(g_pAstraLcnData))
	{
		return AL_FAILURE;
	}

	return AL_SUCCESS;
}


/**
* @brief ASTRA LCN monitor init function.
*
* @param pfEwsMonCallbks[input] ews monitor notify framwork callback function.
*
* @return AL_SUCCESS if initialization is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_AstraLcn_Monitor_Init(AI_pfAstraLcnMonCallback pfAstraLcnMonCallbks)
{
	if (g_bAstraLcnMonInited == al_true)
	{
		ASTRALCN_MONITOR_DEBUG_MSG("ASTRA LCN monitor has inited.\n");
		return AL_SUCCESS;
	}

	g_bAstraLcnMonInited = al_true;
	g_pfAstraLcnMonCallbks = pfAstraLcnMonCallbks;

	MidSGTCallBack pSGTMonCallbks = NULL;
	pSGTMonCallbks = _AI_AstraLcn_Monitor_SGTCallback;
	if (MID_SUCCESS != MID_SGTInit(pSGTMonCallbks))
	{
		ASTRALCN_MONITOR_DEBUG_MSG("sgt monitor mid init failed.\n");
		return AL_FAILURE;
	}

	return AL_SUCCESS;
}

/**
* @brief ASTRA LCN monitor init function.
*
* @param al_void.
*
* @return AL_SUCCESS if uninitialization is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_AstraLcn_Monitor_Uninit(al_void)
{
	if (g_bAstraLcnMonInited != al_true)
	{
		ASTRALCN_MONITOR_DEBUG_MSG("ASTRA LCN monitor has not inited.\n");
		return AL_UNINITIALIZED;
	}

	g_bAstraLcnMonInited = al_false;
	g_pfAstraLcnMonCallbks = al_null;
	MID_SGTUninit();
	
	return AL_SUCCESS;
}

/**
* @brief start mid ASTRA LCN for other service.
*
* start mid ASTRA LCN mon for receive SGT table.
*
* @ param None.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_AstraLcn_Monitor_Start(al_void)
{
	if (g_bAstraLcnMonInited != al_true)
	{
		ASTRALCN_MONITOR_DEBUG_MSG("astra lcn monitor has not inited.\n");
		return AL_UNINITIALIZED;
	}
	ASTRALCN_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);

	if (MID_SUCCESS != MID_SGTStart())
	{
		ASTRALCN_MONITOR_DEBUG_MSG("astra lcn monitor start failture.\n");
		return AL_FAILURE;
	}

	return AL_SUCCESS;
}

/**
* @brief stop mid ews for other service.
*
* stop mid ASTRA LCN mon.
*
* @ param bExit[input] exit mid ASTRA LCN task flag.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_AstraLcn_Monitor_Stop(al_bool bExit)
{
	if (g_bAstraLcnMonInited != al_true)
	{
		ASTRALCN_MONITOR_DEBUG_MSG("astra lcn monitor has not inited.\n");
		return AL_UNINITIALIZED;
	}
	
	ASTRALCN_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);

	if (MID_SUCCESS != MID_SGTStop(bExit))
	{
		ASTRALCN_MONITOR_DEBUG_MSG("astra lcn monitor stop failture.\n");
		return AL_FAILURE;
	}

	_SGTFreeList(&g_pAstraLcnData);
	
	return AL_SUCCESS;
}

AL_Return_t AI_AstraLcn_Monitor_StartGetSgtPid(al_void)
{
	ASTRALCN_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);

	MID_PmtOtherStop(al_false);
	if (MID_SUCCESS != MID_PmtOtherStart())
	{
		ASTRALCN_MONITOR_DEBUG_MSG("Start Get Sgt Pid failture.\n");
		return AL_FAILURE;
	}

	g_bAstraLcnStarted = al_true;
	
	return AL_SUCCESS;
}

AL_Return_t AI_AstraLcn_Monitor_StopGetSgtPid(al_void)
{
	ASTRALCN_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	g_bAstraLcnStarted = al_false;

	return AL_SUCCESS;
}

#endif

#ifdef __cplusplus
}
#endif
