/*
*********************************************************************************
*                                                                               *
* NOTICE:                                                                 *
*                                                                               *
*********************************************************************************
*                         mgcPhyPort <---------- mgcChnls <-----------mgcConnections
*/
 
    
/*********************************************************************************
 *                              <mgc_chnl_info.c>                                                              
 *     This file should provide CHNL_INFO APIs used in MGC module. Every chnl should be attached to
 *a phyport if used, and could contain servral connections
 *
 *    Author                         Date
 *    ------                        ------
 *    Sam Yao                     Aug 2008                                                                                              
 *********************************************************************************/


/*-----------------------------------------------------------------------*/
/*                        INCLUDE HEADER FILES                           */
/*-----------------------------------------------------------------------*/
#include "./include/mgc_chnl_info.h"
#include "./include/mgc_debug.h"
#include "./include/mgc_conn_info.h"
#include "./include/mgc_phy_port.h"
#include "./include/mgc_mg_info.h"
#include "./include/mgc_mgcp.h"

static CHNL_INFO mgcChnl[MGC_MAX_NUM_OF_CHNL];
/*-----------------------------------------------------------------------
              CHNL_INFO      M A N A G E R
 ------------------------------------------------------------------------*/	 

/***************************************************************************
 * mgc_chnl_info_init
 * ------------------------------------------------------------------------
 * General: init CHNL_INFO structure objcet
 * Return Value:  void.
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo- the pointer of the CHNL_INFO object which should be init
 *	       id - the id of the CHNL_INFO object
 ***************************************************************************/
void mgc_chnl_info_init(CHNL_INFO *pChnlInfo , int id)
{
    int i;
    if((pChnlInfo == NULL)||(id < 0))
        return;
	
	pChnlInfo->id = id;
    pChnlInfo->status = MGC_CHNL_INFO_STATUS_UNDEF;
	pChnlInfo->pPhyPort = NULL;
	pChnlInfo->connectNum = 0;
	pChnlInfo->monFlag = 0;
    pChnlInfo->maxConnectNum = 0;
    for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
    {
        pChnlInfo->pConnection[i] = NULL;
    }
    return;	
}

void mgc_chnl_info_setup(void)
{
    int i;
	CHNL_INFO *pChnlInfo = NULL;
	
    for(i=0 ; i<MGC_MAX_NUM_OF_CHNL ; i++)
    {
        pChnlInfo = &mgcChnl[i];
		mgc_chnl_info_init(pChnlInfo, i);
    }
    return;
}

/***************************************************************************
 * mgc_chnl_info_clear
 * ------------------------------------------------------------------------
 * General: clear and init CHNL_INFO structure objcet , if any connection is created in this chnl, 
 *               DLCX will be sent out
 * Return Value:  void.
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo- the pointer of the CHNL_INFO object which should be init
 ***************************************************************************/
void mgc_chnl_info_clear(CHNL_INFO *pChnlInfo)
{
    int i;
	CONNECT_INFO *pConnect = NULL;
	MGCP_PARA mgcpPara;
    if(pChnlInfo == NULL)
        return;		

    for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
    {
        pConnect = pChnlInfo->pConnection[i];

		if(pConnect == NULL)
		    continue;	

        if((mgc_connect_get_status(pConnect) == MGC_CONNECT_STATUS_CREATED)||(mgc_connect_get_status(pConnect) == MGC_CONNECT_STATUS_CREATE))
        {
            if(mgc_connect_dlcx_para_create(pConnect , &mgcpPara) == TRUE)
		        MGCP_req(mgc_mgcp_get_sap_index(), MGCP_CMD_DLCX , 0xFFFF , &mgcpPara);
        }
		mgc_connect_init(pConnect , pConnect->id);
    }

    mgc_chnl_info_init(pChnlInfo , pChnlInfo->id);
	return;
}


