/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   Interthread synchronization object
*
*/



#include <e32debug.h>
#include <coemain.h>
#include "alfstreamerbridge.h"
#include "alfstreamerserver.h"
#include "alflogger.h"

#ifdef ALF_DEBUG_TRACK_DRAWING
#include "alfcommanddebug.h"
#endif


// #define EGL_TALKS_TO_WINDOW_SERVER

// ---------------------------------------------------------------------------
// constructor
// 
// ---------------------------------------------------------------------------
//

// The priority is EActivePriorityWsEvents+1 instead of CActive::EPriorityHigh to prevent a deadlock
// when AlfredServerThread calling iWsSession.GetEvent(event); from inside CCoeEnv::RunL() while
// wserv was waiting inside CWindowServer::CDefaultAnimationScheduler::DoRedrawNow for Alf to
// complete the TRequestStatus passed to CAlfRenderStage::End.

CAlfStreamerBridge::CAlfStreamerBridge(MAlfStreamerListener* aObserver):CActive(EActivePriorityWsEvents+1),iObserver(aObserver), iBatchObserver(0), iThread(RThread().Id())
    {
    CActiveScheduler::Add(this);       
    iStatus = KRequestPending;
    SetActive();
    TInt err = iQueueSema.CreateLocal();
    
    if (err)
        {
        // Device startup ends here.
        __ALFLOGSTRING("Stop the music, we are running without synchronization");
        User::Panic(_L("UI Accelerator"), 1);
        }
    }

