/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 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.
 * -------------------------------------------------------------------
 */
#ifndef PV_OMXDEFS_H_INCLUDED
#include "pv_omxdefs.h"
#endif

#ifndef PV_OMX_INTERFACE_PROXY_H_INCLUDED
#include "pv_omx_interface_proxy.h"
#endif

#ifndef PV_OMX_INTERFACE_PROXY_HANDLER_H_INCLUDED
#include "pv_omx_interface_proxy_handler.h"
#endif

#ifndef PV_OMX_INTERFACE_PROXY_NOTIFIER_H_INCLUDED
#include "pv_omx_interface_proxy_notifier.h"
#endif

#ifndef OSCL_MEM_H_INCLUDED
#include "oscl_mem.h"
#endif

#ifndef OSCL_DLL_H_INCLUDED
#include "oscl_dll.h"
#endif

#ifndef PVLOGGER_H_INCLUDED
#include "pvlogger.h"
#endif

OSCL_DLL_ENTRY_POINT_DEFAULT()

#if PROXY_INTERFACE

//
//CPVInterfaceProxy_OMX
//

OSCL_EXPORT_REF CPVInterfaceProxy_OMX * CPVInterfaceProxy_OMX::NewL(
    PVProxiedEngine_OMX& app
    , Oscl_DefAlloc *alloc
    , int32 stacksize
    , uint32 nreserve1
    , uint32 nreserve2
    , int32 handlerPri
    , int32 notifierPri)
//called under app thread context
{
    // custom allocator not using TRY/LEAVE/TLS
    OsclMemAllocator defallocL;
    OsclAny *ptr = NULL;
    if (alloc)
    {
        ptr = alloc->ALLOCATE(sizeof(CPVInterfaceProxy_OMX));
        //Commented all the oscl tls statements
        //OsclError::LeaveIfNull(ptr);
        if (ptr == NULL)
        {
            return NULL;
        }
    }
    else
    {
        ptr = defallocL.ALLOCATE(sizeof(CPVInterfaceProxy_OMX));
    }
    CPVInterfaceProxy_OMX *self = OSCL_PLACEMENT_NEW(ptr, CPVInterfaceProxy_OMX(app, alloc, stacksize));
    bool err;
    //Commented OSCL_TRY to remove oscl initialization dependency
    err = self->ConstructL(nreserve1, nreserve2, handlerPri, notifierPri);
    if (err == false)
    {
        self->Delete();
        return NULL;
    }
    return self;
}

CPVInterfaceProxy_OMX::CPVInterfaceProxy_OMX(PVProxiedEngine_OMX& app, Oscl_DefAlloc*alloc, int32 stacksize)
        : iPVApp(app)
//called under app thread context
{
    iCommandIdCounter = 0;
    iProxyIdCounter = 0;
    iHandler = NULL;
    iPVScheduler = NULL;
    iNotifier = NULL;
    iStacksize = 0x8000;
    OSCL_UNUSED_ARG(stacksize);
    iStopped = true;
    iAlloc = (alloc) ? alloc : &iDefAlloc;
    iLogger = NULL;
    iOMXThreadCreated = false;
}

//Commented all the OsclError::Leave statements to remove oscl tls dependency
//Changed return type of this function from void to bool