/***************************************************************************
 * mgc_chnl_info_get_unused_chnl
 * ------------------------------------------------------------------------
 * General:  get a unused CHNL_INFO structure
 * Return Value:  if failure return NULL , else return a pointer
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	void
 ***************************************************************************/
CHNL_INFO *mgc_chnl_info_get_unused_chnl(void)
{
    int i;
    CHNL_INFO *pChnlInfo = NULL;

    for(i=0 ; i<MGC_MAX_NUM_OF_CHNL ; i++)
    {
        pChnlInfo = &mgcChnl[i];

        if(pChnlInfo->pPhyPort != NULL)
		    continue;
		
		return pChnlInfo;
    }

    return NULL;	
}

/***************************************************************************
 * mgc_chnl_info_attach_to_phy_port
 * ------------------------------------------------------------------------
 * General:  record  the owner phyPort of the chnl
 * Return Value:  if failure return FALSE , else return TRUE
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo - the pointer of the CHNL_INFO object which need to record it's own phyPort
 *           pPhyPort -  the pointer of the PHY_PORT_INFO object which is the owner of the chnl
 ***************************************************************************/
BOOL mgc_chnl_info_attach_to_phy_port(CHNL_INFO *pChnlInfo , PHY_PORT_INFO *pPhyPort)
{
    if((pChnlInfo == NULL) || (pPhyPort == NULL))
        return FALSE;		

    if(pChnlInfo->status != MGC_CHNL_INFO_STATUS_UNDEF)    
        return FALSE;		

    switch(pPhyPort->portType)
    {
        case MGC_PHY_PORT_TYPE_E1:
	    case MGC_PHY_PORT_TYPE_T1:
		case MGC_PHY_PORT_TYPE_ANALOG:
		case MGC_PHY_VIRTUAL_TYPE_INTERNAL:	
             pChnlInfo->maxConnectNum = 1;
			 break;
		case MGC_PHY_VIRTUAL_TYPE_TANDEM:
			 pChnlInfo->maxConnectNum = 2;
			 break;
		case MGC_PHY_VIRTUAL_TYPE_SURVEILLANCE:
			 pChnlInfo->maxConnectNum = 1;			
			 break;
	    case MGC_PHY_VIRTUAL_TYPE_ANN:
			 pChnlInfo->maxConnectNum = MGC_MAX_NUM_OF_CHNL_CON;
			 break;
		default:
			 pChnlInfo->maxConnectNum = 0;
			 break;
    }

    pChnlInfo->pPhyPort = pPhyPort;
	return TRUE;
}

/***************************************************************************
 * mgc_chnl_info_status_set
 * ------------------------------------------------------------------------
 * General: set the status of the CHNL_INFO object
 * Return Value:  void
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo - the pointer of the CHNL_INFO object which need to be set
 *           status -  the status of the CHNL_INFO object
 ***************************************************************************/
void mgc_chnl_info_status_set(CHNL_INFO *pChnlInfo , MGC_CHNL_INFO_STATUS status)
{
    if(pChnlInfo == NULL)
        return;

    pChnlInfo->status = status;
	return;
}



/***************************************************************************
 * mgc_chnl_info_find_chnl_of_phy
 * ------------------------------------------------------------------------
 * General: get the CHNL_INFO object in the phyPort according to the chnl id
 * Return Value:  if failure return NULL ,  else return the pointor of the object
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pPhyPort - the pointer of the phyPort which contain the CHNL_INFO objects pointers
 *           chnlNo -  the CHNL_INFO object id
 ***************************************************************************/
CHNL_INFO *mgc_chnl_info_find_chnl_of_phy(PHY_PORT_INFO *pPhyPort , int chnlNo)
{

    if(pPhyPort == NULL)
       return NULL;

    if(chnlNo > MGC_MAX_CHNL_NUM_PER_PHY)
       return NULL;
	
    if(chnlNo >= pPhyPort->chnlNum)
    {
        MGC_WARN("chnlNo is out of suppose to be ");
    }
		
    return pPhyPort->pChnlInfo[chnlNo];
}