// ---------------------------------------------------------------------------
// NewL
// 
// ---------------------------------------------------------------------------
// 
CAlfStreamerBridge* CAlfStreamerBridge::NewL( MAlfStreamerListener* aObserver )
    {
    CAlfStreamerBridge* self = new(ELeave)CAlfStreamerBridge( aObserver );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// ConstructL
// 
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::ConstructL()
    {
    iDataBuf.Create( KInitialVariableBufferSize );
    iDataBuf.SetLength(0);
    iStreamerServer = NULL;
#ifdef ALF_DEBUG_TRACK_DRAWING
    iCommandDebugger = CAlfCommandDebug::NewL();
#endif
    CHuiEnv::Static()->iSwapObserver = this;
    }

// ---------------------------------------------------------------------------
// destructor
// 
// ---------------------------------------------------------------------------
// 
CAlfStreamerBridge::~CAlfStreamerBridge()
    {
    CHuiEnv::Static()->iSwapObserver = 0;
    iDataBuf.Close();
    iQueueSema.Close();
#ifdef ALF_DEBUG_TRACK_DRAWING
    delete iCommandDebugger;
#endif
    }

// ---------------------------------------------------------------------------
// SetBatchObserver
// 
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::SetBatchObserver(MAlfBatchObs* aBatchObserver)
    {
    iBatchObserver = aBatchObserver;
    }

// ---------------------------------------------------------------------------
// Activate
// 
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::Activate()
    {
    iStatus = KRequestPending;    
    SetActive();
    }
        
// ---------------------------------------------------------------------------
// Trigger
// 
// ---------------------------------------------------------------------------
// 
TInt CAlfStreamerBridge::Trigger(TInt aStatus)
    {
    TInt err = KErrNone;
    if (!iVarDataAddedButNotPosted)
        {
        iQueueSema.Wait();
        }
    iVarDataAddedButNotPosted = EFalse; // after this point, data may just be lost if posting fails    
    __ALFLOGSTRING2("CAlfStreamerBridge::Trigger status %d, own request status %d", aStatus, iStatus.Int() );
    if (IsActive() && iStatus == KRequestPending)
        {
        RThread t;
        TInt err = t.Open(iThread);
        if (err)
            {
            __ALFLOGSTRING1( "CAlfStreamerBridge::Trigger() ignore RThread::Open() error: %d", err );
            }
        TRequestStatus* status = &iStatus;
        __ALFLOGSTRING("CAlfStreamerBridge::Trigger - completing  CAlfStreamerBridge");
        t.RequestComplete(status, aStatus);
        t.Close();
        }
    else
        {
#ifdef _DEBUG
        if ( IsActive() )
            {
            __ALFLOGSTRING("CAlfStreamerBridge::Trigger - already active");
            }
        __ALFLOGSTRING("CAlfStreamerBridge::Trigger - appending data");
#endif        
        err = iQueue.Append(aStatus);
        }

    iQueueSema.Signal();
    __ALFLOGSTRING1("CAlfStreamerBridge::Trigger request status %d", iStatus.Int() );
    return err;
    }
  
// ---------------------------------------------------------------------------
// AddData
// 
// ---------------------------------------------------------------------------
// 
TInt CAlfStreamerBridge::AddData( TAlfDecoderServerBindings aOp,TInt aI1,TInt aI2 ,TAny* aPtr )
    {
    if (iSwapActive && aOp == EAlfRequestCommitBatch)
        {
        __ALFLOGSTRING("CAlfStreamerBridge::AddData() just release window server");    
        iBatchObserver->BridgerCallback(KRELEASEWINDOWSERVER,KRELEASEDBEFOREQUEUE);
        return KErrNone;
        }
    
    TInt err = KErrNotFound;
        {    
#ifdef ALF_DEBUG_TRACK_DRAWING    
        iCommandDebugger->SetDescription( aOp, R_ALF_BRIDGE_COMMAND_DESCRIPTION_ARRAY );
        __ALFLOGSTRING2("CAlfStreamerBridge::AddData op %S, iQueue.Length = %d", &iCommandDebugger->Text(), iQueue.Count());
    #else
        __ALFLOGSTRING2("CAlfStreamerBridge::AddData op %d, iQueue.Length = %d", aOp, iQueue.Count());    
    #endif
        err = KErrNone;
        for(TInt i = 0; i < iMessages.Count(); i++ )
            {
            if (iMessages[i].IsEmpty())
                {
                iMessages[i].Set(aOp,aI1,aI2,aPtr);
                err = Trigger(i);
                return err;
                }
            }
    
        // No existing (empty) cell was available for recycling, add new cell to array
        TAlfBridgerData data;
        data.Set(aOp,aI1,aI2,aPtr);
        err = iMessages.Append(data);
        }
   if (!err) 
        {
        err = Trigger(iMessages.Count()-1);
        }
	else
		{
		iQueueSema.Signal();
		}	    
    return err;
    }

// ---------------------------------------------------------------------------
// GetData
// 
// Note, that this method frees the message entry for reuse.
// ---------------------------------------------------------------------------
// 
TAlfBridgerData CAlfStreamerBridge::GetData(TInt aIndex)
    {
    TAlfBridgerData data;
    ASSERT(aIndex <= iMessages.Count()-1);
    data = iMessages[aIndex];
    return data;    
    }

void CAlfStreamerBridge::PrepareSwap()
    {
#ifdef EGL_TALKS_TO_WINDOW_SERVER
    __ALFLOGSTRING("CAlfStreamerBridge:: Prepare swap, flush the queue");    
    iSwapActive = ETrue;
    if (iStatus.Int() >=0 && iMessages.Count() > iStatus.Int() )
        {
        Cancel(); // remove from scheduler
        RunL(); // run manually (and activate)
        }
    __ALFLOGSTRING("CAlfStreamerBridge:: Prepare swap, the queue emptied");    
#endif //#ifdef EGL_TALKS_TO_WINDOW_SERVER
    }

void CAlfStreamerBridge::SwapComplete()
    {
#ifdef EGL_TALKS_TO_WINDOW_SERVER
    __ALFLOGSTRING("CAlfStreamerBridge:: Swap buffers complete");    
    iSwapActive = EFalse;
#endif // #ifdef EGL_TALKS_TO_WINDOW_SERVER

    }
	
#ifdef EGL_TALKS_TO_WINDOW_SERVER
void CAlfStreamerBridge::ReleaseWindowServer(TBool aRelease)
    {
    __ALFLOGSTRING1("CAlfStreamerBridge::ReleaseWindowServer: %d",aRelease);    
    if (aRelease)
        {
        iMakeCurrentActive = ETrue;    
        if (iBatchObserver && !iSwapActive)    
            {
            iBatchObserver->BridgerCallback(KRELEASEWINDOWSERVER);
            }
        }
    else
        {
        iMakeCurrentActive = EFalse;
        }                
#else
void CAlfStreamerBridge::ReleaseWindowServer(TBool)
	{
#endif
    }

void CAlfStreamerBridge::SetWgIdArray(TInt* aArray)
    {
    iWgArray = aArray;
    }

EXPORT_C TUid CAlfStreamerBridge::FindAppUidForWgId(TInt /*aWgId*/)
    {
    __ASSERT_DEBUG(EFalse, User::Leave(KErrNotSupported));
    return KNullUid;
    }        

EXPORT_C TInt CAlfStreamerBridge::FindWgForAppUid(TUid /*aAppUid*/)
    {
    __ASSERT_DEBUG(EFalse, User::Leave(KErrNotSupported));
    return 0;
    }        
    
// ---------------------------------------------------------------------------
// RunL
// 
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::RunL()
    {
    TInt operation = iMessages[iStatus.Int()].iOp;
    
#ifdef ALF_DEBUG_TRACK_DRAWING 
    iCommandDebugger->SetDescription( operation, R_ALF_BRIDGE_COMMAND_DESCRIPTION_ARRAY );
    __ALFLOGSTRING1("CAlfStreamerBridge::RunL: First op %S", &iCommandDebugger->Text() );
#else
    __ALFLOGSTRING1("CAlfStreamerBridge::RunL: First op %d", operation);
#endif
    switch ( operation )
        {
        // Just call back to Alf decoder thread
        case EAlfRequestCommitBatch:
        case EAlfRequestCommandReadNotification:
        case EAlfReleaseTemporaryChunk:
            {
            if (iBatchObserver)
                {
                iBatchObserver->BridgerCallback( operation, iMessages[iStatus.Int()].iInt1 );
                }
            iMessages[iStatus.Int()].ResetEntry();
            break;
            }
        default:
            {
            // Handle command
            __ALFLOGSTRING("CAlfStreamerBridge:: calling observer callback");
            iObserver->HandleCallback(iStatus.Int());
            
            // For "getters" also call back to Alf decoder thread
            if ((operation > EAlfDSGetCommandsStart) && (operation < EAlfDSGetCommandsEnd))
            	{
            	if (iBatchObserver)
                	{
                	iBatchObserver->BridgerCallback( operation, iMessages[iStatus.Int()].iInt1 );
                	}
            	}
            	
    		iMessages[iStatus.Int()].ResetEntry(); // mark the cell free
            }
        }
    
    // Todo: is this kind of burst mode ok, it should be..
    iQueueSema.Wait();
    while (iQueue.Count())
        {
    	TInt operation2 = iMessages[iQueue[0]].iOp;
#ifdef ALF_DEBUG_TRACK_DRAWING 
    iCommandDebugger->SetDescription( operation2, R_ALF_BRIDGE_COMMAND_DESCRIPTION_ARRAY );
    __ALFLOGSTRING1("CAlfStreamerBridge:: Queue op %S", &iCommandDebugger->Text() );
#else
    __ALFLOGSTRING1("CAlfStreamerBridge:: Queue op %d", operation2 );
#endif
    switch ( operation2 )
            {
            case EAlfRequestCommitBatch:
            case EAlfRequestCommandReadNotification:
            case EAlfReleaseTemporaryChunk:
                {
                if (iBatchObserver)
                    {
                    __ALFLOGSTRING("CAlfStreamerBridge:: calling BATCH observer callback");
                    iBatchObserver->BridgerCallback( operation2, iMessages[iQueue[0]].iInt1 );
                    }
                iMessages[iQueue[0]].ResetEntry();
                break;
                }
            default:
                {
	            // Handle command
                __ALFLOGSTRING("CAlfStreamerBridge:: calling observer callback");
	            iObserver->HandleCallback(iQueue[0]);
	            
	            // For "getters" also call back to Alf decoder thread
	            if ((operation2 > EAlfDSGetCommandsStart) && (operation2 < EAlfDSGetCommandsEnd))
	            	{
	            	if (iBatchObserver)
	                	{
	                	iBatchObserver->BridgerCallback( operation2, iMessages[iQueue[0]].iInt1 );
	                	}
	            	}
	    		iMessages[iQueue[0]].ResetEntry(); // mark the cell free
                }
            }
        iQueue.Remove(0);
        }
    if ( !iItemsInBuffer )
        {
        iDataBuf.SetLength(0);
        }
        
    __ALFLOGSTRING1("CAlfStreamerBridge::RunL - activating", iItemsInBuffer);
    Activate();
    iQueueSema.Signal();
    __ALFLOGSTRING1("CAlfStreamerBridge::RunL - really really ending", iItemsInBuffer);
    }

// ---------------------------------------------------------------------------
// DoCancel
// 
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::DoCancel()
    {
    if (iBatchObserver)
        {
        iBatchObserver->BridgerCanceled();
        }
    }

// ---------------------------------------------------------------------------
// StartNewBlock
// 
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::StartNewBlock()
    {
    // Queue marker. Basically we could use one new member to assert that there can
    // be only one marker
    __ALFLOGSTRING1("CAlfStreamerBridge:: Request command read notification, swap active: %d", iSwapActive );    
   if ( iSwapActive || iMakeCurrentActive )
        {
        __ALFLOGSTRING("CAlfStreamerBridge::StartNewBlock() just release window server");    
        iBatchObserver->BridgerCallback(KRELEASEWINDOWSERVER,KRELEASEDBEFOREQUEUE);
        return;
        }
    AddData(EAlfRequestCommitBatch,0,0,0);
    }

// ---------------------------------------------------------------------------
// RequestCommandReadNotification
// 
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::RequestCommandReadNotification( TInt aLastReadOffset, TAlfDecoderServerBindings aCommand )
    {
    AddData( aCommand, aLastReadOffset, 0, 0);
    }

// ---------------------------------------------------------------------------
// AppendVarDataL
// 
// Note, that memory allocation is done only if current buffer is not large enough 
// to add an item.
// TODO: The buffers added are not the buffer in optimal way.
// ---------------------------------------------------------------------------
// 
EXPORT_C const TAny* CAlfStreamerBridge::AppendVarDataL( TInt aSize, TInt& aIndex )
    {
    iQueueSema.Wait();
    // Make sure the length of the allocated data is always divisible by 8
    if ( aSize % 8 != 0 )
        {
        __ALFLOGSTRING1("CAlfStreamerBridge::AppendVarDataL, size not multiple of 8: %d", aSize );  
        aSize = aSize + ( 8 - aSize % 8 );
        }
    if ( iDataBuf.Length() + aSize >= iDataBuf.MaxLength() )
        {
        TRAPD(err, iDataBuf.ReAllocL( iDataBuf.MaxLength() + KInitialVariableBufferSize ));
        if ( err )
            {
            __ALFLOGSTRING("CAlfStreamerBridge::AppendVarDataL, realloc failed");
		    iQueueSema.Signal();
            return NULL;
            }
        __ALFLOGSTRING1("CAlfStreamerBridge::AppendVarDataL, new max size: %d", iDataBuf.MaxLength() );  
        }
    aIndex = iDataBuf.Length();
    iDataBuf.SetLength( iDataBuf.Length() + aSize);
    iItemsInBuffer++;
    __ALFLOGSTRING1("CAlfStreamerBridge::AppendVarDataL: buflength %d", iDataBuf.Length());  
    iVarDataAddedButNotPosted = ETrue;    
    return iDataBuf.MidTPtr( aIndex ).Ptr();
    }

// ---------------------------------------------------------------------------
// GetVarDataL
// 
// Note, that data is not removed at any point. However it should not be accessed again 
// with this method, because that would confuse iItemsInBuffer counter.
// ---------------------------------------------------------------------------
// 
const TAny* CAlfStreamerBridge::GetVarDataL( TInt aIndex )
    {
    ASSERT( aIndex >= 0 && aIndex < iDataBuf.MaxLength() );
    ASSERT( iItemsInBuffer );
    iItemsInBuffer--;
    return iDataBuf.MidTPtr( aIndex ).Ptr();
    }

EXPORT_C const TAny* CAlfStreamerBridge::AppendEffectsDataL( TInt aSize, TInt& aIndex )
    {
	return AppendVarDataL( aSize, aIndex );
    }

// ---------------------------------------------------------------------------
// GetVarDataL
// 
// Note, that data is not removed at any point. However it should not be accessed again 
// with this method, because that would confuse iItemsInBuffer counter.
// ---------------------------------------------------------------------------
// 
const TAny* CAlfStreamerBridge::GetEffectsDataL( TInt aIndex )
    {
    return GetVarDataL(aIndex);
    }

// ---------------------------------------------------------------------------
// SetStreamerServer
// ---------------------------------------------------------------------------
// 
void CAlfStreamerBridge::SetStreamerServer( CAlfStreamerServer& aStreamerServer )
    {
    iStreamerServer = &aStreamerServer;   
    }

// ---------------------------------------------------------------------------
// StreamerServer
// ---------------------------------------------------------------------------
// 
CAlfStreamerServer* CAlfStreamerBridge::StreamerServer()
    {
    return iStreamerServer;
    }
