/* ------------------------------------------------------------------
 * 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.
 * -------------------------------------------------------------------
 */
/**
 * @file pv_comms_io_node.cpp
 * @brief
 */

#include "oscl_base.h"
#include "pv_comms_io_node_factory.h"
#include "pv_comms_io_node.h"
#include "pv_comms_io_node_port.h"
#include "oscl_dll.h"
#include "pvmf_basic_errorinfomessage.h"
#include "pv_comms_io_node_events.h"

// Define entry point for this DLL
OSCL_DLL_ENTRY_POINT_DEFAULT()

/**
//Macros for calling PVLogger
*/
#define LOGERROR(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iLogger,PVLOGMSG_ERR,m);
#define LOGINFOHI(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG,iLogger,PVLOGMSG_INFO,m);
#define LOGINFOMED(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG,iLogger,PVLOGMSG_INFO,m);
#define LOGINFOLOW(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iLogger,PVLOGMSG_INFO,m);
#define LOGINFO(m) LOGINFOMED(m)

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFNodeInterface* PVCommsIONodeFactory::Create(PvmiMIOControl* aMIOInputControl, PvmiMIOControl *aMIOOutputControl, bool logBitstream)
{
    return PVCommsIONode::Create(aMIOInputControl, aMIOOutputControl, logBitstream);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFNodeInterface* PVCommsIONodeFactory::Create(PvmiMIOControl *aMIOControl, bool logBitstream)
{
    return PVCommsIONode::Create(aMIOControl, logBitstream);
}


////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVCommsIONodeFactory::Delete(PVMFNodeInterface* aNode)
{
    PVCommsIONode::Release(aNode);
}

////////////////////////////////////////////////////////////////////////////
PVMFNodeInterface* PVCommsIONode::Create(PvmiMIOControl* aMIOInputControl, PvmiMIOControl *aMIOOutputControl, bool logBitstream)
{
    PVCommsIONode* node = OSCL_NEW(PVCommsIONode, (logBitstream));
    if (node)
    {
        OSCL_TRAPSTACK_PUSH(node);
        node->ConstructL(aMIOInputControl, aMIOOutputControl);
        OSCL_TRAPSTACK_POP();
    }
    return (PVMFNodeInterface*)node;
}

////////////////////////////////////////////////////////////////////////////
PVMFNodeInterface* PVCommsIONode::Create(PvmiMIOControl* aMIOControl, bool logBitstream)
{
    PVCommsIONode* node = OSCL_NEW(PVCommsIONode, (logBitstream));
    if (node)
    {
        OSCL_TRAPSTACK_PUSH(node);
        node->ConstructL(aMIOControl);
        OSCL_TRAPSTACK_POP();
    }
    return (PVMFNodeInterface*)node;
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::Release(PVMFNodeInterface* aNode)
{
    OSCL_DELETE(((PVCommsIONode*)aNode));
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVCommsIONode::ThreadLogon()
{
    if (iInterfaceState != EPVMFNodeCreated)
        return PVMFErrInvalidState;

    iLogger = PVLogger::GetLoggerObject("PVCommsIONode");

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::ThreadLogon"));

    if (!IsAdded())
        AddToScheduler();

    iMediaInputContext.ThreadLogon();
    if (!(iMediaOutputContext == iMediaInputContext))
        iMediaOutputContext.ThreadLogon();

    SetState(EPVMFNodeIdle);
    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVCommsIONode::ThreadLogoff()
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::ThreadLogoff"));

    if (iInterfaceState != EPVMFNodeIdle)
        return PVMFErrInvalidState;

    if (IsAdded())
        RemoveFromScheduler();

    iLogger = NULL;

    iMediaInputContext.ThreadLogoff();
    if (!(iMediaOutputContext == iMediaInputContext))
        iMediaOutputContext.ThreadLogoff();

    SetState(EPVMFNodeCreated);
    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFStatus PVCommsIONode::GetCapability(PVMFNodeCapability& aNodeCapability)
{
    if (iInterfaceState == EPVMFNodeIdle ||
            iInterfaceState == EPVMFNodeCreated)
        return PVMFFailure;

    if ((iMediaInputContext.isValid() && !iMediaInputContext.hasConfig()) ||
            (iMediaOutputContext.isValid() && !iMediaOutputContext.hasConfig()))
        return PVMFFailure;

    aNodeCapability.iCanSupportMultipleInputPorts = false;
    aNodeCapability.iCanSupportMultipleOutputPorts = false;
    aNodeCapability.iHasMaxNumberOfPorts = true;
    if (iMediaOutputContext.isValid() && iMediaInputContext.isValid())
        aNodeCapability.iMaxNumberOfPorts = 2;
    else
        aNodeCapability.iMaxNumberOfPorts = 1;

    PvmiKvp* kvp ;
    int numParams ;
    int32 err ;
    int32 i ;
    PVMFStatus status;

    // Get input formats capability from media IO
    kvp = NULL;
    numParams = 0;

    PvmiCapabilityAndConfig *inputConfig, *outputConfig;

    inputConfig = iMediaInputContext.iMediaIOConfig;
    outputConfig = iMediaOutputContext.iMediaIOConfig;

    if (inputConfig)
    {
        status = inputConfig->getParametersSync(NULL,
                                                OSCL_CONST_CAST(char*, INPUT_FORMATS_CAP_QUERY),
                                                kvp, numParams, NULL);
        if (status == PVMFSuccess)
        {
            OSCL_TRY(err,
                     for (i = 0; i < numParams; i++)
                     aNodeCapability.iInputFormatCapability.push_back(kvp[i].value.pChar_value););
            if (kvp)
                inputConfig->releaseParameters(0, kvp, numParams);
        }
        //else ignore errors.
    }
    // Get output formats capability from media IO
    kvp = NULL;
    numParams = 0;

    if (outputConfig)
    {
        status = outputConfig->getParametersSync(NULL,
                 OSCL_CONST_CAST(char*, OUTPUT_FORMATS_CAP_QUERY),
                 kvp, numParams, NULL);
        if (status == PVMFSuccess)
        {
            OSCL_TRY(err,
                     for (i = 0; i < numParams; i++)
                     aNodeCapability.iOutputFormatCapability.push_back(kvp[i].value.pChar_value);
                    );
            if (kvp)
                outputConfig->releaseParameters(0, kvp, numParams);
        }
        //else ignore errors.
    }

    if (aNodeCapability.iInputFormatCapability.empty() && aNodeCapability.iOutputFormatCapability.empty())
        return PVMFFailure;

    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFPortIter* PVCommsIONode::GetPorts(const PVMFPortFilter* aFilter)
{
    OSCL_UNUSED_ARG(aFilter);//port filter is not implemented.
    iPortVector.Reset();
    return &iPortVector;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::QueryInterface(PVMFSessionId s, const PVUuid& aUuid,
        PVInterface*& aInterfacePtr,
        const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::QueryInterface() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_QUERYINTERFACE, aUuid, aInterfacePtr, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::RequestPort(PVMFSessionId s, int32 aPortTag, const PvmfMimeString* aPortConfig, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::RequestPort() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_REQUESTPORT, aPortTag, aPortConfig, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::ReleasePort(PVMFSessionId s, PVMFPortInterface& aPort, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::ReleasePort() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_RELEASEPORT, aPort, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::Init(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::Init() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_INIT, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::Prepare(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::Prepare() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_PREPARE, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::Start(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::Start() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_START, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::Stop(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::Stop() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_STOP, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::Flush(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::Flush() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_FLUSH, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::Pause(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::Pause() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_PAUSE, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::Reset(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::Reset() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_RESET, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::CancelAllCommands(PVMFSessionId s, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::CancelAllCommands() called"));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_CANCELALLCOMMANDS, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF PVMFCommandId PVCommsIONode::CancelCommand(PVMFSessionId s, PVMFCommandId aCmdId, const OsclAny* aContext)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::CancelCommands() called cmdId=%d", aCmdId));
    PVCommsIONodeCmd cmd;
    cmd.PVCommsIONodeCmdBase::Construct(s, PVMF_GENERIC_NODE_CANCELCOMMAND, aCmdId, aContext);
    return QueueCommandL(cmd);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVCommsIONode::addRef()
{
    ++iExtensionRefCount;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVCommsIONode::removeRef()
{
    if (iExtensionRefCount > 0)
        --iExtensionRefCount;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF bool PVCommsIONode::queryInterface(const PVUuid& uuid, PVInterface*& iface)
{
    if (uuid == PvmfNodesSyncControlUuid)
    {
        PvmfNodesSyncControlInterface* myInterface = OSCL_STATIC_CAST(PvmfNodesSyncControlInterface*, this);
        iface = OSCL_STATIC_CAST(PVInterface*, myInterface);
        ++iExtensionRefCount;
    }
    else
    {
        iface = NULL;
        return false;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVCommsIONode::RequestCompleted(const PVMFCmdResp& aResponse)
//callback from the MIO module.
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::RequestCompleted: Cmd ID=%d", aResponse.GetCmdId()));

    //look for cancel completion.
    if (iMediaIOCancelPending
            && aResponse.GetCmdId() == iMediaIOCancelCmdId)
    {
        iMediaIOCancelPending = false;

        Assert(!iCancelCommand.empty());

        //Current cancel command is now complete.
        CommandComplete(iCancelCommand, iCancelCommand.front(), PVMFSuccess);
    }
    //look for non-cancel completion
    else if (iMediaIORequest != ENone
             && aResponse.GetCmdId() == iMediaIOCmdId)
    {
        // Kind of tricky here.  For each node request, ie Init()
        // up to 2 asynchronous MIO requests will be made.  We need
        // to intercept the first MIO response here and make sure to
        // send another one if necessary.  The actual node command
        // completion message won't be sent until we get the 2nd
        // MIO response.
        Assert(!iCurrentCommand.empty());
        PVCommsIONodeCmd& cmd = iCurrentCommand.front();

        switch (iMediaIORequest)
        {
            case EQueryCapability:
                if (aResponse.GetCmdStatus() != PVMFSuccess)
                {
                    cmd.iEventCode = PVCommsIONodeErr_MediaIOQueryCapConfigInterface;
                }
                else
                {
                    if ((cmd.iCmd == PVMF_GENERIC_NODE_INIT) && iTempCapConfigInterface)
                    {
                        cmd.iControlContext->iMediaIOConfig = OSCL_STATIC_CAST(PvmiCapabilityAndConfig *, iTempCapConfigInterface);
                        iTempCapConfigInterface = NULL;

                    }
                    else
                    {
                        LOGERROR((0, "PVCommsIONode:RequestComplete Error:Not setting interface iCmd=%d,iTempCapConfigInterface=%x",
                                  cmd.iCmd, iTempCapConfigInterface));


                    }
                    if (ReRunCommandForNextMIO(cmd))
                    {
                        return;
                    }
                    else    if (cmd.iControlContext == &iMediaInputContext &&
                                iMediaInputContext.iControl == iMediaOutputContext.iControl)
                    {
                        // if we have a single object that supports both input and
                        // output, the context's controls will be identical, and
                        // we don't need to query the interface again, so we'll just
                        // copy the session and config interface
                        // to the output context, since they will be the same
                        iMediaOutputContext = iMediaInputContext;
                    }
                }
                break;

            case EInit:
                if (aResponse.GetCmdStatus() != PVMFSuccess)
                {
                    cmd.iEventCode = PVCommsIONodeErr_MediaIOInit;
                }
                else if (ReRunCommandForNextMIO(cmd))
                {
                    return;
                }
                else
                {
                    iMediaIOState = MIO_STATE_INITIALIZED;
                }
                break;

            case EStart:
                if (aResponse.GetCmdStatus() != PVMFSuccess)
                {
                    cmd.iEventCode = PVCommsIONodeErr_MediaIOStart;
                }
                else if (ReRunCommandForNextMIO(cmd))
                {
                    return;
                }
                else
                {
                    iMediaIOState = MIO_STATE_STARTED;
                    //tell all the ports that I/O is started.
                    {
                        uint32 i;
                        for (i = 0; i < iPortVector.size(); i++)
                            iPortVector[i]->MediaIOStarted();
                    }
                }
                break;

            case EPause:
                if (aResponse.GetCmdStatus() != PVMFSuccess)
                {
                    cmd.iEventCode = PVCommsIONodeErr_MediaIOPause;
                }
                else if (ReRunCommandForNextMIO(cmd))
                {
                    return;
                }
                else
                {
                    iMediaIOState = MIO_STATE_PAUSED;
                }
                break;

            case EStop:
                if (aResponse.GetCmdStatus() != PVMFSuccess)
                {
                    cmd.iEventCode = PVCommsIONodeErr_MediaIOStop;
                }
                else if (ReRunCommandForNextMIO(cmd))
                {
                    return;
                }
                else
                {
                    iMediaIOState = MIO_STATE_INITIALIZED;
                }
                break;

            case EReset:
                if (aResponse.GetCmdStatus() != PVMFSuccess)
                {
                    cmd.iEventCode = PVCommsIONodeErr_MediaIOReset;
                }
                else if (ReRunCommandForNextMIO(cmd))
                {
                    return;
                }
                else
                {
                    iMediaIOState = MIO_STATE_IDLE;
                }
                break;

            default:
                Assert(false);
                break;
        }

        //current node command is now complete.
        if (IsFlushPending())
        {
            iMediaIORequest = ENone;
            FlushComplete();
        }
        else
        {
            CommandComplete(iCurrentCommand, iCurrentCommand.front(), aResponse.GetCmdStatus());
        }
    }
    else
    {
        //unexpected response.
        LOGERROR((0, "PVCommsIONode:RequestComplete Warning! Unexpected command ID %d"
                  , aResponse.GetCmdId()));
    }
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVCommsIONode::ReportErrorEvent(PVMFEventType aEventType, PVInterface* aExtMsg)
{
    OSCL_UNUSED_ARG(aEventType);
    OSCL_UNUSED_ARG(aExtMsg);
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVCommsIONode::ReportInfoEvent(PVMFEventType aEventType, PVInterface* aExtMsg)
{
    OSCL_UNUSED_ARG(aEventType);
    OSCL_UNUSED_ARG(aExtMsg);
}

////////////////////////////////////////////////////////////////////////////
PVCommsIONode::PVCommsIONode(bool logBitstream)
        : OsclActiveObject(OsclActiveObject::EPriorityNominal, "PVCommsIONode")
        , iMediaIOState(PVCommsIONode::MIO_STATE_IDLE)
        , iEventUuid(PVCommsIONodeEventTypesUUID)
        , iExtensionRefCount(0)
        , iLogger(NULL)
        , iLogBitstream(logBitstream)
        , iTempCapConfigInterface(NULL)
{
}

////////////////////////////////////////////////////////////////////////////
PVCommsIONode::~PVCommsIONode()
{
    Cancel();
    if (IsAdded())
        RemoveFromScheduler();

    //if any MIO commands are outstanding, there will be
    //a crash when they callback-- so panic here instead.
    OSCL_ASSERT(!(!iCancelCommand.empty()
                  || iMediaIORequest != ENone));

    //Cleanup allocated ports
    while (!iPortVector.empty())
        iPortVector.Erase(&iPortVector.front());

    //Cleanup commands
    //The command queues are self-deleting, but we want to
    //notify the observer of unprocessed commands.
    while (!iCurrentCommand.empty())
    {
        CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFFailure);
        iCurrentCommand.Erase(&iCurrentCommand.front());
    }
    while (!iInputCommands.empty())
    {
        CommandComplete(iInputCommands, iInputCommands.front(), PVMFFailure);
        iInputCommands.Erase(&iInputCommands.front());
    }
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::ConstructL(PvmiMIOControl* aMIOInputControl, PvmiMIOControl *aMIOOutputControl)
{
    if (((aMIOInputControl == NULL) && (aMIOOutputControl == NULL)) ||
            aMIOInputControl == aMIOOutputControl)
        OsclError::Leave(OsclErrArgument);
    iLogger = NULL;
    iMediaInputContext.iControl = aMIOInputControl;
    iMediaOutputContext.iControl = aMIOOutputControl;
    iPortActivity = 0;
    iInputCommands.Construct(1, 10);//reserve 10
    iCurrentCommand.Construct(1, 1);//reserve 1.
    iCancelCommand.Construct(1, 1);//reserve 1.
    iPortVector.Construct(0);//reserve zero.
    iMediaIORequest = ENone;
    iMediaIOCancelPending = false;
}

void PVCommsIONode::ConstructL(PvmiMIOControl* aMIOControl)
{
    if (aMIOControl == NULL)
        OsclError::Leave(OsclErrArgument);

    iLogger = NULL;
    iMediaInputContext.iControl = aMIOControl;
    iMediaOutputContext.iControl = aMIOControl;

    iPortActivity = 0;
    iInputCommands.Construct(1, 10);//reserve 10
    iCurrentCommand.Construct(1, 1);//reserve 1.
    iCancelCommand.Construct(1, 1);//reserve 1.
    iPortVector.Construct(0);//reserve zero.
    iMediaIORequest = ENone;
    iMediaIOCancelPending = false;
}


////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::Run()
{
    //Process async node commands.
    if (!iInputCommands.empty())
    {
        ProcessCommand();
    }
}

////////////////////////////////////////////////////////////////////////////
PVMFCommandId PVCommsIONode::QueueCommandL(PVCommsIONodeCmd& aCmd)
{
    PVMFCommandId id;

    id = iInputCommands.AddL(aCmd);

    //wakeup the AO
    RunIfNotReady();

    return id;
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::ProcessCommand()
{
    //Can't do anything when an asynchronous cancel is in progress-- just
    //need to wait on completion.
    if (!iCancelCommand.empty())
        return ; //keep waiting.

    //If a command is in progress, only hi pri cancel commands can interrupt it.
    if (!iCurrentCommand.empty()
            && !iInputCommands.front().hipri())
    {
        return ; //keep waiting
    }

    //The newest or highest pri command is in the front of the queue.
    Assert(!iInputCommands.empty());
    PVCommsIONodeCmd& aCmd = iInputCommands.front();

    PVMFStatus cmdstatus;
    OsclAny* aEventData = NULL;
    if (aCmd.hipri())
    {
        //save input command in cancel command
        int32 err;
        OSCL_TRY(err, iCancelCommand.StoreL(aCmd););
        if (err != OsclErrNone)
        {
            cmdstatus = PVMFErrNoMemory;
        }
        else
        {
            //Process the interrupt commands.
            switch (aCmd.iCmd)
            {
                case PVMF_GENERIC_NODE_CANCELALLCOMMANDS:
                    cmdstatus = DoCancelAllCommands(aCmd);
                    break;

                case PVMF_GENERIC_NODE_CANCELCOMMAND:
                    cmdstatus = DoCancelCommand(aCmd);
                    break;

                default:
                    Assert(false);
                    cmdstatus = PVMFFailure;
                    break;
            }
        }

        //erase the input command.
        if (cmdstatus != PVMFPending)
        {
            //Request already failed-- erase from Cancel Command.
            //Node command remains in Input Commands.
            iCancelCommand.Erase(&iCurrentCommand.front());
        }
        else
        {
            //Node command is now stored in Cancel Command, so erase from Input Commands.
            iInputCommands.Erase(&aCmd);
        }
    }
    else
    {
        //save input command in current command
        int32 err;
        OSCL_TRY(err, iCurrentCommand.StoreL(aCmd););
        if (err != OsclErrNone)
        {
            cmdstatus = PVMFErrNoMemory;
        }
        else
        {
            //Process the normal pri commands.
            switch (aCmd.iCmd)
            {
                case PVMF_GENERIC_NODE_QUERYINTERFACE:
                    cmdstatus = DoQueryInterface(aCmd);
                    break;

                case PVMF_GENERIC_NODE_REQUESTPORT:
                    cmdstatus = DoRequestPort(aCmd, aEventData);
                    break;

                case PVMF_GENERIC_NODE_RELEASEPORT:
                    cmdstatus = DoReleasePort(aCmd);
                    break;

                case PVMF_GENERIC_NODE_INIT:
                    cmdstatus = DoInit(aCmd);
                    break;

                case PVMF_GENERIC_NODE_PREPARE:
                    cmdstatus = DoPrepare(aCmd);
                    break;

                case PVMF_GENERIC_NODE_START:
                    cmdstatus = DoStart(aCmd);
                    break;

                case PVMF_GENERIC_NODE_STOP:
                    cmdstatus = DoStop(aCmd);
                    break;

                case PVMF_GENERIC_NODE_FLUSH:
                    cmdstatus = DoFlush(aCmd);
                    break;

                case PVMF_GENERIC_NODE_PAUSE:
                    cmdstatus = DoPause(aCmd);
                    break;

                case PVMF_GENERIC_NODE_RESET:
                    cmdstatus = DoReset(aCmd);
                    break;

                default://unknown command type
                    Assert(false);
                    cmdstatus = PVMFFailure;
                    break;
            }
        }

        //erase the input command.
        if (cmdstatus != PVMFPending)
        {
            //Request already failed/succeeded-- erase from Current Command.
            //Node command remains in Input Commands.
            iCurrentCommand.Erase(&iCurrentCommand.front());
        }
        else
        {
            //Node command is now stored in Current Command, so erase from Input Commands.
            //The DoXXX() routines will modify the command object, so we
            //need to update the command object in the current command queue
            iCurrentCommand[iCurrentCommand.size()-1] = aCmd;
            iInputCommands.Erase(&aCmd);
        }
    }

    if (cmdstatus != PVMFPending)
        CommandComplete(iInputCommands, aCmd, cmdstatus, aEventData);

}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::CommandComplete(PVCommsIONodeCmdQ& aCmdQ, PVCommsIONodeCmd& aCmd, PVMFStatus aStatus, OsclAny*aEventData)
{
    if (aStatus == PVMFSuccess)
    {
        LOGINFO((0, "PVCommsIONode:CommandComplete Id %d Cmd %d Status %d Context %d EVData %d EVCode %d"
                 , aCmd.iId, aCmd.iCmd, aStatus, aCmd.iContext, aEventData, aCmd.iEventCode));
    }
    else
    {
        LOGERROR((0, "PVCommsIONode:CommandComplete Error! Id %d Cmd %d Status %d Context %d EVData %d EVCode %d"
                  , aCmd.iId, aCmd.iCmd, aStatus, aCmd.iContext, aEventData, aCmd.iEventCode));
    }

    //do state transitions and any final command completion.
    if (aStatus == PVMFSuccess)
    {
        switch (aCmd.iCmd)
        {
            case PVMF_GENERIC_NODE_INIT:
                SetState(EPVMFNodeInitialized);
                break;
            case PVMF_GENERIC_NODE_PREPARE:
                SetState(EPVMFNodePrepared);
                break;
            case PVMF_GENERIC_NODE_START:
                SetState(EPVMFNodeStarted);
                break;
            case PVMF_GENERIC_NODE_PAUSE:
                SetState(EPVMFNodePaused);
                break;
            case PVMF_GENERIC_NODE_STOP:
                SetState(EPVMFNodePrepared);
                //clear the message queues of any unprocessed data now.
                {
                    uint32 i;
                    for (i = 0; i < iPortVector.size(); i++)
                        iPortVector[i]->ClearMsgQueues();
                }
                break;
            case PVMF_GENERIC_NODE_FLUSH:
                SetState(EPVMFNodePrepared);
                //Stop the ports
                {
                    for (uint32 i = 0; i < iPortVector.size(); i++)
                        iPortVector[i]->Stop();
                }
                break;
            default:
                break;
        }
    }

    //Reset the media I/O request
    iMediaIORequest = ENone;


    //The "startmio" command originated from port activity,
    //not from the node controller, so no command complete event
    //is needed.
    if (aCmd.iCmd == PVMF_COMMSIONODE_STARTMIO)
    {
        //Erase the command from the queue.
        aCmdQ.Erase(&aCmd);
    }
    else
    {
        //Extract parameters needed for command response.
        PVMFCommandId cmdId = aCmd.iId;
        const OsclAny* cmdContext = aCmd.iContext;
        PVMFSessionId cmdSess = aCmd.iSession;
        PVMFStatus eventCode = aCmd.iEventCode;

        //Erase the command from the queue.
        aCmdQ.Erase(&aCmd);

        if (eventCode != PVCommsIONodeErr_First)
        {
            //create extended response.

            PVMFBasicErrorInfoMessage*eventmsg = OSCL_NEW(PVMFBasicErrorInfoMessage, (eventCode, iEventUuid, NULL));

            PVMFCmdResp resp(cmdId
                             , cmdContext
                             , aStatus
                             , OSCL_STATIC_CAST(PVInterface*, eventmsg)
                             , aEventData);

            //report to the session observers.
            PVMFNodeInterface::ReportCmdCompleteEvent(cmdSess, resp);

            //remove the ref to the extended response
            if (eventmsg)
                eventmsg->removeRef();
        }
        else
        {
            //create basic response
            PVMFCmdResp resp(cmdId, cmdContext, aStatus, aEventData);

            //report to the session observers.
            PVMFNodeInterface::ReportCmdCompleteEvent(cmdSess, resp);
        }
    }
    //re-schedule if there are more commands and node isn't logged off
    if (!iInputCommands.empty()
            && IsAdded())
        RunIfNotReady();
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoQueryInterface(PVCommsIONodeCmd& aCmd)
{
    PVUuid* uuid;
    PVInterface** ptr;
    aCmd.PVCommsIONodeCmdBase::Parse(uuid, ptr);
    if (uuid && ptr)
    {
        if (queryInterface(*uuid, *ptr))
            return PVMFSuccess;
    }
    return PVMFFailure;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoRequestPort(PVCommsIONodeCmd& aCmd, OsclAny*&aEventData)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::DoRequestPort"));
    //This node supports port request from any state

    //retrieve port tag & mimetype
    int32 tag;
    OSCL_String* mimetype;
    aCmd.PVCommsIONodeCmdBase::Parse(tag, mimetype);

    switch (tag)
    {

        case PVMF_COMMSIO_NODE_OUTPUT_PORT_TAG:
        case PVMF_COMMSIO_NODE_INPUT_PORT_TAG:
        case PVMF_COMMSIO_NODE_IO_PORT_TAG:
        {
            //Allocate a new port
            OsclAny *ptr = NULL;
            int32 err;
            if (!ValidatePortTagRequest(tag))
                return PVMFErrNotSupported;

            OSCL_TRY(err, ptr = iPortVector.Allocate(););
            if (err != OsclErrNone || !ptr)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "PVCommsIONode::DoRequestPort: Error - iPortVector Out of memory"));
                return PVMFErrNoMemory;
            }
            PVCommsIONodePort *port = OSCL_PLACEMENT_NEW(ptr, PVCommsIONodePort(tag, this));

            //set the format from the mimestring.
            if (mimetype)
            {
                PVMFFormatType fmt = mimetype->get_str();
                if (fmt != PVMF_MIME_FORMAT_UNKNOWN)
                    port->Configure(fmt);
            }

            //Add the port to the port vector.
            OSCL_TRY(err, iPortVector.AddL(port););
            if (err != OsclErrNone)
            {
                iPortVector.DestructAndDealloc(port);
                return PVMFErrNoMemory;
            }
            aEventData = (OsclAny*)port;

            //cast the port to the generic interface before returning.
            PVMFPortInterface*retval = port;
            aEventData = (OsclAny*)retval;
            return PVMFSuccess;
        }
        break;

        default:
        {
            //bad port tag
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                            (0, "PVCommsIONode::DoRequestPort: Error - Invalid port tag"));
            return PVMFFailure;
        }
        break;
    }
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoReleasePort(PVCommsIONodeCmd& aCmd)
{
    //This node supports release port from any state

    PVMFPortInterface* p;
    aCmd.PVCommsIONodeCmdBase::Parse(p);
    //search the output port vector
    {
        PVCommsIONodePort* port = (PVCommsIONodePort*)p;
        PVCommsIONodePort** portPtr = iPortVector.FindByValue(port);
        if (portPtr)
        {
            (*portPtr)->Disconnect();
            iPortVector.Erase(portPtr);
            return PVMFSuccess;
        }
    }
    aCmd.iEventCode = PVCommsIONodeErr_PortNotExist;
    return PVMFFailure;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoInit(PVCommsIONodeCmd& aCmd)
{
    OSCL_UNUSED_ARG(aCmd);
    bool haveNewContext;

    if (iInterfaceState != EPVMFNodeIdle)
        return PVMFErrInvalidState;

    if (!(haveNewContext = GetNextContextInSequence(aCmd)) && aCmd.iControlContext == NULL)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                        (0, "PVCommsIONode::DoInit: Error - All media controls are NULL"));
        aCmd.iEventCode = PVCommsIONodeErr_MediaIONotExist;
        return PVMFFailure;
    }
    else if (aCmd.iControlContext->iControl->connect(aCmd.iControlContext->iMediaSession, this) != PVMFSuccess)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                        (0, "PVCommsIONode::DoInit: Error - iMediaIOControl->connect failed"));
        aCmd.iEventCode = PVCommsIONodeErr_MediaIOConnect;
        return PVMFFailure;
    }

    //Query for MIO capability and config interface
    return SendMioRequest(aCmd, EQueryCapability);
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoPrepare(PVCommsIONodeCmd& aCmd)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::DoPrepare"));

    if (iInterfaceState != EPVMFNodeInitialized)
        return PVMFErrInvalidState;

    if (aCmd.iControlContext == NULL)
        GetNextContextInSequence(aCmd);
    return SendMioRequest(aCmd, EInit);
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoStart(PVCommsIONodeCmd& aCmd)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::DoStart"));

    if (iInterfaceState != EPVMFNodePrepared
            && iInterfaceState != EPVMFNodePaused)
        return PVMFErrInvalidState;

    //Start the MIO
    if (aCmd.iControlContext == NULL)
        GetNextContextInSequence(aCmd);

    PVMFStatus status = SendMioRequest(aCmd, EStart);

    if (status != PVMFPending)
        return status;

    // don't start ports until last MIO start has been sent
    PVCommsIONodeCmd tempCmd = aCmd;
    if (!GetNextContextInSequence(tempCmd))
    {
        uint32 i = 0;
        for (i = 0; i < iPortVector.size(); i++)
            iPortVector[i]->Start();
    }

    return status;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoStop(PVCommsIONodeCmd& aCmd)
{
    if (iInterfaceState != EPVMFNodeStarted
            && iInterfaceState != EPVMFNodePaused)
        return PVMFErrInvalidState;

    //Stop the MIO
    if (aCmd.iControlContext == NULL)
        GetNextContextInSequence(aCmd);

    PVMFStatus status = SendMioRequest(aCmd, EStop);

    //Stop the ports
    for (uint32 i = 0; i < iPortVector.size(); i++)
        iPortVector[i]->Stop();

    return status;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoFlush(PVCommsIONodeCmd& aCmd)
{
    if (iInterfaceState != EPVMFNodeStarted
            && iInterfaceState != EPVMFNodePaused)
        return PVMFErrInvalidState;

    //Disable the input.
    if (iPortVector.size() > 0)
    {
        //Stop the MIO input
        if (aCmd.iControlContext == NULL)
            GetNextContextInSequence(aCmd);

        SendMioRequest(aCmd, EStop);
        //ignore returned error.
    }
    //wait for all data to be consumed
    return PVMFPending;
}

////////////////////////////////////////////////////////////////////////////
bool PVCommsIONode::IsFlushPending()
{
    return (iCurrentCommand.size() > 0
            && iCurrentCommand.front().iCmd == PVMF_GENERIC_NODE_FLUSH);
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::FlushComplete()
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVMFVideoEncNode::FlushComplete"));

    if (iMediaIORequest != ENone)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
                        (0, "PVMFVideoEncNode::FlushComplete: Media IO request is pending"));
        return;
    }

    if (!PortQueuesEmpty())
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
                        (0, "PVMFVideoEncNode::FlushComplete: Port queues are not empty"));
        return;
    }

    //Flush is complete.
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
                    (0, "PVMFVideoEncNode::FlushComplete: Done"));
    CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFSuccess);
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoPause(PVCommsIONodeCmd& aCmd)
{
    if (iInterfaceState != EPVMFNodeStarted)
        return PVMFErrInvalidState;

    if (aCmd.iControlContext == NULL)
        GetNextContextInSequence(aCmd);

    return SendMioRequest(aCmd, EPause);
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoReset(PVCommsIONodeCmd& aCmd)
{
    if (aCmd.iControlContext == NULL)
        GetNextContextInSequence(aCmd);

    PVMFStatus status = SendMioRequest(aCmd, EReset);

    switch (iInterfaceState)
    {
        case EPVMFNodeCreated:
        case EPVMFNodeIdle:
        case EPVMFNodeInitialized:
        case EPVMFNodePrepared:
        {
            //delete all ports and notify observer.
            while (!iPortVector.empty())
                iPortVector.Erase(&iPortVector.front());

            //restore original port vector reserve.
            iPortVector.Reconstruct();

            //logoff & go back to Created state.
            SetState(EPVMFNodeIdle);
        }
        break;

        default:
            status = PVMFErrInvalidState;
            break;
    }
    return status;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoCancelAllCommands(PVCommsIONodeCmd& aCmd)
{
    //first cancel any MIO commmand in progress.
    if (iMediaIORequest != ENone)
    {
        return CancelMioRequest(aCmd);
    }

    //first cancel the current command if any
    {
        while (!iCurrentCommand.empty())
            CommandComplete(iCurrentCommand, iCurrentCommand[0], PVMFErrCancelled);
    }

    //next cancel all queued commands
    {
        //start at element 1 since this cancel command is element 0.
        while (iInputCommands.size() > 1)
            CommandComplete(iInputCommands, iInputCommands[1], PVMFErrCancelled);
    }

    //finally, report cancel complete.
    return PVMFSuccess;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::DoCancelCommand(PVCommsIONodeCmd& aCmd)
{
    //tbd, need to cancel the MIO

    //extract the command ID from the parameters.
    PVMFCommandId id;
    aCmd.PVCommsIONodeCmdBase::Parse(id);

    //first check "current" command if any
    {
        PVCommsIONodeCmd* cmd = iCurrentCommand.FindById(id);
        if (cmd)
        {
            //first cancel any MIO commmand in progress.
            if (iMediaIORequest != ENone)
            {
                return CancelMioRequest(aCmd);
            }
            else
            {
                //cancel the queued command
                CommandComplete(iCurrentCommand, *cmd, PVMFErrCancelled);
                //report cancel success
                return PVMFSuccess;
            }
        }
    }

    //next check input queue.
    {
        //start at element 1 since this cancel command is element 0.
        PVCommsIONodeCmd* cmd = iInputCommands.FindById(id, 1);
        if (cmd)
        {
            //cancel the queued command
            CommandComplete(iInputCommands, *cmd, PVMFErrCancelled);
            //report cancel success
            return PVMFSuccess;
        }
    }
    //if we get here the command isn't queued so the cancel fails.
    aCmd.iEventCode = PVCommsIONodeErr_CmdNotQueued;
    return PVMFFailure;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::SendMioRequest(PVCommsIONodeCmd& aCmd, EMioRequest aRequest)
{
    //Make an asynchronous request to MIO component
    //and save the request in iMediaIORequest.


    //there should not be a MIO command in progress..
    Assert(iMediaIORequest == ENone);

    //save media io request.
    iMediaIORequest = aRequest;

    PVMFStatus status;

    //Issue the command to the MIO.
    switch (aRequest)
    {
        case EQueryCapability:
        {
            int32 err ;

            OSCL_TRY(err,
                     iMediaIOCmdId = aCmd.iControlContext->iControl->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID,
                                     iTempCapConfigInterface , NULL);
                    );
            if (err != OsclErrNone)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "PVCommsIONode::SendMioRequest: Error - iMediaIOControl->QueryInterface failed"));
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOQueryCapConfigInterface;
                status = PVMFFailure;
            }
            else
            {
                status = PVMFPending;
            }
        }
        break;

        case EInit:
        {
            if (iMediaIOState != MIO_STATE_IDLE)
            {
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOWrongState;
                status = PVMFFailure;
                break;
            }

            int32 err ;
            OSCL_TRY(err, iMediaIOCmdId = aCmd.iControlContext->iControl->Init(););

            if (err != OsclErrNone)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "PVCommsIONode::SendMioRequest: Error - iMediaIOControl->Init failed"));
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOInit;
                status = PVMFFailure;
            }
            else
            {
                status = PVMFPending;
            }
        }
        break;

        case EStart:
        {
            if (iMediaIOState != MIO_STATE_INITIALIZED
                    && iMediaIOState != MIO_STATE_PAUSED)
            {
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOWrongState;
                status = PVMFFailure;
                break;
            }

            int32 err ;
            OSCL_TRY(err, iMediaIOCmdId = aCmd.iControlContext->iControl->Start(););

            if (err != OsclErrNone)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "PVCommsIONode::SendMioRequest: Error - iMediaIOControl->Start failed"));
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOStart;
                status = PVMFFailure;
            }
            else
            {
                status = PVMFPending;
            }
        }
        break;

        case EPause:
        {
            if (iMediaIOState != MIO_STATE_STARTED)
            {
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOWrongState;
                status = PVMFFailure;
                break;
            }

            int32 err ;
            OSCL_TRY(err, iMediaIOCmdId = aCmd.iControlContext->iControl->Pause(););

            if (err != OsclErrNone)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "PVCommsIONode::SendMioRequest: Error - iMediaIOControl->Pause failed"));
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOPause;
                status = PVMFFailure;
            }
            else
            {
                status = PVMFPending;
            }
        }
        break;

        case EStop:
        {
            if (iMediaIOState != MIO_STATE_STARTED
                    && iMediaIOState != MIO_STATE_PAUSED)
            {
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOWrongState;
                status = PVMFFailure;
                break;
            }

            int32 err ;
            OSCL_TRY(err, iMediaIOCmdId = aCmd.iControlContext->iControl->Stop(););

            if (err != OsclErrNone)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "PVCommsIONode::SendMioRequest: Error - iMediaIOControl->Stop failed"));
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOStop;
                status = PVMFFailure;
            }
            else
            {
                status = PVMFPending;
            }
        }
        break;

        case EReset:
        {
            if (iMediaIOState != MIO_STATE_PAUSED && iMediaIOState != MIO_STATE_INITIALIZED)
            {
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOWrongState;
                status = PVMFFailure;
                break;
            }

            int32 err ;
            OSCL_TRY(err, iMediaIOCmdId = aCmd.iControlContext->iControl->Reset(););

            if (err != OsclErrNone)
            {
                PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR,
                                (0, "PVCommsIONode::SendMioRequest: Error - iMediaIOControl->Reset failed"));
                aCmd.iEventCode = PVCommsIONodeErr_MediaIOReset;
                status = PVMFFailure;
            }
            else
            {
                status = PVMFPending;
            }
        }
        break;
        default:
            Assert(false);//unrecognized command.
            status = PVMFFailure;
            break;
    }

    if (status == PVMFPending)
    {
        LOGINFOHI((0, "PVCommsIONode:SendMIORequest: Command Issued to MIO component, waiting on response..."));
    }

    return status;
}