/***************************************************************************
 * mgc_chnl_info_get_chnl
 * ------------------------------------------------------------------------
 * General: get the CHNL_INFO object according to the CHNL parameters
 * Return Value:  if failure return NULL ,  else return the pointor of the object
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	chnl -  CHNL object which contain mgNo , chnlNo, connectNo and phyPort No
 ***************************************************************************/
CHNL_INFO *mgc_chnl_info_get_chnl(CHNL chnl)
{
    MG_INFO *pMgInfo = NULL;
    PHY_PORT_INFO *pPhyPort = NULL;
    CHNL_INFO *pChnlInfo = NULL;
	
    pMgInfo = mgc_mg_info_get_index_mg(chnl.mgNo);
    if(pMgInfo == NULL)
	    return NULL;

    pPhyPort = mgc_phy_port_find_port_of_mg(pMgInfo , chnl.portNo); 
    if(pPhyPort == NULL)
        return NULL;

    pChnlInfo = mgc_chnl_info_find_chnl_of_phy(pPhyPort , chnl.chlNo);
    if(pChnlInfo == NULL)
        return NULL;

    return pChnlInfo;
}


/***************************************************************************
 * mgc_chnl_info_attach_connect
 * ------------------------------------------------------------------------
 * General: record a connection in the assgined  place
 * Return Value:  if failure return FALSE ,  else return TRUE
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo -  the pointer of the CHNL_INFO object which need to record the connect
 *           pConnect -  the pointer of the CONNECT_INFO object
 *           connNo -  the place where the CONNECT_INFO should be record in
 ***************************************************************************/
BOOL mgc_chnl_info_attach_connect(CHNL_INFO *pChnlInfo , CONNECT_INFO *pConnect , int connNo)
{
    if((pChnlInfo == NULL)||(pConnect == NULL))
        return FALSE;

    if(connNo >= MGC_MAX_NUM_OF_CHNL_CON)
	    return FALSE;	

    if(mgc_connect_get_status(pConnect)!= MGC_CONNECT_STATUS_UNDEF)
        return FALSE;

    if(pChnlInfo->pConnection[connNo] != NULL)
        return FALSE;		 

    if(pChnlInfo->connectNum >= pChnlInfo->maxConnectNum)
	    return FALSE;
	
    if(mgc_connect_attach_connect_to_chnl(pConnect , pChnlInfo) == FALSE)
	    return FALSE;	
	
    pChnlInfo->pConnection[connNo] = pConnect;
	pChnlInfo->connectNum++;
	mgc_connect_set_status(pConnect , MGC_CONNECT_STATUS_IDLE);
	mgc_chnl_info_status_set(pChnlInfo , MGC_CHNL_INFO_STATUS_USED);
	return TRUE;
}

/***************************************************************************
 * mgc_chnl_info_detach_connect
 * ------------------------------------------------------------------------
 * General: remove a connect info from the CHNL_INFO object
 * Return Value:  if failure return FALSE ,  else return TRUE
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo -  the pointer of the CHNL_INFO object which need to remove connect info
 *           pConnect -  the pointer of the CONNECT_INFO object which need to be remove
 ***************************************************************************/
BOOL mgc_chnl_info_detach_connect(CHNL_INFO *pChnlInfo , CONNECT_INFO *pConnect)
{
    int i;
	
    if((pChnlInfo == NULL)||(pConnect == NULL))
        return FALSE;

    for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
    {
        if(pConnect != pChnlInfo->pConnection[i])
		    continue;

		pChnlInfo->pConnection[i] = NULL;
		pChnlInfo->connectNum--;
		mgc_connect_init(pConnect, pConnect->id);
//		MGC_DEBUG("conn[%d] detach from chnlInfo[%d], as connNo %d" , pConnect->id , pChnlInfo->id , i);
		return TRUE;
    }

	return FALSE;
}

