/*
* 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:  Implementation of the class CFscContactActionPluginEngine.
*
*/


// INCLUDES
#include "emailtrace.h"
#include <e32std.h>

#include "fsccontactactionserviceuids.hrh"
#include "cfsccontactactionpluginengine.h"
#include "cfscactionutils.h"
#include "mfsccontactaction.h"
#include "cfsccontactactionplugin.h"
#include "tfsccontactactionqueryresult.h"
#include "mfsccontactactionpluginengineobserver.h"

// ======== LOCAL FUNCTIONS ========

// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// Two-phased constructor.
// ---------------------------------------------------------------------------
//
CFscContactActionPluginEngine* CFscContactActionPluginEngine::NewL(
    CVPbkContactManager& aContactManager )
    {
    FUNC_LOG;

    CFscContactActionPluginEngine* self = 
        new ( ELeave ) CFscContactActionPluginEngine( aContactManager );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
        
    return self;
    }

// ---------------------------------------------------------------------------
// Destructor.
// ---------------------------------------------------------------------------
//
CFscContactActionPluginEngine::~CFscContactActionPluginEngine()
    {
    FUNC_LOG;
    ReleasePlugins();
    iActionPlugins.Close();
    delete iActionUtils;
    }

// ---------------------------------------------------------------------------
// Detect and load plugins.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::LoadPluginsL()
    {
    FUNC_LOG;
    
    if ( iPluginsLoaded )
        {
        // Realease plugins before reloading
        ReleasePlugins();
        }
        
    RImplInfoPtrArray implArray;
    TUid pluginIfUid = { KFscContactActionPluginIfUid };
    REComSession::ListImplementationsL( pluginIfUid, implArray );
        
    //Create implementations
    CFscContactActionPlugin* plugin = NULL;
    TInt implArrayCount = implArray.Count();
    for ( TInt i = 0; i < implArrayCount; i++ )
        {
        TRAPD( error,
            {
            plugin = CFscContactActionPlugin::NewL( 
                implArray[i]->ImplementationUid(), iPluginParams );
            CleanupStack::PushL( plugin );
            iActionPlugins.AppendL( plugin );    
            CleanupStack::Pop( plugin );
            });
        if ( error )
            {
                     
            // Leave only if no memory. This way unstable plugins can not make 
            // service totaly unusable
            if ( error == KErrNoMemory )
                {
                implArray.ResetAndDestroy();            
                User::Leave( error );
                }
            }
        plugin = NULL;
        }
        
    implArray.ResetAndDestroy();
    iPluginsLoaded = ETrue;
    
    
    }

// ---------------------------------------------------------------------------
// Release loaded plugins.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::ReleasePlugins()
    {
	FUNC_LOG;
    iActionPlugins.ResetAndDestroy();
    iPluginsLoaded = EFalse;
    REComSession::FinalClose();
    }
    
// ---------------------------------------------------------------------------
// Query state of plugins.
// ---------------------------------------------------------------------------
//
TBool CFscContactActionPluginEngine::PluginsLoaded()
    {
	FUNC_LOG;
    return iPluginsLoaded;
    }    

// ---------------------------------------------------------------------------
// CFscContactActionPluginEngine::AllActionsListL
//
// Get all actions of all contact action plugins matching provided action type flags
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::AllActionsListL(
        CFscContactActionList& aActionList,
        TUint64 aActionTypeFlags) 
    {  
    // Loop all loaded action plugins
    TInt i = 0;       
    while ( i < iActionPlugins.Count() )
        {       
        CFscContactActionPlugin* plugin = 
                iActionPlugins[ i++ ];
                  
        const CArrayFix<TUid>* pluginActionList = plugin->ActionList();
        TInt pluginActionCount = 
                ( pluginActionList != NULL ) ? pluginActionList->Count() : 0;
        
        // Loop actions of single plugin and add only actions
        // matching to the filter flags 
        TInt j = 0; 
        while ( j < pluginActionCount  )
            {                                         
            TUid uid = ( *pluginActionList )[ j++ ];
            const MFscContactAction& action = plugin->GetActionL( uid );
            if ( aActionTypeFlags & action.Type() )
                {
                TFscContactActionQueryResult contactActionQueryResult;
                contactActionQueryResult.iAction = &action;
                // add to full list    
                aActionList.AppendL( contactActionQueryResult );     
                }
            }
        }
    }