bool CPVInterfaceProxy_OMX::ConstructL(uint32 nreserve1, uint32 nreserve2, int32 handlerPri, int32 notifierPri)
{
    // Create the semaphores and critical sections
    if (iInitSem.Create() != OsclProcStatus::SUCCESS_ERROR
            || iExitedSem.Create() != OsclProcStatus::SUCCESS_ERROR
            || iCounterCrit.Create() != OsclProcStatus::SUCCESS_ERROR
            || iHandlerQueueCrit.Create() != OsclProcStatus::SUCCESS_ERROR
            || iNotifierQueueCrit.Create() != OsclProcStatus::SUCCESS_ERROR
            || iProxyListCrit.Create() != OsclProcStatus::SUCCESS_ERROR
            || iThreadCreatedSem.Create() != OsclProcStatus::SUCCESS_ERROR)
    {
        return false;
    }
    //reserve space in vectors...
    if (nreserve1 > 0)
        iProxyList.reserve(nreserve1);
    if (nreserve2 > 0)
    {
        iCommandQueue.reserve(nreserve2);
//      iNotificationQueue.reserve(nreserve2);
    }

    //create handler
    OsclAny *ptr = iAlloc->ALLOCATE(sizeof(CPVInterfaceProxyHandler_OMX));
    if (ptr == NULL)
    {
        return false;
    }

    iHandler = OSCL_PLACEMENT_NEW(ptr, CPVInterfaceProxyHandler_OMX(this, handlerPri));

    //create notifier
    ptr = iAlloc->ALLOCATE(sizeof(CPVInterfaceProxyNotifier_OMX));
    if (ptr == NULL)
    {
        return false;
    }

    iNotifier = OSCL_PLACEMENT_NEW(ptr, CPVInterfaceProxyNotifier_OMX(this, notifierPri));
    return true;
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::Delete()
//called under app thread context
{
    Oscl_DefAlloc *alloc = this->iAlloc;
    bool default_alloc = (this->iAlloc == &this->iDefAlloc);
    this->~CPVInterfaceProxy_OMX();
    if (default_alloc)
    {
        OsclMemAllocator defalloc;
        defalloc.deallocate(this);
    }
    else
    {
        alloc->deallocate(this);
    }
}

CPVInterfaceProxy_OMX::~CPVInterfaceProxy_OMX()
//called under app thread context
{
    //make sure thread was stopped.
    StopPVThread();

    CleanupAppThreadQueues();

    //delete handler and notifier
    if (iHandler)
    {
        iHandler->~CPVInterfaceProxyHandler_OMX();
        iAlloc->deallocate(iHandler);
    }
    iHandler = NULL;
    if (iNotifier)
    {
        iNotifier->~CPVInterfaceProxyNotifier_OMX();
        iAlloc->deallocate(iNotifier);
    }
    iNotifier = NULL;

    iCounterCrit.Close();
    iHandlerQueueCrit.Close();
    iNotifierQueueCrit.Close();
    iProxyListCrit.Close();
    iThreadCreatedSem.Close();
    iInitSem.Close();
    iExitedSem.Close();
}

//forward...
TOsclThreadFuncRet OSCL_THREAD_DECL pvproxythreadmain_omx(TOsclThreadFuncArg *aPtr);

OSCL_EXPORT_REF bool CPVInterfaceProxy_OMX::StartPVThread()
//called under app thread context
{
    if (!iStopped)
        return false;//thread already active

    //Notifier not required in omx component, Scheduler not initialized in testapp thread

    // Create the PV thread.
    OsclProcStatus::eOsclProcError err;
    err = iPVThread.Create((TOsclThreadFuncPtr)pvproxythreadmain_omx,
                           iStacksize,
                           (TOsclThreadFuncArg)this,
                           Start_on_creation,
                           true
                          );


    if (err == OSCL_ERR_NONE)
    {
        // the iThreadCreated semaphore blocks the app thread until the new thread initializes oscl etc.
        // we use it before checking the flag "iOMXThreadCreated"
        // In case of either success or error the new thread signals this semaphore
        if (iThreadCreatedSem.Wait() != OsclProcStatus::SUCCESS_ERROR)
        {
            iNotifier->RemoveFromScheduler();
            return false;
        }
        // check if the thread was created OK
        if (!iOMXThreadCreated)
        {
            //error cleanup
            iNotifier->RemoveFromScheduler();
            return false;
        }

        iStopped = false;
        //Wait for PV thread to initialize its scheduler.
        if (iInitSem.Wait() != OsclProcStatus::SUCCESS_ERROR)
        {
            //Commented to remove oscl tls dependency
            return false;
        }
        return true;
    }
    else
    {
        //error cleanup
        iNotifier->RemoveFromScheduler();
        return false;
    }
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::StopPVThread()
//called under app thread context.
{
    //if called under the PV thread, we'll get deadlock..
    //so don't allow it.
    if (iPVThreadContext.IsSameThreadContext())
    {   //Commented to remove oscl tls dependency (OsclError::Panic)
        return;
    }

    if (iStopped)
    {
        return ;
    }

    //deque notifier AO
    iNotifierQueueCrit.Lock();
    if (iNotifier && iNotifier->IsAdded())
    {
        iNotifier->RemoveFromScheduler();
    }

    iNotifierQueueCrit.Unlock();

    //Stop the scheduler loop.
    if (iPVScheduler)
    {
        iPVScheduler->StopScheduler();
    }

    //Wait for PV thread to finish up, then it's safe
    //to delete the remaining stuff.
    if (iExitedSem.Wait() != OsclProcStatus::SUCCESS_ERROR)
    {   //Commented to remove oscl tls dependency (OsclError::Panic)
        return;
    }

    //let the destructor know it's exited.
    iStopped = true;

    //The thread will exit on its own, but go ahead and
    //forcibly terminate it now to make sure it's cleaned
    //up by the time this call returns.
    iPVThread.Terminate(0);
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::DeliverNotifications(int32 aTargetCount, int32& aNoticesPending)
//deliver notifications off the queue, from the app thread size
{
    //make sure this isn't called under PV thread...
    if (iPVThreadContext.IsSameThreadContext())
        OsclError::Leave(OsclErrThreadContextIncorrect);

    for (int32 count = 0; count < aTargetCount;)
    {
        //get next notification or cleanup message.
        iNotifierQueueCrit.Lock();
        //CPVProxyMsg notice(0,0,NULL);
        //Added, increased one more parameter in the call
        CPVProxyMsg_OMX notice(0, 0, 0, NULL);
        if (iNotificationQueue.size() > 0)
        {
            notice = iNotificationQueue[0];
            iNotificationQueue.erase(&iNotificationQueue[0]);
        }
        iNotifierQueueCrit.Unlock();

        if (notice.iMsg)
        {
            count++;
            CPVProxyInterface_OMX *ext = FindInterface(notice.iProxyId);
            if (ext)
                ext->iClient->HandleNotification(notice.iMsgId, notice.iMsg);
            else
            {   //since messages are cleaned up when interfaces
                //get unregistered, we should not get here.
                OSCL_ASSERT(NULL != ext);//debug error.
            }
        }
        else
            break;//no more messages.
    }
    //return number of notices left after trying to process
    //the desired number.
    aNoticesPending = iNotificationQueue.size();
}

void CPVInterfaceProxy_OMX::CleanupAppThreadQueues()
//cleanup memory allocated in App thread.
{
    //un-sent commands...
    iHandlerQueueCrit.Lock();
    while (!iCommandQueue.empty())
    {
        CPVProxyMsg_OMX *msg = &iCommandQueue[0];
        CPVProxyInterface_OMX *proxy = FindInterface(msg->iProxyId);
        if (proxy)
            proxy->iClient->CleanupCommand(msg->iMsgId, msg->iMsg);
        iCommandQueue.erase(msg);
    }
    iCommandQueue.clear();
    iCommandQueue.destroy();
    iHandlerQueueCrit.Unlock();

    //proxy list...
    iProxyListCrit.Lock();
    iProxyList.clear();
    iProxyList.destroy();
    iProxyListCrit.Unlock();
}

void CPVInterfaceProxy_OMX::CleanupPVThreadQueues()
//cleanup memory allocated in PV thread.
{
    //un-sent notifications
    iNotifierQueueCrit.Lock();
    while (!iNotificationQueue.empty())
    {
        CPVProxyMsg_OMX *msg = &iNotificationQueue[0];
        CPVProxyInterface_OMX *proxy = FindInterface(msg->iProxyId);
        if (proxy)
            proxy->iServer->CleanupNotification(msg->iMsgId, msg->iMsg);
        iNotificationQueue.erase(msg);
    }
    iNotificationQueue.clear();
    iNotificationQueue.destroy();
    iNotifierQueueCrit.Unlock();
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::CancelCommand(TPVProxyId aProxyId, TPVProxyMsgId aMsgId)
{
    CleanupCommands(FindInterface(aProxyId), false, aMsgId);
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::CancelAllCommands(TPVProxyId aProxyId)
{
    CleanupCommands(FindInterface(aProxyId), true);
}

void CPVInterfaceProxy_OMX::CleanupCommands(CPVProxyInterface_OMX *aExt, bool aAll, TPVProxyMsgId aMsgId)
{
    if (!aExt)
        return ;
    iHandlerQueueCrit.Lock();
    for (uint32 i = 0; i < iCommandQueue.size(); i++)
    {
        CPVProxyMsg_OMX *msg = &iCommandQueue[i];
        if (msg->iProxyId == aExt->iProxyId
                && (aAll || msg->iMsgId == aMsgId))
        {
            aExt->iClient->CleanupCommand(msg->iMsgId, msg->iMsg);
            iCommandQueue.erase(msg);
            i--;//move back one since vecter gets scrunched.
            if (!aAll)
            {
                iHandlerQueueCrit.Unlock();
                return ;
            }
        }
    }
    iHandlerQueueCrit.Unlock();
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::CancelNotification(TPVProxyId aProxyId, TPVProxyMsgId aMsgId)
{
    CleanupNotifications(FindInterface(aProxyId), false, aMsgId);
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::CancelAllNotifications(TPVProxyId aProxyId)
{
    CleanupNotifications(FindInterface(aProxyId), true);
}

void CPVInterfaceProxy_OMX::CleanupNotifications(CPVProxyInterface_OMX *aExt, bool aAll, TPVProxyMsgId aMsgId)
{
    if (!aExt)
        return ;
    iNotifierQueueCrit.Lock();
    for (uint i = 0; i < iNotificationQueue.size(); i++)
    {
        CPVProxyMsg_OMX *msg = &iNotificationQueue[i];
        if (msg->iProxyId == aExt->iProxyId
                && (aAll || msg->iMsgId == aMsgId))
        {
            aExt->iServer->CleanupNotification(msg->iMsgId, msg->iMsg);
            iNotificationQueue.erase(msg);
            i--;//move back one since vector gets scrunched.
            if (!aAll)
            {
                iNotifierQueueCrit.Unlock();
                return ;
            }
        }
    }
    iNotifierQueueCrit.Unlock();
}

void CPVInterfaceProxy_OMX::CleanupInterfaceMessages(CPVProxyInterface_OMX *aExt)
//cleanup all extension messages for a particular interface.
{
    CleanupCommands(aExt, true);
    CleanupNotifications(aExt, true);
}

OSCL_EXPORT_REF TPVProxyId CPVInterfaceProxy_OMX::RegisterProxiedInterface(
    PVProxiedInterfaceServer_OMX& server_proxy,
    PVProxiedInterfaceClient_OMX& client_proxy)
//Proxy extensions call this to register themselves.
{
    TPVProxyId id = ++iProxyIdCounter;
    iProxyListCrit.Lock();
    CPVProxyInterface_OMX proxy(id, &server_proxy, &client_proxy);
    int32 err;
    OSCL_TRY(err, iProxyList.push_back(proxy););
    iProxyListCrit.Unlock();
    OsclError::LeaveIfError(err);
    return id;
}

OSCL_EXPORT_REF void CPVInterfaceProxy_OMX::UnregisterProxiedInterface(TPVProxyId aProxyId)
//Proxy extensions call this to unregister themselves.
{
    iProxyListCrit.Lock();
    CPVProxyInterface_OMX *ext = FindInterface(aProxyId, true);
    if (ext)
    {
        //cleanup unprocessed messages and remove.
        CleanupInterfaceMessages(ext);
        iProxyList.erase(ext);
    }
    iProxyListCrit.Unlock();
}

OSCL_EXPORT_REF TPVProxyMsgId CPVInterfaceProxy_OMX::SendCommand(TPVProxyId aProxyId, TPVCommandId cmdid, OsclAny *aCmd)
//Proxy extensions call this to send commands from app side
//to PV side.
{
    bool status;
    iCounterCrit.Lock();
    TPVProxyMsgId id = ++iCommandIdCounter;
    iCounterCrit.Unlock();
    iHandlerQueueCrit.Lock();
    //Changed the function arguments to pass command id
    CPVProxyMsg_OMX msg(aProxyId, id, cmdid, aCmd);

    status = iCommandQueue.push_back(msg);

    //if the queue was empty, signal the AO.
    if (iCommandQueue.size() == 1)
    {
        iHandler->PendComplete(OSCL_REQUEST_ERR_NONE);
    }

    iHandlerQueueCrit.Unlock();
    //propagate any allocation failure...
    if (status == false)
    {
        return false;
    }
    return id;
}

OSCL_EXPORT_REF TPVProxyMsgId CPVInterfaceProxy_OMX::SendNotification(TPVProxyId aProxyId, OsclAny *aResp)
//Proxy extensions call this to send notifications from PV
//side to app side.
{
    int32 err = OSCL_ERR_NONE;
    iCounterCrit.Lock();
    TPVProxyMsgId id = ++iCommandIdCounter;
    iCounterCrit.Unlock();
    iNotifierQueueCrit.Lock();
    CPVProxyMsg_OMX msg(aProxyId, id, 0, aResp);
    OSCL_TRY(err, iNotificationQueue.push_back(msg););
    //if the queue was empty and the notifier is scheduled,
    //signal it.
    if (iNotifier
            && iNotifier->IsAdded()
            && iNotificationQueue.size() == 1)
        iNotifier->PendComplete(OSCL_REQUEST_ERR_NONE);
    iNotifierQueueCrit.Unlock();
    //propagate any allocation failure...
    OsclError::LeaveIfError(err);
    return id;
}

CPVProxyInterface_OMX * CPVInterfaceProxy_OMX::FindInterface(TPVProxyId aId, bool locked)
//lookup a registered proxy interface
{
    if (!locked)
        iProxyListCrit.Lock();
    for (uint32 i = 0; i < iProxyList.size(); i++)
    {
        if (iProxyList[i].iProxyId == aId)
        {
            if (!locked)
                iProxyListCrit.Unlock();
            return &iProxyList[i];
        }
    }
    if (!locked)
        iProxyListCrit.Unlock();
    return NULL;
}

void CPVInterfaceProxy_OMX::InThread()
//this is the guts of the proxy thread routine.
//it's a separate routine since it needs to be
//run under a trap handler in order to avoid any
//Cbase 66 panic from the cleanup stack.
{
    volatile int32 errTerm = OsclErrNone;
    volatile int32 errSched = OsclErrNone;

    //create & install scheduler
    OsclScheduler::Init("PVProxy");
    iPVScheduler = OsclExecScheduler::Current();

    iPVThreadContext.EnterThreadContext();

    //add handler to scheduler
    iHandler->AddToScheduler();
    iHandler->PendForExec();

    //App thread logon...
    int32 err;
    OSCL_TRY(err, iPVApp.PVThreadLogon(*this););
    if (err != OsclErrNone)
        errTerm = err;

    iOMXThreadCreated = true;
    // now it's safe to signal the thread create sema
    iThreadCreatedSem.Signal();

    //Start scheduler.  This call blocks until scheduler is
    //either stopped or exits due to an error.
    OSCL_TRY(err, iPVScheduler->StartScheduler(&iInitSem););
    if (err != OsclErrNone)
        errSched = err;

    //Cleanup un-processed data.
    CleanupPVThreadQueues();

    //App thread logoff...
    OSCL_TRY(err, iPVApp.PVThreadLogoff(*this););
    if (err != OSCL_ERR_NONE)
        errTerm = err;

    //Deque the handler AO
    iHandlerQueueCrit.Lock();
    iHandler->RemoveFromScheduler();
    iHandlerQueueCrit.Unlock();

    iPVThreadContext.ExitThreadContext();

    //Uninstall scheduler
    OsclScheduler::Cleanup();
    iPVScheduler = NULL;
    iOMXThreadCreated = false;

    //Generate panics if any leaves happened.
    OSCL_ASSERT(errTerm == OsclErrNone);//EPVProxyPanicEngineLeave
    OSCL_ASSERT(errSched == OsclErrNone);//EPVProxyPanicSchedulerLeave
}

////////////////////////////////
// OS-specific Thread routines
////////////////////////////////


#include "oscl_mem_audit.h"

TOsclThreadFuncRet OSCL_THREAD_DECL pvproxythreadmain_omx(TOsclThreadFuncArg *aPtr)
//PV Thread main routine
{

    CPVInterfaceProxy_OMX *proxy = (CPVInterfaceProxy_OMX *) aPtr;

    //Init OSCL and create logger.
    int error = OsclBase::Init();
    if (error)
    {
        proxy->iThreadCreatedSem.Signal(); // signal to let the app thread run
        return 0;
    }
    error = OsclErrorTrap::Init();
    if (error)
    {
        proxy->iThreadCreatedSem.Signal(); // signal to let the app thread run
        return 0;
    }
    OsclMem::Init();
    PVLogger::Init();

#if defined( OSCL_SET_THREAD_NAME)
    OSCL_SET_THREAD_NAME("OMX proxy");
#endif

    //Call the proxied app routine to create its logger appenders.
    //proxy->iPVApp.CreateLoggerAppenders();
    //proxy->iLogger=PVLogger::GetLoggerObject("");



    int32 leave;
    OSCL_TRY(leave, proxy->InThread(););


    proxy->iThreadCreatedSem.Signal(); // signal to let the app thread run just in case

    //Cleanup logger.
    PVLogger::Cleanup();

    OsclMem::Cleanup();

    OsclErrorTrap::Cleanup();
    OsclBase::Cleanup();

    //Signal & Exit
    proxy->iExitedSem.Signal();

    return 0;
}
#endif // PROXY_INTERFACE