/***************************************************************************
 * mgc_chnl_info_get_chnl_no_of_phy
 * ------------------------------------------------------------------------
 * General: find the record place of the CHNL_INFO object in it's owner phyPort
 * Return Value:  if failure return -1 ,  else return the place number
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo -  the pointer of the CHNL_INFO object which need to be find
 *           pPhyPort -  the pointer of the PHY_PORT_INFO object which contain the CHNL_INFO
 ***************************************************************************/
int mgc_chnl_info_get_chnl_no_of_phy(CHNL_INFO *pChnlInfo , PHY_PORT_INFO *pPhyPort)
{
    int i;
    if((pChnlInfo == NULL)||(pPhyPort == NULL))
        return -1;

    for(i=0 ; i<MGC_MAX_CHNL_NUM_PER_PHY ; i++)
    {
        if(pChnlInfo != pPhyPort->pChnlInfo[i])
		    continue;

		return i;
    }

	return -1;
}


/***************************************************************************
 * mgc_chnl_info_assign_connect
 * ------------------------------------------------------------------------
 * General: assgin a connection on the assigned place
 * Return Value:  if failure return NULL,  else return the pointer of the connect
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo -  the pointer of the CHNL_INFO object which need to assign
 *           connNo - the assgined place NO
 ***************************************************************************/
CONNECT_INFO *mgc_chnl_info_assign_connect(CHNL_INFO *pChnlInfo , int connNo)
{
    CONNECT_INFO *pConnect = NULL;
   
    if(pChnlInfo == NULL)
        return NULL;

    pConnect = mgc_connect_find_connect_of_chnl(pChnlInfo, connNo);
    if(pConnect != NULL)
    {
        //MGC_DEBUG("chnlInfo[%d] already have conn[%d], as connNo %d" , pChnlInfo->id , pConnect->id , connNo);    
        return pConnect;	    
    }
	
    if(pChnlInfo->connectNum >= pChnlInfo->maxConnectNum)
	    return FALSE;	
	
    pConnect = mgc_connect_get_unused();
    if(pConnect == NULL)
        return NULL;

    if(mgc_chnl_info_attach_connect(pChnlInfo , pConnect , connNo) == FALSE)
        return NULL;	

    //MGC_DEBUG("assign unused conn[%d] to chnlInfo[%d]connNo[%d]" , pConnect->id , pChnlInfo->id, connNo);
    return pConnect;
}


/***************************************************************************
 * mgc_chnl_info_assign_idle_connect
 * ------------------------------------------------------------------------
 * General: assgin a connection on the CHNL_INFO object
 * Return Value:  if failure return NULL,  else return the pointer of the connect
 * ------------------------------------------------------------------------
 * Arguments:
 * Input: 	pChnlInfo -  the pointer of the CHNL_INFO object which need to assign
 ***************************************************************************/
CONNECT_INFO *mgc_chnl_info_assign_idle_connect(CHNL_INFO *pChnlInfo)
{
    int i;
	CONNECT_INFO *pConnect = NULL;
	CONNECT_INFO *pConnectTmp = NULL;
	
    if(pChnlInfo == NULL)
	    return NULL;	

	//MGC_DEBUG("func %s " , __FUNCTION__);
    for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
    {
        pConnect = pChnlInfo->pConnection[i];

		if(pConnect != NULL)	
		{
		    if(mgc_connect_get_status(pConnect) != MGC_CONNECT_STATUS_IDLE)	
		        continue;

			return pConnect;
		}

		pConnectTmp = mgc_chnl_info_assign_connect(pChnlInfo , i);

        if(pConnectTmp == NULL)
	        continue;	

        return pConnectTmp;	
    }

	return NULL;
}

void mgc_chnl_info_set_mon(int id, BOOL enable)
{
    if((id < 0) || (id > MGC_MAX_NUM_OF_CHNL))
	    return;

	mgcChnl[id].monFlag = enable;
}

BYTE *mgc_chnl_info_get_res_addr(void)
{
    return (BYTE *)mgcChnl;
}