// ---------------------------------------------------------------------------
// Method for quering actions for contacts and groups.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::QueryActionsL( 
    CFscContactActionList& aActionList, MFscContactSet& aContactSet,
    TBool aStopWhenOneActionFound, TUint64 aActionTypeFlags, 
    TInt aMinPriority, MFscContactActionPluginEngineObserver* aObserver )
    {
	FUNC_LOG;
    switch ( iLastEvent )
        {
        case ECasEventIdle:
            {
            iActionList = &aActionList;
            iContactSet = &aContactSet;
            iStopWhenOneActionFound = aStopWhenOneActionFound;
            iActionTypeFlags = aActionTypeFlags; 
            iMinPriority = aMinPriority;
            iObserver = aObserver;
            iCurrentActionPlugin = 0;
            iCurrentAction = 0;
            //iLastEvent = ECasEventBeforePriorityForContactSet;
            // break; - no break here so we can go further when method 
            // called for 1st time.
            }
        
        case ECasEventBeforePriorityForContactSet:
            {
            TBool asyncStarted = EFalse;
            // Loop plugins
            while ( iCurrentActionPlugin < iActionPlugins.Count() 
                    && !asyncStarted )
                {
                //Loop actions of single plugin
                CFscContactActionPlugin* plugin = 
                    iActionPlugins[ iCurrentActionPlugin ];
                
                const CArrayFix<TUid>* actionList = plugin->ActionList();
                TInt actionCount = 
                    ( actionList != NULL ) ? actionList->Count() : 0;
                
                while ( iCurrentAction < actionCount && !asyncStarted )
                    {
                    TUid uid = ( *actionList )[ iCurrentAction ];
                    
                    if ( aActionTypeFlags & plugin->GetActionL( uid ).Type() )
                        {
                        asyncStarted = ETrue;
                        //Check action's priority for given contact set
                        plugin->PriorityForContactSetL( uid, aContactSet,
                            iContactActionQueryResult.iActionMenuVisibility,
                            iContactActionQueryResult.iOptionsMenuVisibility, 
                            this );
                        }
                    
                    if ( !asyncStarted )
                        {
                        ++iCurrentAction;
                        }
                    }
                
                if ( !asyncStarted )
                    {
                    iCurrentAction = 0;
                    ++iCurrentActionPlugin;
                    }
                }
            
            if ( !asyncStarted )
                {
                // inform observer that method finished
                iObserver->QueryActionsComplete();
                iLastEvent = ECasEventIdle;
                }
            
            break;
            }
            
        case ECasEventAfterPriorityForContactSet:
            {
            // here we have to do the same as in 
            // ECasEventBeforePriorityForContactSet event
            // because GetActionL returns const reference which we can't hold 
            // in class' data.
            CFscContactActionPlugin* plugin = 
                iActionPlugins[ iCurrentActionPlugin ];
            
            const CArrayFix<TUid>* actionList = plugin->ActionList();
            TInt actionCount = 
                ( actionList != NULL ) ? actionList->Count() : 0;
            
            TUid uid = ( *actionList )[ iCurrentAction ];
            const MFscContactAction& action = plugin->GetActionL( uid );
            
            // Add action if given minimum priority is exceeded
            if ( iContactActionQueryResult.iPriority >= aMinPriority &&
                 iContactActionQueryResult.iActionMenuVisibility.iVisibility != 
                 TFscContactActionVisibility::EFscActionHidden )
                {
                iContactActionQueryResult.iAction = &action;
                if( iActionList )
                    {
                    iActionList->AppendL( iContactActionQueryResult );
                    }
                }
            
            // if only one action is needed we stop method execution
            if ( iStopWhenOneActionFound && iActionList && iActionList->Count() )
                {
                iLastEvent = ECasEventIdle;
                iObserver->QueryActionsComplete();
                }
            else
                {
                // get next action
                if ( iCurrentAction < actionCount )
                    {
                    iCurrentAction++;
                    }
                else
                    {
                    // no more actions for current plugin
                    iCurrentAction = 0;
                    iCurrentActionPlugin++;
                    }
                
                // if needed run for next plugin
                if ( iCurrentActionPlugin < iActionPlugins.Count() )
                    {
                    iLastEvent = ECasEventBeforePriorityForContactSet;
                    QueryActionsL( *iActionList, *iContactSet, 
                        iStopWhenOneActionFound, iActionTypeFlags, 
                        iMinPriority, iObserver );
                    }
                else
                    {
                    // inform observer that method finished
                    iLastEvent = ECasEventIdle;
                    iObserver->QueryActionsComplete();
                    }
                }
                        
            break;
            }
            
        case ECasEventCanceledPriorityForContactSet:
            {
            // PriorityforContactSet was cancelled
            iLastEvent = ECasEventIdle;
            iCurrentActionPlugin = 0;
            iCurrentAction = 0;
            if( iActionList )
                {
                iActionList->Reset();
                }
            break;
            }
            
        default:
            {
            // we shouldn't be here
            iObserver->QueryActionsFailed( KErrArgument );
            iLastEvent = ECasEventIdle;
            break;
            }
        }
    }