////////////////////////////////////////////////////////////////////////////
PVMFStatus PVCommsIONode::CancelMioRequest(PVCommsIONodeCmd& aCmd)
{
    Assert(iMediaIORequest != ENone);

    Assert(!iMediaIOCancelPending);

    //Issue the cancel to the MIO.
    iMediaIOCancelPending = true;
    int32 err;
    OSCL_TRY(err, iMediaIOCancelCmdId = aCmd.iControlContext->iControl->CancelCommand(iMediaIOCmdId););
    if (err != OsclErrNone)
    {
        aCmd.iEventCode = PVCommsIONodeErr_MediaIOCancelCommand;
        iMediaIOCancelPending = false;
        return PVMFFailure;
    }
    LOGINFOHI((0, "PVCommsIONode:SendMIORequest: Cancel Command Issued to MIO component, waiting on response..."));
    return PVMFPending;//wait on request to cancel.
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::MioConfigured(int32 aPortTag)
//called by the port when the MIO was just configured.
//only called for ports that need to wait on config before starting
//the MIO.
//RH Note: probably not need for Comms right now, and the port
//does not call this method.
{
    OSCL_UNUSED_ARG(aPortTag);

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                    (0, "PVCommsIONode::MioConfigured() called"));
    if (iInterfaceState == EPVMFNodeStarted)
    {
        PVCommsIONodeCmd cmd;
//      cmd.iControlContext = ContextFromTag(aPortTag);
        cmd.PVCommsIONodeCmdBase::Construct(0, PVMF_COMMSIONODE_STARTMIO, NULL);
        QueueCommandL(cmd);
    }
}

