/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
// ----------------------------------------------------------------------
//
// This Software is an original work of authorship of PacketVideo Corporation.
// Portions of the Software were developed in collaboration with NTT  DoCoMo,
// Inc. or were derived from the public domain or materials licensed from
// third parties.  Title and ownership, including all intellectual property
// rights in and to the Software shall remain with PacketVideo Corporation
// and NTT DoCoMo, Inc.
//
// -----------------------------------------------------------------------
/*****************************************************************************/
/*  file name            : tsc_ce.c                                          */
/*  file contents        : Terminal State Control routine                    */
/*  draw                 : '96.10.04                                         */
/*---------------------------------------------------------------------------*/
/*  amendment                                                                */
/*              Copyright (C) 1996 NTT DoCoMo                                */
/*****************************************************************************/
#include "tscmain.h"
#include "tsc_sub.h"     /* Sub Routine Information Header                */
#include "tsc_constants.h"
#include "tsc_statemanager.h"
#include "tsc_capability.h"
#include "tsc_component.h"

//////////////////////////////////////////////////////////////////////////
// Start the CE process by sending this terminals capabilites to the peer terminal
//////////////////////////////////////////////////////////////////////////
void TSC_324m::CEStart(void)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "TSC_324m: CEStart\n"));
    if (iCeRetries <= 0)
    {
        return;
    }
    if (iTSCcomponent->CEStart())
    {
        iCeRetries--;
    }
}

////////////////////////////////////////////////////////////////////////
// CE User - Transfer.Indication primitive received from H.245
////////////////////////////////////////////////////////////////////////
void TSC_324m::CETransferIndication(OsclSharedPtr<S_TerminalCapabilitySet> tcs)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "TSC_324m: CE transfer indication received.\n"));

    // Cancel timers for TCS receive
    iTimer->Cancel(PV_TSC_TCS_RECEIVE_TIMER_ID);


    Tsc324mNodeCommand* cmd = iTSCcomponent->GetCommand();
    if (cmd)
    {
        iCmdQueue.AddL(*cmd);
        OSCL_DELETE(cmd);
        RunIfNotReady();
    }


    ////////////////////////
    // State = Call Setup
    ////////////////////////
    if (GetTerminalStatus() == PhaseD_CSUP)
    {
        // TRANSFER.response(CE) Primitive Send
        if (Ce) Ce->TransferResponse();

        ExtractTcsParameters(tcs);

        iTSCcomponent->CETransferIndication(tcs, GetTerminalStatus());

        if (iTSCcomponent->GetVideoLayer() == 0)
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "TSC_324m::CETransferIndication - Failed to negotiate video layer.\n"));
            iCeRetries = iN100;
            iConnectFailReason = EPVT_FailedToNegotiate;
            SessionClose_CSUP();
        }
        if (iTSCstatemanager.ReadState(TSC_CE_RECEIVE) != COMPLETE)
        {
            iTSCstatemanager.WriteState(TSC_CE_RECEIVE, COMPLETE);

            if ((iTSCstatemanager.ReadState(TSC_MSD) == COMPLETE))
            {
                int leave_status = 0;
                OSCL_TRY(leave_status, TcsMsdComplete());
                OSCL_FIRST_CATCH_ANY(leave_status, void());
                if (leave_status != 0)
                {
                    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_WARNING,
                                    (0, "TSC_324m::CETransferIndication - Memory Allocation Failed."));
                    SignalCsupComplete(PVMFErrNoMemory);
                    return;
                }
            }
        }
    }
    //////////////////////////////////
    // State = Ongoing Communication
    //////////////////////////////////
    else if (GetTerminalStatus() == PhaseE_Comm)
    {
        iTSCcomponent->CETransferIndication(tcs, GetTerminalStatus());
        if (Ce) Ce->TransferResponse();
    }
    else
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                        (0, "TSC_324m::CETransferIndication Error - invalid state(%d)", iTerminalStatus));
    }
}