char *mgc_chnl_info_print_codec(CHNL_INFO *pChnlInfo)
{
    int i;
    CONNECT_INFO *pConnect = NULL;

    if(pChnlInfo == NULL)
	    return "UNKNOWN";

	if(pChnlInfo->maxConnectNum > 1)
	    return "UNKNOWN";

    for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
    {
        pConnect = pChnlInfo->pConnection[i];
		if(pConnect != NULL)
		    break;	
    }	

    if(pConnect == NULL)
	    return "UNKNOWN";		

	return mgc_mgcp_print_vc_codec(pConnect->mediaAttr.vocoderType);
}



char *mgc_chnl_info_print_remote(CHNL_INFO *pChnlInfo)
{
    int i;
    CONNECT_INFO *pConnect = NULL;	
	PUB_SDP_MEDIA *pMedia = NULL;	
	PUB_SDP_MSG *pSdp = NULL;	
    static char buf[32];

	sprintf(buf , "0.0.0.0:0");
	
	if(pChnlInfo == NULL)
	    return buf;	

    if(pChnlInfo->maxConnectNum > 1)
	    return buf;

    for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
    {
	    pConnect = pChnlInfo->pConnection[i];
	    if(pConnect != NULL)
    		break;	
    }	

    if(pConnect == NULL)
	    return buf;

    pSdp = &pConnect->mediaAttr.sdp;
	pMedia = &pSdp->medias.medias[0];
	if((pMedia->m.port != 0)||(strlen(pSdp->c.addr) != 0))
	    sprintf(buf , "%s:%d" , pSdp->c.addr, pMedia->m.port);
	return buf;
}

char *mgc_chnl_info_print_mode(CHNL_INFO *pChnlInfo)
{
    int i;
    CONNECT_INFO *pConnect = NULL;

    if(pChnlInfo == NULL)
	    return "INACTIVE";

	if(pChnlInfo->maxConnectNum > 1)
	    return "INACTIVE";	

    for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
    {
        pConnect = pChnlInfo->pConnection[i];
		if(pConnect != NULL)
		    break;	
    }	

    if(pConnect == NULL)
	    return "INACTIVE";	

    switch(mgc_connect_get_conn_mode(pConnect))
    {
        case MGC_CON_MODE_INACTIVE:
			return "INACTIVE";
		case MGC_CON_MODE_RECVONLY:
			return "RECVONLY";
		case MGC_CON_MODE_SENDONLY:
			return "SENDONLY";
		case MGC_CON_MODE_SENDRECV:
			return "SENDRECV";
		default:
			break;
    }

	return "INACTIVE";
}

char *mgc_chnl_info_print_status(CHNL_INFO *pChnlInfo)
{
    int i;
    CONNECT_INFO *pConnect = NULL;
	
    if(pChnlInfo == NULL)
	    return "UNDEF";

    if(pChnlInfo->maxConnectNum > 1)
	    return "UNDEF";	

    for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
    {
        pConnect = pChnlInfo->pConnection[i];
		if(pConnect != NULL)
		    break;	
    }

    if(pConnect == NULL)
	    return "IDLE";

    switch(mgc_connect_get_status(pConnect))
    {
        case MGC_CONNECT_STATUS_IDLE:
			return "IDLE";
		case MGC_CONNECT_STATUS_CREATE:
			return "CREATE";
		case MGC_CONNECT_STATUS_CREATED:
			return "CONNECT";
		case MGC_CONNECT_STATUS_CREATING:
			return "CREATING";
		default:
			break;
    }

	return "UNDEF";
}

#ifdef MGC_TEST_ENABLE
int mgc_chnl_info_assigned_num(void)
{
    int i , num;
    CHNL_INFO *pChnlInfo = NULL;

    num = 0;
    for(i=0 ; i<MGC_MAX_NUM_OF_CHNL ; i++)
    {
        pChnlInfo = &mgcChnl[i];

		if(pChnlInfo->pPhyPort != NULL)
		    num++;	
    }

	return num;
}

#endif                               
