/*
* Copyright (c) 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:  
*
*/


// INCLUDE FILES
#include "loadgen_applications.h"
#include "loadgen_model.h"
#include "loadgen.hrh"
#include <loadgen.rsg>

#include <e32math.h>
#include "loadgen_traces.h"

_LIT(KThreadName, "Applications %d");

const TInt KDefaultStartDelay = 500;
const TInt KThreadNameLength = 64;
const TInt KDescriptorBufSize = 256;
const TInt KPriorityBufSize = 16;
const TInt KScanCodeStart = 32;
    
// ===================================== MEMBER FUNCTIONS =====================================

CAppLauncher* CAppLauncher::NewL( TApplicationsAttributes& aAttributes, TInt aReferenceNumber )
    {
    CAppLauncher* self = new(ELeave) CAppLauncher( aAttributes, aReferenceNumber );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;    
    }

// --------------------------------------------------------------------------------------------

CAppLauncher::~CAppLauncher()
    {
    Close();
    }

// --------------------------------------------------------------------------------------------

CAppLauncher::CAppLauncher(  
           TApplicationsAttributes& aAttributes, TInt aReferenceNumber ) : 
           iAttributes( aAttributes )
    {
    iAttributes.iId = aReferenceNumber;
    }

// --------------------------------------------------------------------------------------------

void CAppLauncher::ConstructL()
    {
    CLoadBase::ConstructL();
    
    iType = ELoadGenCmdNewLoadApplications;
    
    TBuf<KThreadNameLength> threadName;
    threadName.Format( KThreadName, iAttributes.iId );
    
    // create a thread
    User::LeaveIfError( iThread.Create( threadName, ThreadFunction, 
                                        KDefaultStackSize*2, KMinHeapSize, 
                                        KKilo * KMinHeapSize, (TAny*) &iAttributes ) );
    
    // set priority of the thread
    SetPriority();
    }

// --------------------------------------------------------------------------------------------

TInt CAppLauncher::ThreadFunction( TAny* aThreadArg )
    {
    CTrapCleanup* pC = CTrapCleanup::New();
    CActiveScheduler* pS = new CActiveScheduler;
    CActiveScheduler::Install( pS );

    // start generating load, pass pointer to arguments
    GenerateLoad( *( ( TApplicationsAttributes* ) aThreadArg ) );

    delete pS;
    delete pC;
    
    return KErrNone;
    }

// --------------------------------------------------------------------------------------------

void CAppLauncher::GenerateLoad( TApplicationsAttributes& aAttributes )
    {
    CAppLauncherManager* appLauncherManager = NULL;
    TRAPD( err, appLauncherManager = CAppLauncherManager::NewL( aAttributes ) );
    if ( err == KErrNone ) 
        {
        CActiveScheduler::Start();
        }
    delete appLauncherManager;
    }

// --------------------------------------------------------------------------------------------

void CAppLauncher::Resume()
    {
    CLoadBase::Resume();
    
    iThread.Resume();
    }

// --------------------------------------------------------------------------------------------

void CAppLauncher::Suspend()
    {
    CLoadBase::Suspend();
    
    iThread.Suspend();
    }

// --------------------------------------------------------------------------------------------

void CAppLauncher::SetPriority()
    {
    CLoadBase::SetPriority();
    
    iThread.SetPriority( CLoadGenModel::SettingItemToThreadPriority( iAttributes.iPriority ) );
    }
    
// --------------------------------------------------------------------------------------------

void CAppLauncher::Close()
    {
    CLoadBase::Close();
    
    if ( iThread.ExitReason() == 0 ) // check if the thread is still alive
        {
        // signal the thread that it needs to close
        iThread.RequestComplete( iAttributes.iDeathStatus, KErrCancel );

        // wait the thread to die
        TRequestStatus waiter;
        iThread.Logon( waiter );
        User::WaitForRequest( waiter );
        iThread.Close();
        }
    }
    
// --------------------------------------------------------------------------------------------