////////////////////////////////////////////////////////////////////////
// CE User - Transfer.Confirm primitive received from H.245
////////////////////////////////////////////////////////////////////////
void TSC_324m::CETransferConfirm()
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "TSC_324m::CETransferConfirm Capability Exchange Send...Complete."));
    ////////////////////////
    // State = Call Setup
    ////////////////////////
    if (GetTerminalStatus() == PhaseD_CSUP || GetTerminalStatus() == PhaseE_Comm)
    {
        iTSCstatemanager.WriteState(TSC_CE_SEND, COMPLETE);
    }
    else
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                        (0, "TSC_324m::CETransferConfirm Error - Invalid state(%d)", iTerminalStatus));
    }
    iCeRetries = iN100; /* Num CE retries left */
}

////////////////////////////////////////////////////////////////////////
// CE User - Reject.Indication primitive received from H.245
////////////////////////////////////////////////////////////////////////
void TSC_324m::CERejectIndication(CESource source, CECause cause, CEDirection direction)
{
    OSCL_UNUSED_ARG(cause);

    /* WWUAPI: four scenarios can cause this function being called
        1. INCOMING SE gets TCS again while in AWAITING RESPONSE state
           We send out CE RPS as soon as we receive a TCS.  It means the other temrinal
           is a whacky terminal sending out successive TCS's.  Maybe we just use the most
           recent TCS ?
        2. OUTGOING SE gets REJECT while in AWAITING RESPONSE state
           End Session and reset.
        3. TIMEOUT for OUTGOING SE
           End Session and reset if we are in PhaseD_CSUP
        4. INCOMING SE gets RELEASE while in AWAITING RESPONSE
           We took too much time to send a response and remote SE timed out.
           End Session and reset
       We need a fat brain in the future to handle all these unusual cases.
       Here we just do nothing.
    */

    ////////////////////////
    // State = Call Setup
    ////////////////////////
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "TSC_324m: CE Reject Indication received."));

    if (GetTerminalStatus() == PhaseD_CSUP || GetTerminalStatus() == PhaseE_Comm)
    {
        if (direction == CE_OUTGOING)
        {
            if (source == CE_USER)  /* Reject */
            {
                iCeRetries = iN100;
                iConnectFailReason = EPVT_ErrorRemoteRejected;
                SessionClose_CSUP();
            }
            else  /* Caused by PROTOCOL, most likely due to timeouts */
            {
                if (iCeRetries)
                {
                    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                                    (0, "TSC_324m: Starting CE Send.\n"));
                    CEStart();      /* INITIATE CE-SEND */
                }
                else  /* Retried enough. Call setup failed */
                {
                    iCeRetries = iN100;
                    iConnectFailReason = EPVT_Timeout;
                    SessionClose_CSUP();
                }
            }
        }
        else  /* INCOMING */
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "TSC_324m: Capability Exchange(I)...Release received.\n"));
            SessionClose_CSUP();
        }
    }
    else
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                        (0, "TSC_324m::CERejectIndication Error - Invalid state(%d)", iTerminalStatus));
    }
}

