/**
* 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 "ai_ews_monitor.h"
#include "al_ews_monitor.h"
#include "mid_playback.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

#define AL_EWS_MONITOR_DEBUG_FLAG 0
#define AL_EWS_MONITOR_ERROR_FLAG 0

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

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

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

#define MAX_LANGUAGE_LANGUAGE	(25)

/**<Global Variables*/
static dvbpsi_anouncement_t g_stAnouncement;
static AI_pfEWSMonCallback g_pfEwsMonCallbks = al_null;
static al_bool g_bEwsMonInited = al_false;
static al_bool g_bEwsMonStarted = al_false;

/**<Function*/

static DBPIDInfo_t _AI_EWS_Monitor_CheckCurrAudio(al_uint8 RefrType, al_uint16 OrgNetId, al_uint16 TSID, al_uint16 SrvId, al_uint8 ComTag)
{
	AL_RecHandle_t hCurrProgHdl = AL_DB_INVALIDHDL;
	al_uint16	usTsId = 0xffff;	
	al_uint16	usOrigNetId = 0xffff;
	al_uint16	usServiceId = 0xffff;
	dvbpsi_strmid_dr_t si_strmid;
	DBPIDInfo_t AudioPid;
	AL_PsiMon_AudioComp_t *pstCompList = NULL;
	frmwrk_pfOpConext_t pfEWSMonGetContext = al_null;
	al_uint16 u16AudioNum = 0;
	al_uint16 u16MinAudioPid = 0;
	al_uint8 u8VideoPidCount = 0;
	al_uint8 uCount = 0;
	al_uint8 AdpFidldId = 0;
	al_uint8 u8Languagenumber = 0;

	memset(&si_strmid, 0, sizeof(dvbpsi_strmid_dr_t));
	memset(&AudioPid, 0, sizeof(DBPIDInfo_t));
	AudioPid.usDataPID = DB_DVB_INVALID_PID;

	AL_FW_GetContextCb(&pfEWSMonGetContext);
	if (pfEWSMonGetContext != al_null)
	{
		pfEWSMonGetContext(FW_CONTEXT_TYPE_GET_LANUGAGE_NUMBER , FW_CONTEXT_OP_TYPE_READ,
		(al_void *)&u8Languagenumber, 0);
	}

	if ((MID_SUCCESS != MID_PlaybackGetCurProgHandle(al_null, &hCurrProgHdl))
		|| (hCurrProgHdl == AL_DB_INVALIDHDL))
	{
		EWS_MONITOR_ERROR_MSG("Get current program handle error.\n");
		return AudioPid;
	}

	if ((AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usTsId", (al_void *)&usTsId))
		|| (AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usOrigNetId", (al_void *)&usOrigNetId))
		|| (AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usServiceId", (al_void *)&usServiceId)))
	{
		EWS_MONITOR_ERROR_MSG("Get service info failed!!!\n");
		return AudioPid;
	}

	if (pstCompList == NULL)
	{
		pstCompList = (AL_PsiMon_AudioComp_t *)zero_malloc(sizeof(AL_PsiMon_AudioComp_t)*MAX_LANGUAGE_LANGUAGE);
		if (al_null == pstCompList)
		{
			goto FAILED;
		}
		memset((void *)pstCompList, 0, sizeof(AL_PsiMon_AudioComp_t)*MAX_LANGUAGE_LANGUAGE);
	}

	if (AL_SUCCESS != AL_PSI_Monitor_GetCurrProgAudio(pstCompList, &u16AudioNum, &u16MinAudioPid, &u8VideoPidCount))
	{
		goto FAILED;
	}
	
	if ((usOrigNetId == OrgNetId) && (usTsId == TSID) && (usServiceId == SrvId))
	{
		for(uCount = 0; uCount < u16AudioNum; uCount++)
		{
			if ((0 == dvbpsi_DecodeStrmIdDr(pstCompList[uCount].pu8Desc, pstCompList[uCount].u16DescLen,&si_strmid)) && (si_strmid.i_comp_data == ComTag))
			{
				AudioPid.usDataPID = pstCompList[uCount].u16ElementaryPid;
				AudioPid.usCodec = pstCompList[uCount].u8StreamType;
				//printf("[%s:%d] AudioPid ====================== %d\n",__FUNCTION__,__LINE__,AudioPid.usDataPID);
				break;
			}
		}	
	}
	else if (RefrType == 0x03)
	{
		for(uCount = 0; uCount < u16AudioNum; uCount++)
		{
			if (al_true == dvbpsi_AdaptationFieldDataDr(pstCompList[uCount].u16DescLen, pstCompList[uCount].pu8Desc, &AdpFidldId))
			{
				//printf("[%s:%d] AdpFidldId ====================== 0x%x\n",__FUNCTION__,__LINE__,AdpFidldId);
				if (AdpFidldId&0x01)
				{
					AudioPid.usDataPID = pstCompList[uCount].u16ElementaryPid;
					AudioPid.usCodec = pstCompList[uCount].u8StreamType;
					//printf("[%s:%d] AudioPid ====================== %d\n",__FUNCTION__,__LINE__,AudioPid.usDataPID);
					break;
				}
			}
		}
	}
	

	FAILED:
	if(pstCompList != NULL)
	{
		SAFE_FREE(pstCompList);
		pstCompList = NULL;
	}

	return AudioPid;
}

static al_void _AI_EWS_Monitor_CheckOtherAudio(MID_SiutilPmtStreamInfoDescr_t *pComp, al_uint16 ProNum)
{
	AL_RecHandle_t hCurrProgHdl = AL_DB_INVALIDHDL;
	al_uint16	usTsId = 0xffff;	
	al_uint16	usOrigNetId = 0xffff;
	al_uint16	usServiceId = 0xffff;
	dvbpsi_strmid_dr_t si_strmid;
	DBPIDInfo_t *pAudioPid = al_null;
	al_uint8 AdpFidldId = 0;

	memset(&si_strmid, 0, sizeof(dvbpsi_strmid_dr_t));
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	if ((MID_SUCCESS != MID_PlaybackGetCurProgHandle(al_null, &hCurrProgHdl))
		|| (hCurrProgHdl == AL_DB_INVALIDHDL))
	{
		EWS_MONITOR_ERROR_MSG("Get current program handle error.\n");
		return;
	}

	if ((AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usTsId", (al_void *)&usTsId))
		|| (AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usOrigNetId", (al_void *)&usOrigNetId))
		|| (AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usServiceId", (al_void *)&usServiceId)))
	{
		EWS_MONITOR_ERROR_MSG("Get service info failed!!!\n");
		return;
	}

	pAudioPid = (DBPIDInfo_t *)zero_malloc(sizeof(DBPIDInfo_t));
	if (pAudioPid == al_null)
	{
		return;
	}
	pAudioPid->usDataPID = DB_DVB_INVALID_PID;
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	/* reference type is 0x01: add for psi monitor re received sdt before pmt, then _AI_EWS_Monitor_CheckCurrAudio can not get audio info */
	if (g_stAnouncement.reference_type == 0x01) 
	{
		if ((usOrigNetId == g_stAnouncement.original_network_id) && (usTsId == g_stAnouncement.transport_stream_id) && (usServiceId == g_stAnouncement.service_id))
		{
			if ((0 == dvbpsi_DecodeStrmIdDr(pComp->p_desc, pComp->descLen, &si_strmid)) 
				&& (ProNum == g_stAnouncement.service_id) && (si_strmid.i_comp_data == g_stAnouncement.component_tag))
			{
				pAudioPid->usDataPID = pComp->elementary_pid;
				pAudioPid->usCodec = pComp->stream_type;
			}				
		 }
	}
	else if (g_stAnouncement.reference_type == 0x02)
	{
		if ((usOrigNetId == g_stAnouncement.original_network_id) && (usTsId == g_stAnouncement.transport_stream_id))
		{
			if ((0 == dvbpsi_DecodeStrmIdDr(pComp->p_desc, pComp->descLen, &si_strmid)) 
				&& (ProNum == g_stAnouncement.service_id) && (si_strmid.i_comp_data == g_stAnouncement.component_tag))
			{
				pAudioPid->usDataPID = pComp->elementary_pid;
				pAudioPid->usCodec = pComp->stream_type;
			}				
		 }
	}
	else if (g_stAnouncement.reference_type == 0x03)
	{
		if (al_true == dvbpsi_AdaptationFieldDataDr(pComp->descLen, pComp->p_desc, &AdpFidldId))
		{
			if (AdpFidldId&0x01)
			{
				pAudioPid->usDataPID = pComp->elementary_pid;
				pAudioPid->usCodec = pComp->stream_type;
			}
		}				
	}

	if (pAudioPid->usDataPID != DB_DVB_INVALID_PID)
	{
		g_pfEwsMonCallbks(AI_EWS_MONITOR_FIND_AUD, (al_uint32)pAudioPid);
	}
	else
	{
		SAFE_FREE(pAudioPid);
	}
}

al_void AI_EWS_Monitor_OtherPmtCallBK(al_void *pData)
{
	MID_SiutilPmtInfo_t *pPmt = (MID_SiutilPmtInfo_t *) pData;
	al_int iCount = 0;
	MID_SiutilPmtStreamInfoDescr_t *pComp = NULL;

	if (pPmt == NULL || g_bEwsMonStarted == al_false)
	{
		return;
	}
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	for (iCount = 0; iCount < pPmt->comp_count; iCount++)
	{
		pComp = &pPmt->p_comp[iCount];

		switch (pComp->stream_type)
		{
			case STREAM_TYPE_AUDIO_MPEG1:
			case STREAM_TYPE_AUDIO_MPEG2:
#ifdef SUPPORT_AUDIO_AC3
			case STREAM_TYPE_AUDIO_AC3:
#endif
			case STREAM_TYPE_AUDIO_AAC:
			case STREAM_TYPE_AUDIO_MPEG2_AAC:
			//case STREAM_TYPE_AUDIO_AVS:
				_AI_EWS_Monitor_CheckOtherAudio(pComp, pPmt->program_number);
				break;

			case STREAM_TYPE_PRIVATE_DATA:
			{
				al_uint8 audiodr_tags[4];

				audiodr_tags[0] = DESCR_AC3;
				audiodr_tags[1] = DESCR_EAC3;
				audiodr_tags[2] = DESCR_EXTENSION;
#ifdef CONFIG_DTMB_SYSTEM
				audiodr_tags[3] = DESCR_DRA_AUDIO_STREAM;
				al_uint8 RDA_Regist_audiodr_tags[1];
				RDA_Regist_audiodr_tags[0] = DESCR_DRA_REGISTRATION;
				if ((MID_SIUTIL_TagsInDescriptorArray(audiodr_tags, 4, pComp->p_desc, pComp->descLen) > 0)
					|| ((MID_SIUTIL_TagsInDescriptorArray(RDA_Regist_audiodr_tags, 1, pComp->p_desc, pComp->descLen) > 0)
					&& (dvbpsi_DecodeDRARegistrationDr(pComp->p_desc, pComp->descLen) == 0x44524131)))
#else
				if (MID_SIUTIL_TagsInDescriptorArray(audiodr_tags, 3, pComp->p_desc, pComp->descLen) > 0)
#endif
				{
					_AI_EWS_Monitor_CheckOtherAudio(pComp, pPmt->program_number);
				}
			}
				break;

			default:
				break;


		}
	}
}

/**
* @brief get EWS announcement support data.
*
* parse sdt service list, get annoumcement data.
*
* @ param pSdtDescr[input] descriptor in sdt service list.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_EWS_Monitor_InSdtEWSDispose(MID_SiutilSdtServiceDescr_t *pSdtDescr)
{
	if (pSdtDescr == al_null)
	{
		return AL_FAILURE;
	}
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	dvbpsi_announcemen_support_dr_t *pAncSptDr = al_null;

	pAncSptDr = dvbpsi_AnnouncementSptDr(pSdtDescr->descLen, pSdtDescr->p_descr);

	if (pAncSptDr != al_null)
	{
		//printf("[%s:%d] annoucement_number ====================== %d\n",__FUNCTION__,__LINE__,pAncSptDr->annoucement_number);
		al_uint8 i = 0;
		for (i = 0; i < pAncSptDr->annoucement_number; i++)
		{
			//printf("[%s:%d] announcement_type: %d, reference_type: %d, original_network_id: %d, transport_stream_id: %d, service_id: %d, component_tag: %d\n",__FUNCTION__,__LINE__, 
				//pAncSptDr->p_announcement[i].announcement_type, pAncSptDr->p_announcement[i].reference_type, 
				//pAncSptDr->p_announcement[i].original_network_id, pAncSptDr->p_announcement[i].transport_stream_id, 
				//pAncSptDr->p_announcement[i].service_id,pAncSptDr->p_announcement[i].component_tag);
			/* Emergency alarm */
			if ((pAncSptDr->p_announcement[i].announcement_type == 0x00) && 
				((g_stAnouncement.reference_type != pAncSptDr->p_announcement[i].reference_type) 
				|| (g_stAnouncement.original_network_id != pAncSptDr->p_announcement[i].original_network_id)
				|| (g_stAnouncement.transport_stream_id != pAncSptDr->p_announcement[i].transport_stream_id)
				|| (g_stAnouncement.service_id != pAncSptDr->p_announcement[i].service_id)
				|| (g_stAnouncement.component_tag != pAncSptDr->p_announcement[i].component_tag)))
			{
				printf("[%s:%d] reference_type: %d, %d, original_network_id: %d, %d, transport_stream_id: %d, %d, service_id: %d, %d, component_tag: %d, %d\n",__FUNCTION__,__LINE__, 
					pAncSptDr->p_announcement[i].reference_type, g_stAnouncement.reference_type,
					pAncSptDr->p_announcement[i].original_network_id, g_stAnouncement.original_network_id, 
					pAncSptDr->p_announcement[i].transport_stream_id, g_stAnouncement.transport_stream_id,
					pAncSptDr->p_announcement[i].service_id, g_stAnouncement.service_id,
					pAncSptDr->p_announcement[i].component_tag, g_stAnouncement.component_tag);
				memset (&g_stAnouncement, 0, sizeof(dvbpsi_anouncement_t));
				memcpy(&g_stAnouncement, &(pAncSptDr->p_announcement[i]), sizeof(dvbpsi_anouncement_t));
				g_pfEwsMonCallbks(AI_EWS_MONITOR_RECV_ANC, 0);
				break;
			}
		}

		dvbpsi_FreeAnnouncementSptDr(pAncSptDr);
	}
	else if ((g_stAnouncement.reference_type != 0)
		||(g_stAnouncement.original_network_id != 0) 
		||(g_stAnouncement.transport_stream_id != 0)
		||(g_stAnouncement.service_id != 0)
		||(g_stAnouncement.component_tag != 0) )
	{
		g_pfEwsMonCallbks(AI_EWS_MONITOR_NON_ANC, 0);
		AL_EWS_Monitor_ResetData();
	}

	return AL_SUCCESS;
}

/**
* @brief find need to monitor audio pid.
*
* find need to monitor audio pid according to anouncement data.
*
* @ param pAudioPid[output] audio pid.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_EWS_Monitor_FindAudioPid(DBPIDInfo_t *pAudioPid)
{
	AL_RecHandle_t hCurrProgHdl = AL_DB_INVALIDHDL;
	al_uint16	usTsId = 0xffff;	
	al_uint16	usOrigNetId = 0xffff;
	al_uint16	usServiceId = 0xffff;	
	dvbpsi_strmid_dr_t si_strmid;
	DBPIDInfo_t AudioPid;

	if (pAudioPid == NULL)
	{
		return AL_FAILURE;
	}
	
	memset(&si_strmid, 0, sizeof(dvbpsi_strmid_dr_t));
	memset(&AudioPid, 0, sizeof(DBPIDInfo_t));
	AudioPid.usDataPID = DB_DVB_INVALID_PID;
	
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	if ((MID_SUCCESS != MID_PlaybackGetCurProgHandle(al_null, &hCurrProgHdl))
		|| (hCurrProgHdl == AL_DB_INVALIDHDL))
	{
		EWS_MONITOR_ERROR_MSG("Get current program handle error.\n");
		return AL_FAILURE;
	}

	if ((AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usTsId", (al_void *)&usTsId))
		|| (AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usOrigNetId", (al_void *)&usOrigNetId))
		|| (AL_SUCCESS != AL_DB_QueryDetailFieldByName(hCurrProgHdl, (al_uint8 *)"usServiceId", (al_void *)&usServiceId)))
	{
		EWS_MONITOR_ERROR_MSG("Get service info failed!!!\n");
		return AL_FAILURE;
	}
	
	/* 0x00:Announcement is broadcast in the usual audio stream of the service
	*   0x01:Announcement is broadcast in a separate audio stream that is part of the service 
	*   0x02:Announcement is broadcast by means of a different service within the same transport stream 
	*   0x03:Announcement is broadcast by means of a different service within a different transport stream
	*/
	/* first find out on current service */
	AudioPid = _AI_EWS_Monitor_CheckCurrAudio(g_stAnouncement.reference_type, g_stAnouncement.original_network_id,
		g_stAnouncement.transport_stream_id, g_stAnouncement.service_id, g_stAnouncement.component_tag);
	//EWS_MONITOR_DEBUG_MSG("[%s:%d],  AudioPid ========== %d\n",__FUNCTION__,__LINE__,AudioPid.usDataPID);

	memcpy(pAudioPid, &AudioPid, sizeof(DBPIDInfo_t));
	return AL_SUCCESS;
}

/**
* @brief ews 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_EWS_Monitor_Init(AI_pfEWSMonCallback pfEwsMonCallbks)
{
	if (g_bEwsMonInited == al_true)
	{
		EWS_MONITOR_ERROR_MSG("ews monitor has inited.\n");
		return AL_SUCCESS;
	}

	g_bEwsMonInited = al_true;
	g_pfEwsMonCallbks = pfEwsMonCallbks;

	return AL_SUCCESS;
}

/**
* @brief ews monitor init function.
*
* @param al_void.
*
* @return AL_SUCCESS if uninitialization is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_EWS_Monitor_Uninit(al_void)
{
	if (g_bEwsMonInited != al_true)
	{
		EWS_MONITOR_ERROR_MSG("ews monitor has not inited.\n");
		return AL_UNINITIALIZED;
	}

	g_bEwsMonInited = al_false;
	g_pfEwsMonCallbks = al_null;
	return AL_SUCCESS;
}

/**
* @brief start mid ews for other service.
*
* start mid ews for find audio pid in other services.
*
* @ param None.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_EWS_Monitor_Start(al_void)
{
	if (g_bEwsMonInited != al_true)
	{
		EWS_MONITOR_ERROR_MSG("ews monitor has not inited.\n");
		return AL_UNINITIALIZED;
	}
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);

	MID_PmtOtherStop(al_false);
	if (MID_SUCCESS != MID_PmtOtherStart())
	{
		EWS_MONITOR_ERROR_MSG("ews monitor start failture.\n");
		return AL_FAILURE;
	}

	g_bEwsMonStarted = al_true;
	return AL_SUCCESS;
}

/**
* @brief stop mid ews for other service.
*
* stop mid ews for find audio pid in other services.
*
* @ param bExit[input] exit mid ews task flag.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AI_EWS_Monitor_Stop(al_bool bExit)
{
	if (g_bEwsMonInited != al_true)
	{
		EWS_MONITOR_ERROR_MSG("ews monitor has not inited.\n");
		return AL_UNINITIALIZED;
	}
	
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	g_bEwsMonStarted = al_false;
	
	return AL_SUCCESS;
}

/**
* @brief get anouncement data.
*
* Get anouncement data for change audio.
*
* @ param pAncmData[output] anouncement data.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AL_EWS_Monitor_GetData(dvbpsi_anouncement_t *pAncmData)
{
	if (pAncmData == al_null)
	{
		return AL_FAILURE;
	}
	
	*pAncmData = g_stAnouncement;
	return AL_SUCCESS;
}

/**
* @brief reset anouncement data.
*
* Reset anouncement data for change channel and so on.
*
* @ param None.
*
* @return AL_SUCCESS  is successful, AL_FAILURE otherwise.
*/
AL_Return_t AL_EWS_Monitor_ResetData(al_void)
{
	EWS_MONITOR_DEBUG_MSG("[%s:%d] \n",__FUNCTION__,__LINE__);
	memset(&g_stAnouncement, 0, sizeof(dvbpsi_anouncement_t));
	return AI_EWS_Monitor_Stop(TRUE);
}
#ifdef __cplusplus
}
#endif