// ---------------------------------------------------------------------------
// Cancels async method QueryActionsL.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::CancelQueryActions()
    {
	FUNC_LOG;
    TInt err = KErrNone;
    if ( iActionPlugins.Count() > iCurrentActionPlugin )
        {
        iActionPlugins[ iCurrentActionPlugin ]->CancelPriorityForContactSet();
        iLastEvent = ECasEventCanceledPriorityForContactSet;
        TRAP( err, QueryActionsL( *iActionList, *iContactSet, 
                    iStopWhenOneActionFound, iActionTypeFlags, iMinPriority, 
                    iObserver ) );        
        }
    
    if ( err!= KErrNone )
        {
        iObserver->QueryActionsFailed( err );
        iLastEvent = ECasEventIdle;
        }
    }

// ---------------------------------------------------------------------------
// Called when PriorityForContactSetL method is complete.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::PriorityForContactSetComplete( 
    TInt aPriority )
    {
	FUNC_LOG;
    iContactActionQueryResult.iPriority = aPriority;
    iLastEvent = ECasEventAfterPriorityForContactSet;
    TRAPD(err, QueryActionsL( *iActionList, *iContactSet, 
            iStopWhenOneActionFound, iActionTypeFlags, iMinPriority, 
            iObserver ) );
    if ( err!= KErrNone )
    	{
    	iLastEvent = ECasEventIdle;
    	iObserver->QueryActionsFailed( err );
    	}
    }

// ---------------------------------------------------------------------------
// Called when PriorityForContactSetL method failed.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::PriorityForContactSetFailed( 
    TInt aError )
    {
	FUNC_LOG;
    iLastEvent = ECasEventIdle;
    iObserver->QueryActionsFailed( aError );
    }

// ---------------------------------------------------------------------------
// Execute action. Aynchronous method.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::ExecuteL( 
    TUid aActionUid, MFscContactSet& aContactSet,
    MFscContactActionPluginEngineObserver* aObserver )
    {
	FUNC_LOG;
    iObserver = aObserver;
        
    // Loop plugins
    TBool found = EFalse;
    for ( iCurrentActionPlugin = 0; 
          iCurrentActionPlugin < iActionPlugins.Count() && !found; 
          iCurrentActionPlugin++ )
        {
        // Loop actions
        const CArrayFix<TUid>* actionList = 
            iActionPlugins[ iCurrentActionPlugin ]->ActionList();
        
        TInt actionCount = 
            ( actionList != NULL) ? actionList->Count() : 0;
        for ( TInt j = 0; j < actionCount && !found; j++)
            {
            if ( ( *actionList )[j] == aActionUid )
                {
                found = ETrue;
                
                // Action found -> Execute
                iActionPlugins[ iCurrentActionPlugin ]->ExecuteL( 
                    aActionUid, aContactSet, this );
                    
                }
            }// Loop actions
        } // Loop plugins
        
    if ( !found )
        {
        ExecuteFailed( KErrNotFound );
        }
        
    }

// ---------------------------------------------------------------------------
// Cancels async method ExecuteL.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::CancelExecute()
    {
	FUNC_LOG;
    iActionPlugins[ iCurrentActionPlugin ]->CancelExecute();
    }

// ---------------------------------------------------------------------------
// Called when ExecuteL method is complete.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::ExecuteComplete()
    {
	FUNC_LOG;
    iObserver->ExecuteComplete();
    }
       
// ---------------------------------------------------------------------------
// Called when ExecuteL method failed.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::ExecuteFailed( TInt aError )
    {
	FUNC_LOG;
    iObserver->ExecuteFailed( aError );
    }

// ---------------------------------------------------------------------------
// Constructor.
// ---------------------------------------------------------------------------
//
CFscContactActionPluginEngine::CFscContactActionPluginEngine(
    CVPbkContactManager& aContactManager )
    : iContactManager( aContactManager ),
    iPluginParams( NULL ),
    iLastEvent( ECasEventIdle )
    {
	FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// Second phase constructor.
// ---------------------------------------------------------------------------
//
void CFscContactActionPluginEngine::ConstructL()
    {
	FUNC_LOG;
    iActionUtils = CFscActionUtils::NewL( iContactManager );
    iPluginParams = TFscContactActionPluginParams( iActionUtils );
    }   