////////////////////////////////////////////////////////////////////////////
// ExtractTcsParameters()                       (RAN-32K)
//
// This routine takes the incoming TerminalCapabilitySet
//   and extracts the following useful parameters:
//      {h263_qcifMPI, h263_maxBitRate, mpeg4_maxBitRate}
// The parameters are stored in globals and may be sent
//   later to the application.
////////////////////////////////////////////////////////////////////////////
void TSC_324m::ExtractTcsParameters(PS_TerminalCapabilitySet pTcs)
{
    uint32 i;
    PS_CapabilityTableEntry pCapEntry = NULL;
    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "TSC_324m::ExtractTcsParameters"));

    iTSCcapability.ResetCapability();
    Oscl_Vector<CPvtMediaCapability*, OsclMemAllocator> capabilityItems;
    uint32 userInputCapabilities = 0;
    struct _UserInputCapability *userInputCapability = NULL;

    iTSCcomponent->ExtractTcsParameters(pTcs);
    if (pTcs->option_of_multiplexCapability)
    {
        PS_MultiplexCapability muxcaps = &pTcs->multiplexCapability;
        if (muxcaps->index == 2)
        {
            PS_H223Capability h223caps = muxcaps->h223Capability;
            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "TSC_324m::ExtractTcsParameters maximumAl2SDUSize=%d,maximumAl3SDUSize=%d",
                             h223caps->maximumAl2SDUSize, h223caps->maximumAl3SDUSize));
            iH223->SetSduSize(OUTGOING, h223caps->maximumAl2SDUSize, E_EP_MEDIUM);
            iH223->SetSduSize(OUTGOING, h223caps->maximumAl3SDUSize, E_EP_HIGH);
            if ((h223caps->option_of_nsrpSupport == ON) && (h223caps->nsrpSupport == ON))
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                                (0, "TSC_324m: Remote video caps Option of NSRP support is ON\n"));

                // switch to NSRP if mux level == 0.  If level was 1 or 2, we would have switched after level setup
                if (iH223->GetMuxLevel() == 0)
                {
                    iSrp->UseNSRP(true);
                }
            }
            else
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                                (0, "TSC_324m: Remote video caps Option of NSRP support is OFF\n"));
            }

            if (h223caps->option_of_mobileOperationTransmitCapability)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                                (0, "TSC_324m: Remote video caps Option of Mobile Transmit Capability is ON\n"));
                struct _MobileOperationTransmitCapability* mobile_caps = &h223caps->mobileOperationTransmitCapability;
                if (iTransmitCaps)
                    OSCL_DEFAULT_FREE(iTransmitCaps);
                iTransmitCaps = (struct _MobileOperationTransmitCapability*)OSCL_DEFAULT_MALLOC(sizeof(struct _MobileOperationTransmitCapability));
                oscl_memcpy(iTransmitCaps, mobile_caps, sizeof(struct _MobileOperationTransmitCapability));
            }

            PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "TSC_324m::ExtractTcsParameters option_of_maxMUXPDUSizeCapability=%d",
                             h223caps->option_of_maxMUXPDUSizeCapability));
            if (h223caps->option_of_maxMUXPDUSizeCapability)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                                (0, "TSC_324m: Remote maxMuxPduCapability - %d\n",
                                 h223caps->maxMUXPDUSizeCapability));
                iMaxMuxPduCapabilityR = h223caps->maxMUXPDUSizeCapability ? true : false;
                unsigned size = iRequestMaxMuxPduSize;
                if (iH223->GetMuxLevel() == H223_LEVEL2)
                    size = size > H223_MAX_MUX_PDU_SIZE_LEVEL2 ? H223_MAX_MUX_PDU_SIZE_LEVEL2 : size;
                if (iMaxMuxPduCapabilityR && size)
                {
                    PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                                    (0, "TSC_324m::ExtractTcsParameters Requesting max mux pdu size (%d) from remote",
                                     size));
                    RequestMaxMuxPduSize(size);
                }
            }
        }
    }

    if (pTcs->option_of_capabilityTable)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                        (0, "TSC_324m: Remote video caps Option of Capability Table is ON, size(%d)\n",
                         pTcs->size_of_capabilityTable));
        for (i = 0; i < pTcs->size_of_capabilityTable; ++i)
        {
            pCapEntry = pTcs->capabilityTable + i;
            if (pCapEntry->option_of_capability)
            {
                iTSCcapability.ParseTcsCapabilities(pCapEntry->capability, capabilityItems, userInputCapabilities, userInputCapability);
            }

        }
    }
    iTSCcapability.CreateNewCapability(capabilityItems);

    if (iTSC_324mObserver && userInputCapabilities)
    {
        iTSC_324mObserver->UserInputCapability(userInputCapabilities);
    }
}