TPtrC CAppLauncher::Description()
    {
    TBuf<KDescriptorBufSize> buf;
    TBuf<KPriorityBufSize> prioBuf;
    CLoadGenModel::SettingItemToThreadDescription( iAttributes.iPriority, prioBuf );
    
    _LIT(KAppLauncherEntry, "[%d] Appls prio=%S max apps=%d heartbeat=%dms random=%d%%");
    buf.Format( KAppLauncherEntry, iAttributes.iId, &prioBuf, iAttributes.iMaxOpen, 
                iAttributes.iHeartBeat, iAttributes.iRandomVariance );
   
    return TPtrC( buf );
    }               

// --------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------

CAppLauncherManager* CAppLauncherManager::NewL( TApplicationsAttributes& aAttributes )
    {
    CAppLauncherManager* self = new(ELeave) CAppLauncherManager(aAttributes);
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------------------------

CAppLauncherManager::CAppLauncherManager( TApplicationsAttributes& aAttributes ) :
            CActive( EPriorityStandard ), iAttributes( aAttributes )
    {
    iAppEventType = EApplicationsLaunchApplication;
    }

// --------------------------------------------------------------------------------------------
    
CAppLauncherManager::~CAppLauncherManager()
    {
    if ( iPeriodicTimer )
        {
        iPeriodicTimer->Cancel();
        delete iPeriodicTimer;
        }
    delete iLauncherEngine;
    Cancel();
    
    iWsSession.Close();
    }

// --------------------------------------------------------------------------------------------
 
void CAppLauncherManager::ConstructL()
    {
    CActiveScheduler::Add( this );
    
    // create instance to application launcher
    iLauncherEngine = CLauncherEngine::NewL( iAttributes );
    
    // set the status as pending
    iStatus = KRequestPending;
    SetActive();
    
    // set the death status pointer point to the request status of this ao
    iAttributes.iDeathStatus = &iStatus;
    
    // init
    User::LeaveIfError( iWsSession.Connect() );
    
    // start timer    
    iPeriodicTimer = CPeriodic::NewL( CActive::EPriorityStandard );
    iPeriodicTimer->Start( KDefaultStartDelay, 
                           CLoadGenModel::MilliSecondsToMicroSeconds( 
                               iAttributes.iLaunchingInterval, iAttributes.iRandomVariance ),
                           TCallBack( PeriodicTimerCallBack, this ) );
    }
    
// --------------------------------------------------------------------------------------------
 
void CAppLauncherManager::RunL()
    {
    // request status has completed by the main thread meaning that we need to stop now
    CActiveScheduler::Stop();
    }

// --------------------------------------------------------------------------------------------
 
void CAppLauncherManager::DoCancel()
    {
    }

// --------------------------------------------------------------------------------------------

TInt CAppLauncherManager::PeriodicTimerCallBack( TAny* aAny )
    {
    CAppLauncherManager* self = static_cast<CAppLauncherManager*>( aAny );

    TRAPD( err, self->SimulateEventL() );
    if ( KErrNone != err )
        {
        return err;
        }
    return KErrNone;
    }
    
// --------------------------------------------------------------------------------------------
 
void CAppLauncherManager::SimulateEventL()
    {    
    // Randomly start new applications.
    // After maximum applications launched stop one application.
    // And after that start launching again.
    if ( iAppEventType == EApplicationsLaunchApplication && 
         iAttributes.iMaxOpen == iLauncherEngine->AppLaunchCounter() )
        {
        iAppEventType = EApplicationsCloseApplication;
        }
    if ( iAppEventType == EApplicationsCloseApplication && 
         iAttributes.iMaxOpen -1 == iLauncherEngine->AppLaunchCounter() )
        {
        iAppEventType = EApplicationsLaunchApplication;
        }
    
    // Launch or stop
    if ( iAppEventType == EApplicationsLaunchApplication )
        {
        iLauncherEngine->StartAppLaunchingL();
        }  
    else
        {
        iLauncherEngine->StopApplication( ETrue );
        }
    }
    
// --------------------------------------------------------------------------------------------

#include <avkon.hrh>
#include <e32std.h>
#include <w32std.h>
#include <apgtask.h>
#include <bautils.h>
#include <s32file.h>
#include <apgwgnam.h> 

// ---------------------------------------------------------------------------

CLauncherEngine* CLauncherEngine::NewL( TApplicationsAttributes& aAttributes )
    {
    CLauncherEngine* self = new(ELeave) CLauncherEngine( aAttributes );
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------

CLauncherEngine::CLauncherEngine( TApplicationsAttributes& aAttributes ) : 
                        iAttributes( aAttributes )
    {
    }

// ---------------------------------------------------------------------------

void CLauncherEngine::ConstructL()
    {
    LOGSTRING("LoadGen: CLauncherEngine::ConstructL");
    iAppKeyPressManager = CAppKeyPressManager::NewL( iAttributes );
    iAppLaunchCounter = 0;
    User::LeaveIfError( iTimer.CreateLocal() );

    User::LeaveIfError( iLs.Connect() );
    User::LeaveIfError( iWs.Connect() );

    }

// ---------------------------------------------------------------------------

CLauncherEngine::~CLauncherEngine()
    {
    LOGSTRING("LoadGen: CLauncherEngine::~CLauncherEngine");
    StopApplication( EFalse );
    iWs.Close();
    iLs.Close();
    iTimer.Close();
    delete iAppKeyPressManager;
    }

// ---------------------------------------------------------------------------

void CLauncherEngine::StartAppLaunchingL()
    {
    LOGSTRING("LoadGen: CLauncherEngine::StartAppLaunchingL");
    if ( iAppLaunchCounter == iAttributes.iMaxOpen )
        {
        return;
        }

    // start launching
    TBool launched( EFalse );
    while ( !launched )
        {
        launched = LaunchApplicationL();
        }
    }

// ---------------------------------------------------------------------------

TBool CLauncherEngine::LaunchApplicationL()
    {
    LOGSTRING("LoadGen: CLauncherEngine::LaunchApplicationL");
    TBool result( EFalse );
    // get the uid of the current app
    iCurrentAppUid = KNullUid;
    TApaAppInfo appInfo;
    
    // get application list size
    TInt appsListSize = iAttributes.iAppsArray->MdcaCount();
    // get random application number in the list
    TInt appTolaunch = CLoadGenModel::RandomNumber( 0,  appsListSize - 1 );
    // get file name of the application to be launched
    TFileName launchFileName = iAttributes.iAppsArray->MdcaPoint( appTolaunch );
    iLs.GetAllApps();
    while ( iLs.GetNextApp( appInfo ) == KErrNone )
        {
        // get file name in  current appInfo
        TFileName appFileName = appInfo.iFullName;
        // get AppUid
        if ( appFileName.CompareF( launchFileName ) == 0 )
            {
            iCurrentAppUid = appInfo.iUid;
            break;
            }
        }

    if ( iCurrentAppUid != KNullUid )
        {        
        // parse the filename
        TParse nameParser;
        nameParser.SetNoWild( iAttributes.iAppsArray->MdcaPoint( appTolaunch ), NULL, NULL );
        iCurrentAppNameAndExt.Copy( nameParser.Drive() );
        iCurrentAppNameAndExt.Append( nameParser.NameAndExt() );
           
        // do not try to launch these apps
        if ( iCurrentAppUid != KNullUid &&
              iAttributes.iAppsArray->MdcaPoint( appTolaunch).FindF(_L("\\loadgen.")) == KErrNotFound &&
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\Phone." ) ) == KErrNotFound &&
              iAttributes.iAppsArray->MdcaPoint( appTolaunch).FindF( _L("\\Startup.") ) == KErrNotFound &&  
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\SplashScreen.") ) == KErrNotFound &&  
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\cameraapp.") ) == KErrNotFound &&
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\eshell.") ) == KErrNotFound &&
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\ConnTest.") ) == KErrNotFound &&
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\Launcher.") ) == KErrNotFound &&
              
#ifdef __WINS__
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\DebugAgent.") ) == KErrNotFound &&
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\redirect.") ) == KErrNotFound &&              
#endif              
              iAttributes.iAppsArray->MdcaPoint( appTolaunch ).FindF( _L("\\VoiceRecorder.") ) == KErrNotFound )            
            {
        
            // check if the app is already running
            TApaTaskList taskList( iWs );
            TApaTask thisTask = taskList.FindApp( iCurrentAppUid );
            if ( !thisTask.Exists( ))
                {
                // check the program's capabilities
                TApaAppCapabilityBuf buf;
                iLs.GetAppCapability( buf, iCurrentAppUid );
                TApaAppCapability cap = buf();
        
                // if it's embeddable only, don't launch if setting is enabled
                // if it's hidden, don't launch if setting is enabled
                if ( cap.iEmbeddability != TApaAppCapability::EEmbeddableOnly &&
                     !cap.iAppIsHidden )
                    {
                    LOGSTRING2("launchFileName = %S", &launchFileName);
                    
                    DoLaunchApplicationL();
                    result = ETrue;
                    }            
                } 
            }
        }
    return result;
    }


// ---------------------------------------------------------------------------

void CLauncherEngine::DoLaunchApplicationL()
    {
    LOGSTRING("LoadGen: CLauncherEngine::DoLaunchApplicationL");

    // Find the task with uid3
    TApaTaskList tasklist( iWs );
    TApaTask task=tasklist.FindApp( iCurrentAppUid );

    if ( !task.Exists() )
        // Task doesn't exist, launch a new instance of an application
        {
        TApaAppInfo appInfo;
        User::LeaveIfError( iLs.GetAppInfo( appInfo, iCurrentAppUid ) );
        TApaAppCapabilityBuf capBuf;
        User::LeaveIfError( iLs.GetAppCapability( capBuf, iCurrentAppUid ) );
        TApaAppCapability& caps = capBuf();

        CApaCommandLine* cmdLine=CApaCommandLine::NewLC();
        cmdLine->SetExecutableNameL( appInfo.iFullName );

        // app is launched in background
        cmdLine->SetCommandL( EApaCommandBackground );       

        // start the app
        User::LeaveIfError( iLs.StartApp( *cmdLine ) );
        iAppKeyPressManager->AddNewApplicationUidToKeyEventsL( iCurrentAppUid );
        iAppLaunchCounter++;

        CleanupStack::PopAndDestroy( cmdLine );
        }
    }

// ---------------------------------------------------------------------------

void CLauncherEngine::StopApplication( TBool aRandomApplication )
    {
    LOGSTRING("LoadGen: CLauncherEngine::StopApplication");

    // remove key pressing instance
    TUid appToDelete = iAppKeyPressManager->KillApplication( aRandomApplication );
    if ( appToDelete != KNullUid )
        {
        TApaTaskList taskList( iWs );
        TApaTask thisTask = taskList.FindApp( appToDelete );
    
        if ( thisTask.Exists() )
            {
            // since the application is still open, let's close it
            thisTask.EndTask();
            }
        iAppLaunchCounter--;
        }
    
    // return if only one application requested
    if ( aRandomApplication )
        {
        return;
        }
    
    // remove all launched applications because load is requested to be closed
    while ( appToDelete != KNullUid )
        {
        appToDelete = iAppKeyPressManager->KillApplication( aRandomApplication );
            
        if ( appToDelete != KNullUid )
            {
            TApaTaskList taskList( iWs );
            TApaTask thisTask = taskList.FindApp( appToDelete );
        
            if ( thisTask.Exists() )
                {
                // since the application is still open, let's close it
                thisTask.EndTask();
                }
            iAppLaunchCounter--;
            }
        }
    }

// --------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------

CAppKeyPressManager* CAppKeyPressManager::NewL( TApplicationsAttributes& aAttributes )
    {
    LOGSTRING("LoadGen: CAppKeyPressManager::NewL");
    CAppKeyPressManager* self = new(ELeave) CAppKeyPressManager( aAttributes );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------------------------

CAppKeyPressManager::CAppKeyPressManager( TApplicationsAttributes& aAttributes ) :
                                            iAttributes( aAttributes )
    {
    LOGSTRING("LoadGen: CAppKeyPressManager::CAppKeyPressManager");
    }

// --------------------------------------------------------------------------------------------
    
CAppKeyPressManager::~CAppKeyPressManager()
    {
    LOGSTRING("LoadGen: CAppKeyPressManager::~CAppKeyPressManager");
    if ( iKeyPresses.Count() != 0 )
        {
        iKeyPresses.ResetAndDestroy();
        }

    iKeyPresses.Close();
    }

// --------------------------------------------------------------------------------------------
 
void CAppKeyPressManager::ConstructL()
    {
    LOGSTRING("LoadGen: CAppKeyPressManager::ConstructL");
    }
    
   
// --------------------------------------------------------------------------------------------
void CAppKeyPressManager::AddNewApplicationUidToKeyEventsL( TUid aUid )
    {
    LOGSTRING("LoadGen: CAppKeyPressManager::AddNewApplicationUidToKeyEventsL");
    CApplicationKeyPresses* applicationKP = CApplicationKeyPresses::NewL( aUid,
                                                                    iAttributes );
    CleanupStack::PushL( applicationKP );
    User::LeaveIfError( iKeyPresses.Append( applicationKP ) );
    CleanupStack::Pop( applicationKP );
    }

// --------------------------------------------------------------------------------------------
TUid CAppKeyPressManager::KillApplication( TBool aRandomApplication )
    {
    TUid applicationUid = KNullUid;
    TInt appToDelete = iKeyPresses.Count() - 1 ;
    // remove the newest application if not random 
    if ( aRandomApplication )
        {
         appToDelete = CLoadGenModel::RandomNumber( 0, iKeyPresses.Count() - 1 );
        }
    if ( iKeyPresses.Count() )
        {
        // get random application and delete it
        
        applicationUid = iKeyPresses[appToDelete]->ApplicationUid();
        delete iKeyPresses[appToDelete];
        iKeyPresses.Remove( appToDelete );
        }
    return applicationUid;
    }
// --------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------

CApplicationKeyPresses* CApplicationKeyPresses::NewL( TUid aUid, TApplicationsAttributes&  
        aAttributes )
    {
    LOGSTRING("LoadGen: CApplicationKeyPresses::NewL");
    CApplicationKeyPresses* self = new(ELeave) CApplicationKeyPresses( aUid, aAttributes );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------------------------

CApplicationKeyPresses::CApplicationKeyPresses( TUid aUid, 
                                                        TApplicationsAttributes& aAttributes ) :
                                                iUid( aUid ), iAttributes( aAttributes ) 
    {
    LOGSTRING("LoadGen: CApplicationKeyPresses::CApplicationKeyPresses");
    }

// --------------------------------------------------------------------------------------------
    
CApplicationKeyPresses::~CApplicationKeyPresses()
    {
    LOGSTRING("LoadGen: CApplicationKeyPresses::~CApplicationKeyPresses");
    if ( iPeriodicTimer )
        {
        iPeriodicTimer->Cancel();
        delete iPeriodicTimer;
        }
    
    iWsSession.Close();
    }

// --------------------------------------------------------------------------------------------
 
void CApplicationKeyPresses::ConstructL()
    {
    LOGSTRING("LoadGen: CApplicationKeyPresses::ConstructL");
    // init
    User::LeaveIfError( iWsSession.Connect() );
    
    // start timer    
    iPeriodicTimer = CPeriodic::NewL( CActive::EPriorityStandard );
    if ( iAttributes.iKeyPressType )
        {
        iPeriodicTimer->Start( KDefaultStartDelay,
                               CLoadGenModel::MilliSecondsToMicroSeconds( 
                                       iAttributes.iHeartBeat,
                                       iAttributes.iRandomVariance ),
                                TCallBack( PeriodicTimerCallBack, this ) );
        }
    }
    
// --------------------------------------------------------------------------------------------

TInt CApplicationKeyPresses::PeriodicTimerCallBack( TAny* aAny )
    {
    CApplicationKeyPresses* self = static_cast<CApplicationKeyPresses*>( aAny );
    self->SimulateKeyEvent();
    return KErrNone;
    }
    
// --------------------------------------------------------------------------------------------
 
void CApplicationKeyPresses::SimulateKeyEvent()
    {
    LOGSTRING("LoadGen: CApplicationKeyPresses::SimulateKeyEvent");
    // for arrow key events
    
    TInt wgId(0);
    CApaWindowGroupName::FindByAppUid( iUid, iWsSession, wgId );
    // generate a random arrow key event
    TWsEvent event;
    event.SetType( EEventKey );
    event.Key()->iCode = CLoadGenModel::RandomNumber( EKeyLeftArrow, EKeyDownArrow );
    event.Key()->iScanCode = event.Key()->iCode - KScanCodeStart;
    event.Key()->iModifiers = 0;
    event.Key()->iRepeats = 0;
    iWsSession.SendEventToWindowGroup( wgId, event );
    }

// End of File
