/*
* Copyright (c) 2002-2009 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:  ?Description
*
*/


// INCLUDE FILES
#include <cfcontextinterface.h>
#include <cfcontextsourcesetting.h>
#include <cfcontextsourcesettingarray.h>
#include <cfcontextsourcesettingparameter.h>
#include <cfkeyvaluepair.h>
#include <centralrepository.h>
#include <apgwgnam.h>
#include <applicationorientation.h>
#include <aknappui.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <vwsdefpartner.h>
#endif
#include "ApplicationStateSourcePlugin.h"
#include "ApplicationStateSourceContextDef.h"
#include "ContextFrameworkPrivateCRKeys.h"
#include "trace.h"

// CONSTANTS

#ifdef _DEBUG
// Panic codes
enum TPanicCode
    {
    EInvalidUid,
    EInvalidAttribute
    };
#endif

_LIT( KDefaultApplicationName,  "DefaultAppName" );
_LIT( KDefaultViewName,         "DefaultViewName" );
_LIT( KAttributeAppUid,         "AppUid" );
_LIT( KAttributeAppName,        "AppName" );
_LIT( KTagView,                 "View" );
_LIT( KAttributeUid,            "Uid" );
_LIT( KAttributeName,           "Name" );

const TUint KLimit = 0xFFFFFFFF;

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

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::CApplicationStateSourcePlugIn
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CApplicationStateSourcePlugIn::CApplicationStateSourcePlugIn( TContextSourceInitParams* aParams ):
    CCFContextSourcePlugIn( aParams ),
    iDefStateSetManyTimes( 0 )
    {
    FUNC_LOG;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::ConstructL
// Symbian second-phase constructor that may leave.
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::ConstructL()
    {
    FUNC_LOG;

    iDefaultAppName = KApplicationStateDefaultValue().AllocL();
    iDefaultViewName = KApplicationStateDefaultValue().AllocL();
    iContext = CCFContextObject::NewL();
    
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CApplicationStateSourcePlugIn* CApplicationStateSourcePlugIn::NewL(
    TContextSourceInitParams* aParams )
    {
    FUNC_LOG;

    CApplicationStateSourcePlugIn* self = CApplicationStateSourcePlugIn::NewLC(
        aParams );
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CApplicationStateSourcePlugIn* CApplicationStateSourcePlugIn::NewLC(
    TContextSourceInitParams* aParams )
    {
    FUNC_LOG;

    CApplicationStateSourcePlugIn* self = new( ELeave ) CApplicationStateSourcePlugIn(
        aParams );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

// Destructor
CApplicationStateSourcePlugIn::~CApplicationStateSourcePlugIn()
    {
    FUNC_LOG;

    if( iCRAppOrientationListener )
        {
        iCRAppOrientationListener->StopListening();
        delete iCRAppOrientationListener;
        }
    iUidOrientationList.Close();    
    delete iCRAppOrientation;
    iApplicationSettings.ResetAndDestroy();
    delete iSettings;
    delete iVws;
    delete iDefaultViewName;
    delete iDefaultAppName;
    delete iApplDefaultState;
    delete iContext;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::HandleSettingL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::HandleSettingL(
    CCFContextSourceSettingArray* aSettingList )
    {
    FUNC_LOG;

    // Take ownership from settings
    iSettings = aSettingList;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::DefineContextsL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::DefineContextsL()
    {
    FUNC_LOG;

    // Define contexts here
    _LIT_SECURITY_POLICY_C2(
        appStatePolicy, ECapabilitySwEvent, ECapabilityUserEnvironment );

    TInt err = iCF.DefineContext( KApplicationStateSource,
       KApplicationStateForegroundApplicationType,
       appStatePolicy );
    User::LeaveIfError( err );
    
    err = iCF.DefineContext( KApplicationStateSource,
       KApplicationStateForegroundApplicationViewType,
       appStatePolicy );
    User::LeaveIfError( err );

    err = iCF.DefineContext( KApplicationStateSource,
       KApplicationStateBackgroundApplicationType,
       appStatePolicy );
    User::LeaveIfError( err );
    
    err = iCF.DefineContext( KApplicationStateSource,
        KFgApplicationOrientation,
        KFgApplicationOrientationSec );
    User::LeaveIfError( err );
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::InitializeL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::InitializeL()
    {
    FUNC_LOG;

    iContext->SetSourceL( KApplicationStateSource );

    // Get default app orientation
    CRepository* cenRep = CRepository::NewL( KCRUidCFApplDefaultState );
    TInt err = cenRep->Get( KApplDefaultState, iDefaultFgAppOrientation );
    if( err != KErrNone )
        {
        ERROR( err, "Failed to get default fg app orientation" );
        iDefaultFgAppOrientation = 0;
        }
    delete cenRep;
    cenRep = NULL;

    // Create KCRUidDefaultAppOrientation repository for default app orientation
    iCRAppOrientation = CRepository::NewL( KCRUidDefaultAppOrientation ); 
    
    // Create KCRUidDefaultAppOrientation repository listener
    iCRAppOrientationListener = CCenRepNotifyHandler::NewL(
        *this, *iCRAppOrientation );
    iCRAppOrientationListener->StartListeningL();
        
    if( iSettings )
        {
        InitializeApplicationsFromXMLL();
        InitializeFgApplicationL();

        // Free memory allocated for settings since we don't need them anymore
        delete iSettings;
        iSettings = NULL;
        }

    iVws = CVwsSessionWrapper::NewL( *this );
    iVws->NotifyNextActivation();
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::InitializeApplicationsFromXMLL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::InitializeApplicationsFromXMLL()
    {
    FUNC_LOG;

     for( TInt i = 0; i < iSettings->Count(); ++i )
        {
        // Check attributes
        TBool defaultValueDefinitions = EFalse;
        TPtrC appUid( KNullDesC );
        TPtrC appName( KNullDesC );
        const RKeyValueArray& attributes = iSettings->Item( i ).Attributes();

        // Check application and view names (also default values)
        for( TInt j = 0; j < attributes.Count(); ++j )
            {
            TPtrC key( attributes[ j ]->Key() );
            TPtrC value( attributes[ j ]->Value() );

            if( key.CompareF( KAttributeAppUid ) == KErrNone )
                {
                appUid.Set( value );
                }
            else if( key.CompareF( KAttributeAppName ) == KErrNone )
                {
                appName.Set( value );
                }
            else if( key.CompareF( KDefaultApplicationName ) == KErrNone )
                {
                delete iDefaultAppName;
                iDefaultAppName = NULL;
                iDefaultAppName = value.AllocL();
                defaultValueDefinitions = ETrue;
                }
            else if( key.CompareF( KDefaultViewName ) == KErrNone )
                {
                delete iDefaultViewName;
                iDefaultViewName = NULL;
                iDefaultViewName = value.AllocL();
                defaultValueDefinitions = ETrue;
                }
            }

        if ( defaultValueDefinitions )
            {
            continue; // Default value definitions contain only attributes.
            }

        if ( appUid.Length() == 0 || appName.Length() == 0 )
            {
            ERROR_GEN( "Either AppUid or AppName tag (or both) not found, skipping" );
            continue; // Skip this definition.
            }

        TUint32 uid = 0;
        TInt err = ConvertToHex( appUid, uid );
        if( err != KErrNone )
            {
            ERROR_1( err, "Invalid uid [%S] for an application, skipping", &appUid );
            continue; // Skip this definition.
            }

        // below:                                  CLEANUP<< appStateSettings
        CCFApplicationStateSettings* appStateSettings
            = CCFApplicationStateSettings::NewLC( TUid::Uid( uid ), appName );

        // Check parameters
        const RSettingParameterArray& parameters = iSettings->Item( i ).Parameters();
        for( TInt j = 0; j < parameters.Count(); ++j )
            {
            if ( parameters[ j ]->Name() != KTagView )
                {
                continue; // At the moment, handle only view tags
                }

            TPtrC viewUid( KNullDesC );
            TPtrC viewName( KNullDesC );
            const RKeyValueArray& paramAttr = parameters[ j ]->Attributes();
            for( TInt k = 0; k < paramAttr.Count(); ++k )
                {
                TPtrC key( paramAttr[ k ]->Key() );
                TPtrC value( paramAttr[ k ]->Value() );

                // Check view uid
                if( key.CompareF( KAttributeUid ) == KErrNone )
                    {
                    viewUid.Set( value );
                    }
                // Check view name
                else if( key.CompareF( KAttributeName ) == KErrNone )
                    {
                    viewName.Set( value );
                    }
                }

            if ( viewUid.Length() == 0 || viewName.Length() == 0 )
                {
                ERROR_GEN( "Either View's Uid or Name tag (or both) not found, skipping" );
                continue; // Skip this definition.
                }

            uid = 0;
            TInt err = ConvertToHex( viewUid, uid );
            if( err != KErrNone )
                {
                ERROR_1( err, "Invalid view uid [%S], skipping", &viewUid );
                continue; // Skip this definition.
                }

            // below:                              CLEANUP<< viewUidName
            CCFUidNamePair* viewUidName
                = CCFUidNamePair::NewLC( TUid::Uid( uid ), viewName );
            appStateSettings->AddViewSettingsL( viewUidName );
            CleanupStack::Pop( viewUidName );   // CLEANUP>> viewUidName
            }

        iApplicationSettings.AppendL( appStateSettings );
        CleanupStack::Pop( appStateSettings );  // CLEANUP>> appStateSettings
        }
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::HandleViewEventL
// Maps view activations to contexts.
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::HandleViewEventL(
    const TVwsViewEvent& aEvent )
    {
    FUNC_LOG;

    // Renew listening
    iVws->NotifyNextActivation();

    // Handle the view server event
    DoHandleViewEventL( aEvent );

    // Log event members!
    INFO_1( "HandleViewEventL: eventType: [%d]", aEvent.iEventType );
    
    INFO_2( "HandleViewEventL: viewOne [appUid:%S] [viewUid%S]",
        &(const TDesC&)aEvent.iViewOneId.iAppUid.Name(),
        &(const TDesC&)aEvent.iViewOneId.iViewUid.Name() );
    
    INFO_2( "HandleViewEventL: viewTwo [appUid:%S] [viewUid%S]",
        &(const TDesC&)aEvent.iViewTwoId.iAppUid.Name(),
        &(const TDesC&)aEvent.iViewTwoId.iViewUid.Name() );
    }

//------------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::HandleNotifyGeneric
//------------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::HandleNotifyGeneric( TUint32 aId )
    {
    FUNC_LOG;

    // Get the new value from cenrep
    TInt val = 0;
    TInt err = iCRAppOrientation->Get( aId, val );
    if( err == KErrNone )
        {
        // New value found, check if the element is found from cache
        TInt index = FindByUid( TUid::Uid( aId ) );
        if( index != KErrNotFound )
            {
            // Update cache element
            TUidOrientationPair& pair = iUidOrientationList[index];
            pair.iOrientation = val;
            INFO_2( "Updated cache uid: [0x%x] to orientation: [%d]", pair.iUid.iUid, val );
            }
        else
            {
            // Add new cache element
            TUidOrientationPair pair( TUid::Uid( aId ), val );
            err = iUidOrientationList.Append( pair );
            if( err == KErrNone )
                {
                INFO_2( "Added cache uid: [0x%x], orientation: [%d]", pair.iUid.iUid, val );
                }
            else
                {
                ERROR_1( err, "Failed to add cache uid: [0x%x]", pair.iUid.iUid );
                }
            }
        }
    else
        {
        ERROR_1( err, "Failed to get new value from CR key: [0x%x]", aId );
        }
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::DoHandleViewEventL
// Maps view activations to contexts.
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::DoHandleViewEventL(
    const TVwsViewEvent& aEvent )
    {
    FUNC_LOG;

    TPtrC bgApp( *iDefaultAppName );
    TPtrC fgApp( *iDefaultAppName );
    TPtrC fgView( *iDefaultViewName );
    TBool bgFound( EFalse );
    TBool fgFound( EFalse );

    if ( aEvent.iViewTwoId.iAppUid == TUid::Null() )
        {
        // Previous foreground application was closed, get the stored name for
        // background application's name.
        bgFound = ETrue;
        bgApp.Set( iPreviousForegroundApplication );
        }

    for ( TInt i = 0; i < iApplicationSettings.Count(); ++i )
        {
        CCFApplicationStateSettings* appSettings = iApplicationSettings[ i ];
        if ( !fgFound && aEvent.iViewOneId.iAppUid == appSettings->Uid() )
            {
            fgApp.Set( appSettings->Name() );
            appSettings->GetViewName( aEvent.iViewOneId.iViewUid, fgView );
            fgFound = ETrue;
            }
        if ( !bgFound && aEvent.iViewTwoId.iAppUid == appSettings->Uid() )
            {
            bgApp.Set( appSettings->Name() );
            bgFound = ETrue;
            }
        if ( bgFound && fgFound )
            {
            break;
            }
        }

    TBool publishFgApp( EFalse );
    TBool publishFgView( EFalse );
    TBool publishBgApp( EFalse );

    if ( iPreviousForegroundApplication.Compare( fgApp ) != 0 )
        {
        publishFgApp = ETrue;
        }
    else if ( iPreviousForegroundView.Compare( fgView ) != 0 )
        {
        publishFgView = ETrue;
        }

    if ( bgApp != fgApp )
        {
        publishBgApp = ETrue;
        }

    iPreviousForegroundApplication.Set( fgApp ); // Store for next round
    iPreviousForegroundView.Set( fgView );

    // Publish foreground application and view contexts.
    RThread thread;

    if ( publishFgApp )
        {
        iContext->SetTypeL( KApplicationStateForegroundApplicationType );
        iContext->SetValueL( fgApp );
        iCF.PublishContext( *iContext, thread );
        }

    if ( publishFgApp || publishFgView )
        {
        iContext->SetTypeL( KApplicationStateForegroundApplicationViewType );
        iContext->SetValueL( fgView );
        iCF.PublishContext( *iContext, thread );
        }

    if ( publishBgApp )
        {
        iContext->SetTypeL( KApplicationStateBackgroundApplicationType );
        iContext->SetValueL( bgApp );
        iCF.PublishContext( *iContext, thread );
        }
    
    // Publish foreground application orientation context
    TPtrC fgAppOrientation( ResolveFgAppOrientation( aEvent.iViewOneId.iAppUid ) );
    if( fgAppOrientation.Length() )
        {
        // Fg app found
        if( fgAppOrientation.Compare( iPreviousOrientation ) != KErrNone )
            {
            // Update for next round
            iPreviousOrientation.Set( fgAppOrientation );
            
            // Fg app orientation changed, publish new context
            iContext->SetTypeL( KFgApplicationOrientation );
            iContext->SetValueL( fgAppOrientation );
            iCF.PublishContext( *iContext, thread );
            }
        }

    thread.Close();
    }

//------------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::ResolveFgAppOrientation
//------------------------------------------------------------------------------
//
TPtrC CApplicationStateSourcePlugIn::ResolveFgAppOrientation(
    const TUid& aUid )
    {
    FUNC_LOG;
    
    TPtrC fgAppOrientation( KNullDesC );
    TInt val = 0;
    
    // Check cache first
    TInt index = FindByUid( aUid );
    if( index != KErrNotFound )
        {
        // App found from cache
        val = iUidOrientationList[index].iOrientation;
        }
    else
        {
        // Get the SID from cenrep and update cache
        TInt err = iCRAppOrientation->Get( aUid.iUid, val );
        if( err != KErrNone )
            {
            INFO_1( "Application uid: [0x%x] not found, using default orientation",
                aUid.iUid );
            val = iDefaultFgAppOrientation;
            }
        
        // Update cache
        TUidOrientationPair pair( aUid, val );
        err = iUidOrientationList.Append( pair );
        if( err != KErrNone )
            {
            ERROR_2( err, "Failed to add uid: [0x%x], orientation: [%d] pair in cache",
                aUid.iUid, val );
            }
        }
    
    // Resolve context value
    if( val == CAknAppUiBase::EAppOrientationPortrait )
        {
        // Portrait
        fgAppOrientation.Set( TPtrC(
            KFgApplicationOrientationVal[EFgApplicationOrientationPortrait] ) );
        }
    else if( val == CAknAppUiBase::EAppOrientationLandscape )
        {
        // Landscape
        fgAppOrientation.Set( TPtrC(
            KFgApplicationOrientationVal[EFgApplicationOrientationLandscape] ) );
        }
    else
        {
        // Automatic
        fgAppOrientation.Set( TPtrC(
            KFgApplicationOrientationVal[EFgApplicationOrientationAutomatic] ) );
        }
    
    return fgAppOrientation;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::InitializeFgApplicationL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::InitializeFgApplicationL()
    {
    FUNC_LOG;

    RWsSession wsSession;
    User::LeaveIfError( wsSession.Connect() );
    CleanupClosePushL( wsSession );
    TUid appUid( TUid::Null() );

    TInt focScr( wsSession.GetFocusScreen() );
    TInt focWndGrp( wsSession.GetFocusWindowGroup() );
    TInt wndGrpPrio( wsSession.GetWindowGroupOrdinalPriority( focWndGrp ) );
    
    INFO_1( "CApplicationStateSourcePlugIn::focScr     = [%d]", focScr );
    INFO_1( "CApplicationStateSourcePlugIn::focWndGrp  = [%d]", focWndGrp );
    INFO_1( "CApplicationStateSourcePlugIn::wndGrpPrio = [%d]", wndGrpPrio );

    CApaWindowGroupName* wndGrpName = CApaWindowGroupName::NewL( wsSession );
    CleanupStack::PushL( wndGrpName );

    wndGrpName->ConstructFromWgIdL( focWndGrp );

    TPtrC appCaption = wndGrpName->Caption();
    TPtrC appName = wndGrpName->AppUid().Name();

    // if focScr == 0 it's not CoverUi
    // if focScr != 0 it's CoverUi

    // if wndGrpPrio == 0 it's S60 application

    if ( ( focScr != 0 ) || ( ( focScr == 0 ) && ( wndGrpPrio == 0 ) ) )
        {
        INFO( "CApplicationStateSourcePlugIn::It's CoverUi or S60 application" );
        // It is CoverUi
        // or
        // It is Not CoverUi and it is S60 application

        appUid = wndGrpName->AppUid();
        INFO_1( "appUid = [0x%x]", appUid.iUid );
        }
    else
        {
        // Default values will still be published
        TUid tmpAppUid( TUid::Null() );
        tmpAppUid = wndGrpName->AppUid();

        INFO( "CApplicationStateSourcePlugIn::It's neiher CoverUi nor S60 application" );
        INFO_1( "appUid = [0x%x]", tmpAppUid.iUid );
        }

    INFO_1( "appCaption = [%S]", &appCaption );
    INFO_1( "appName    = [%S]", &appName );

    CleanupStack::PopAndDestroy( wndGrpName );
    CleanupStack::PopAndDestroy( &wsSession );

    TPtrC bgApp( *iDefaultAppName );
    TPtrC fgApp( *iDefaultAppName );
    TPtrC fgView( *iDefaultViewName );

    if ( appUid != TUid::Null() )
        {
        for ( TInt i = 0; i < iApplicationSettings.Count(); ++i )
            {
            CCFApplicationStateSettings* appSettings = iApplicationSettings[ i ];
            if ( appUid == appSettings->Uid() )
                {
                fgApp.Set( appSettings->Name() );
                break;
                }
            }
        }

    iPreviousForegroundApplication.Set( fgApp ); // Store for next round
    iPreviousForegroundView.Set( fgView );

    RThread thread;

    // Publish foreground application
    iContext->SetTypeL( KApplicationStateForegroundApplicationType );
    iContext->SetValueL( fgApp );
    iCF.PublishContext( *iContext, thread );

    // Publish  foreground view
    iContext->SetTypeL( KApplicationStateForegroundApplicationViewType );
    iContext->SetValueL( fgView );
    iCF.PublishContext( *iContext, thread );

    // Publish background application
    iContext->SetTypeL( KApplicationStateBackgroundApplicationType );
    iContext->SetValueL( bgApp );
    iCF.PublishContext( *iContext, thread );
    
    // Publish foreground application orientation
    TPtrC fgAppOrientation( ResolveFgAppOrientation( appUid ) );
    iContext->SetTypeL( KFgApplicationOrientation );
    iContext->SetValueL( fgAppOrientation );
    iCF.PublishContext( *iContext, thread );
    
    // Store for next round
    iPreviousOrientation.Set( fgAppOrientation );

    thread.Close();
    }


//-----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::ConvertToHex
//-----------------------------------------------------------------------------
//
TInt CApplicationStateSourcePlugIn::ConvertToHex( const TDesC& aString,
    TUint32& aValue )
    {
    FUNC_LOG;

    TInt err = KErrNone;
    TLex lex( aString );

    // Get rid of '0x'
    const TInt skip = 2;
    lex.Inc( skip );
    if( lex.BoundedVal( aValue, EHex, KLimit ) != KErrNone )
        {
        err = KErrNotFound;
        }
    return err;
    }

//------------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::FindByUid
//------------------------------------------------------------------------------
//
TInt CApplicationStateSourcePlugIn::FindByUid( const TUid& aUid ) const
    {
    FUNC_LOG;
    
    TInt index = KErrNotFound;
    for( TInt i = 0; i < iUidOrientationList.Count(); i++ )
        {
        if( iUidOrientationList[i].iUid == aUid )
            {
            index = i;
            break;
            }
        }
    return index;
    }


	