////////////////////////////////////////////////////////////////////////////
//                  Event reporting routines.
////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::SetState(TPVMFNodeInterfaceState s)
{
    LOGINFO((0, "PVCommsIONode:SetState %d", s));
    PVMFNodeInterface::SetState(s);
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::ReportErrorEvent(PVMFEventType aEventType, OsclAny* aEventData, PVMFStatus aEventCode)
{
    LOGERROR((0, "PVCommsIONode:NodeErrorEvent Type %d EVData %d EVCode %d"
              , aEventType, aEventData, aEventCode));

    //create the extension message if any.
    if (aEventCode != PVCommsIONodeErr_First)
    {
        PVMFBasicErrorInfoMessage* eventmsg = OSCL_NEW(PVMFBasicErrorInfoMessage, (aEventCode, iEventUuid, NULL));
        PVMFAsyncEvent asyncevent(PVMFErrorEvent, aEventType, NULL, OSCL_STATIC_CAST(PVInterface*, eventmsg), aEventData, NULL, 0);
        PVMFNodeInterface::ReportErrorEvent(asyncevent);
        eventmsg->removeRef();
    }
    else
        PVMFNodeInterface::ReportErrorEvent(aEventType, aEventData);
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::ReportInfoEvent(PVMFEventType aEventType, OsclAny* aEventData, PVMFStatus aEventCode)
{
    LOGINFO((0, "PVCommsIONode:NodeInfoEvent Type %d EVData %d EVCode %d"
             , aEventType, aEventData, aEventCode));

    //create the extension message if any.
    if (aEventCode != PVCommsIONodeErr_First)
    {
        PVMFBasicErrorInfoMessage* eventmsg = OSCL_NEW(PVMFBasicErrorInfoMessage, (aEventCode, iEventUuid, NULL));
        PVMFAsyncEvent asyncevent(PVMFErrorEvent, aEventType, NULL, OSCL_STATIC_CAST(PVInterface*, eventmsg), aEventData, NULL, 0);
        PVMFNodeInterface::ReportInfoEvent(asyncevent);
        eventmsg->removeRef();
    }
    else
        PVMFNodeInterface::ReportInfoEvent(aEventType, aEventData);
}

////////////////////////////////////////////////////////////////////////////
bool PVCommsIONode::PortQueuesEmpty()
{
    uint32 i;
    for (i = 0; i < iPortVector.size(); i++)
    {
        if (iPortVector[i]->IncomingMsgQueueSize() > 0
                || iPortVector[i]->OutgoingMsgQueueSize() > 0)
        {
            return false;
        }
    }
    return true;
}

////////////////////////////////////////////////////////////////////////////
void PVCommsIONode::Assert(bool condition)
{
    if (!condition)
    {
        LOGERROR((0, "PVCommsIONode::Assert Failed!"));
        OSCL_ASSERT(0);
    }
}

MIOControlContextSet PVCommsIONode::ContextSetFromTag(int32 aTag)
{
    MIOControlContextSet contextSet;

    switch (aTag)
    {
        case PVMF_COMMSIO_NODE_OUTPUT_PORT_TAG:
            if (iMediaInputContext.isValid())
                contextSet.iMediaInputElement = &iMediaInputContext;
            break;
        case PVMF_COMMSIO_NODE_INPUT_PORT_TAG:
            if (iMediaOutputContext.isValid())
                contextSet.iMediaOutputElement = &iMediaOutputContext;
            break;
        case PVMF_COMMSIO_NODE_IO_PORT_TAG:
            if (iMediaOutputContext.isValid() && iMediaInputContext.isValid())
            {
                contextSet.iMediaInputElement = &iMediaInputContext;
                contextSet.iMediaOutputElement = &iMediaOutputContext;
            }
            break;
        default:
            break;
    }
    return contextSet;
}
/*
PvmiCapabilityAndConfig *PVCommsIONode::GetConfig(int32 aPortTag)
{
    MIOControlContextElement *context = ContextSetFromTag(aPortTag);
    if (context)
        return context->iMediaIOConfig;
    return NULL;
}
*/
bool PVCommsIONode::CreateMediaTransfer(int32 aPortTag, PvmiMediaTransfer *&aInputTransfer, PvmiMediaTransfer *&aOutputTransfer)
{
    MIOControlContextSet set = ContextSetFromTag(aPortTag);

    if (set.isEmpty())
        return false;

    if (set.iMediaInputElement)
    {
        aInputTransfer = set.iMediaInputElement->iControl->createMediaTransfer(set.iMediaInputElement->iMediaSession);
        if (!aInputTransfer)
            return false;
    }
    if (set.iMediaOutputElement)
    {
        aOutputTransfer = set.iMediaOutputElement->iControl->createMediaTransfer(set.iMediaOutputElement->iMediaSession);
        if (!aOutputTransfer)
            return false;
    }

    return true;
}

void PVCommsIONode::DeleteMediaTransfer(int32 aPortTag, PvmiMediaTransfer *aInputTransfer, PvmiMediaTransfer *aOutputTransfer)
{
    MIOControlContextSet set = ContextSetFromTag(aPortTag);

    if (set.isEmpty())
        return;

    if (set.iMediaInputElement && aInputTransfer)
    {
        set.iMediaInputElement->iControl->deleteMediaTransfer(set.iMediaInputElement->iMediaSession, aInputTransfer);
    }
    else if (set.iMediaOutputElement && aOutputTransfer)
    {
        set.iMediaOutputElement->iControl->deleteMediaTransfer(set.iMediaOutputElement->iMediaSession, aOutputTransfer);
    }
}

bool PVCommsIONode::GetNextContextInSequence(PVCommsIONodeCmd &aCmd)
{
    // We need to determine which IO controls to start
    // the init with
    if (aCmd.iControlContext == NULL)
    {
        if (iMediaInputContext.isValid())
            aCmd.iControlContext = &iMediaInputContext;
        else if (iMediaOutputContext.isValid())
            aCmd.iControlContext = &iMediaOutputContext;
        else
            return false;
    }
    else if ((aCmd.iControlContext == &iMediaInputContext) &&
             iMediaOutputContext.isValid() &&
             (iMediaOutputContext.iControl != iMediaInputContext.iControl))
    {
        aCmd.iControlContext = &iMediaOutputContext;
    }
    else
        return false;

    return true;
}

bool PVCommsIONode::ReRunCommandForNextMIO(PVCommsIONodeCmd &aCmd)
{
    // This method should only be called from the
    // RequestCompleted method, and allows us to re-use
    // command objects to asynchronously initialize more than
    // one MIO component under the context of a single node
    // command id.

    // The force parameter will force the command to be re run for
    // an output context even if its MIO component is a reference to
    // the input context component.  This is useful for the query interface
    // portion of initialization.

    PVCommsIONodeCmd newCmd = aCmd;

    if (GetNextContextInSequence(newCmd))
    {
        iCurrentCommand.Erase(&aCmd);
        // Don't use AddL because that overwrites the iId
        // which must remain the same.
        iInputCommands.StoreL(newCmd);
        RunIfNotReady();
        iMediaIORequest = ENone;
        return true;
    }
    return false;
}

bool PVCommsIONode::MIOSupportsPortTag(int32 aTag, MIOControlContextSet &aSet)
{
    PvmiKvp* kvp ;
    int numParams ;

    // Get input formats capability from media IO
    kvp = NULL;
    numParams = 0;

    if (aTag & PVMF_COMMSIO_NODE_OUTPUT_PORT_TAG)
    {
        if (!aSet.iMediaInputElement->hasConfig() ||
                aSet.iMediaInputElement->iMediaIOConfig->getParametersSync(NULL,
                        OSCL_CONST_CAST(char*, OUTPUT_FORMATS_CAP_QUERY),
                        kvp, numParams, NULL) != PVMFSuccess)
            return false;
        aSet.iMediaInputElement->iMediaIOConfig->releaseParameters(0, kvp, numParams);
        if (numParams == 0)
            return false;
    }
    if (aTag & PVMF_COMMSIO_NODE_INPUT_PORT_TAG)
    {
        if (!aSet.iMediaOutputElement->hasConfig() ||
                aSet.iMediaOutputElement->iMediaIOConfig->getParametersSync(NULL,
                        OSCL_CONST_CAST(char*, INPUT_FORMATS_CAP_QUERY),
                        kvp, numParams, NULL) != PVMFSuccess)
            return false;
        aSet.iMediaOutputElement->iMediaIOConfig->releaseParameters(0, kvp, numParams);
        if (numParams == 0)
            return false;
    }

    return true;
}

// Check some stuff to determine if we can hand out
// a port of this type...

bool PVCommsIONode::ValidatePortTagRequest(int32 aTag)
{
    uint32 curPorts = iPortVector.size();

    switch (aTag)
    {
        case PVMF_COMMSIO_NODE_IO_PORT_TAG:
            if (curPorts > 0)
                return false; // can have only one I/O port
            break;

        case PVMF_COMMSIO_NODE_OUTPUT_PORT_TAG:
            if (curPorts > 1 ||
                    (curPorts == 1 && iPortVector[0]->GetPortTag() != PVMF_COMMSIO_NODE_INPUT_PORT_TAG))
                return false;
            break;
        case PVMF_COMMSIO_NODE_INPUT_PORT_TAG:
            if (curPorts > 1 ||
                    (curPorts == 1 && iPortVector[0]->GetPortTag() != PVMF_COMMSIO_NODE_OUTPUT_PORT_TAG))
                return false;
            break;
        default:
            return false;
    }

    MIOControlContextSet set = ContextSetFromTag(aTag);

    if (set.isEmpty() || !MIOSupportsPortTag(aTag, set))
        return false;

    return true;
}
