/*
* Copyright (c) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:  This class implements WLAN background scan logic.
*
*/

/*
* %version: 16 %
*/

#include <e32base.h>
#include <e32const.h> 

#include "wlanscanproviderinterface.h"
#include "awsinterface.h"
#include "awsenginebase.h"
#include "wlanbgscanawscommsinterface.h"
#include "wlanbgscanstates.h"
#include "wlanbgscan.h"
#include "wlandevicesettings.h" // default values in case invalid data is passed in NotifyChangedSettings
#include "am_debug.h"

/**
 * One day to be added to TTime.
 */
const TInt KAddOneDay = 1;

/**
 * Get hours from CenRep variable.
 */
const TInt KGetHours = 100;

/**
 * Zero seconds.
 */
const TInt KZeroSeconds = 0;

/**
 * Last minute of a day.
 */
const TInt KWlanBgScanTwentyThreeFiftyNineOclock = 2359;

/**
 * Maximun interval for background scan.
 */
const TInt KWlanBgScanMaxInterval = 1800;

#ifdef _DEBUG
/**
 * Formatting of date time debug string.
 */
_LIT( KWlanBgScanDateTimeFormat, "%F %*E %*N %Y %H:%T:%S" );
_LIT( KWlanBgScanDateTimeFormat2, "%H%T" );

/**
 * Maximun length for date time debug string.
 */
const TInt KWlanBgScanMaxDateTimeStrLen = 50;
#endif


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

// ---------------------------------------------------------------------------
// CWlanBgScan::CWlanBgScan
// ---------------------------------------------------------------------------
//
CWlanBgScan::CWlanBgScan( MWlanScanResultProvider& aProvider, MWlanTimerServices& aTimerServices ) :
    CWlanBgScanStates( *this, aProvider, aTimerServices ),
    iProvider ( aProvider ),
    iAwsComms( NULL ),
    iAwsOk( EFalse ),
    iCurrentPsmServerMode( 0 )
    {
    DEBUG( "CWlanBgScan::CWlanBgScan()" );
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::ConstructL
// ---------------------------------------------------------------------------
//
void CWlanBgScan::ConstructL()
    {
    DEBUG( "CWlanBgScan::ConstructL()" );
    
    // create AWS comms interface
    TRAPD( err, iAwsComms = CWlanBgScanAwsCommsFactory::InstanceL( *this ) );
    if( err != KErrNone )
        {
        DEBUG1( "CWlanBgScan::ConstructL() - AWS comms creation failed with code %i", err );

        delete iAwsComms;
        iAwsComms = NULL;
        }
    else
        {
        DEBUG( "CWlanBgScan::ConstructL() - AWS comms creation successful" );
        }
    
    CWlanBgScanStates::ConstructL();
            
    DEBUG( "CWlanBgScan::ConstructL() - done" );    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::NewL
// ---------------------------------------------------------------------------
//
CWlanBgScan* CWlanBgScan::NewL( MWlanScanResultProvider& aProvider, MWlanTimerServices& aTimerServices )
    {
    DEBUG( "CWlanBgScan::NewL()" );
    CWlanBgScan* self = new ( ELeave ) CWlanBgScan( aProvider, aTimerServices );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::~CWlanBgScan
// ---------------------------------------------------------------------------
//
CWlanBgScan::~CWlanBgScan()
    {
    DEBUG1( "CWlanBgScan::~CWlanBgScan() - deallocating iAwsComms @ 0x%08X", iAwsComms );
    delete iAwsComms;
    iAwsComms = NULL;
    }

// ---------------------------------------------------------------------------
// From class MWlanBgScanProvider.
// CWlanBgScan::ScanComplete
// ---------------------------------------------------------------------------
//
void CWlanBgScan::ScanComplete()
    {
    DEBUG1( "CWlanBgScan::ScanComplete() - current interval %us", GetBgInterval() );

    if ( GetBgInterval() != KWlanBgScanIntervalNever )
        {
        DEBUG1( "CWlanBgScan::ScanComplete() - issue a new request with %us as expiry", GetBgInterval() );
        iProvider.Scan( GetBgInterval() );
        }
    }

// ---------------------------------------------------------------------------
// From class MWlanBgScanProvider.
// CWlanBgScan::WlanStateChanged
// ---------------------------------------------------------------------------
//
void CWlanBgScan::WlanStateChanged( const MWlanBgScanProvider::TWlanBgScanWlanState &aState )
    {
    DEBUG2( "CWlanBgScan::WlanStateChanged() - old state: %u, new state: %u", iWlanState, aState );
    
    // store state
    iWlanState = aState;

    RefreshUsedInterval();

    }

// ---------------------------------------------------------------------------
// From class MWlanBgScanProvider.
// CWlanBgScan::IsBgScanEnabled
// ---------------------------------------------------------------------------
//
TBool CWlanBgScan::IsBgScanEnabled()
    {
    // If ( interval != never )                              -> Return True
    // Otherwise                                             -> return False
    DEBUG1( "CWlanBgScan::IsBgScanEnabled() - returning %u", 
           ( GetBgInterval() != KWlanBgScanIntervalNever ) ? 1 : 0 );
        
    return ( GetBgInterval() != KWlanBgScanIntervalNever );
    }

// ---------------------------------------------------------------------------
// From class MWlanBgScanProvider.
// CWlanBgScan::NotifyChangedSettings
// ---------------------------------------------------------------------------
//
void CWlanBgScan::NotifyChangedSettings( MWlanBgScanProvider::TWlanBgScanSettings& aSettings )
    {
    DEBUG6( "CWlanBgScan::NotifyChangedSettings( interval: %u, psm srv mode: %u, peak start: %04u, peak end: %04u, peak: %u, off-peak: %u )",
            aSettings.backgroundScanInterval,
            aSettings.psmServerMode,
            aSettings.bgScanPeakStartTime,
            aSettings.bgScanPeakEndTime,
            aSettings.bgScanIntervalPeak,
            aSettings.bgScanIntervalOffPeak);
    
    // validate settings and use default values if needed
    MWlanBgScanProvider::TWlanBgScanSettings settingsToUse;
    CheckSettings( settingsToUse, aSettings ); // Note: Any PSM server mode value is allowed.
    
    // store settings for later use
    iBgScanSettings = settingsToUse;
    
    IntervalChanged();
    
    if( iCurrentPsmServerMode != iBgScanSettings.psmServerMode )
        {
        if( AwsPresence() == CWlanBgScan::EAwsPresent )
            {
            DEBUG( "CWlanBgScan::NotifyChangedSettings() - sending power mode command to AWS" );
            MWlanBgScanAwsComms::TAwsMessage msg = { MWlanBgScanAwsComms::ESetPowerSaveMode, iBgScanSettings.psmServerMode };
            iAwsComms->SendOrQueueAwsCommand( msg );
            }
        
        // remember current psm server mode
        iCurrentPsmServerMode = iBgScanSettings.psmServerMode;
        }
    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::CheckSettings
// ---------------------------------------------------------------------------
//
void CWlanBgScan::CheckSettings(
        MWlanBgScanProvider::TWlanBgScanSettings& aSettingsToUse,                   // [OUT]
        const MWlanBgScanProvider::TWlanBgScanSettings& aProposedSettings ) const   // [IN]
    {
    aSettingsToUse = aProposedSettings;

    if( aSettingsToUse.bgScanPeakStartTime > KWlanBgScanTwentyThreeFiftyNineOclock ||
        aSettingsToUse.bgScanPeakEndTime > KWlanBgScanTwentyThreeFiftyNineOclock )
        {
        DEBUG2( "CWlanBgScan::CheckSettings() - peak start or end invalid, using default values (start %u, end %u)",
                KWlanDefaultBgScanPeakPeriodStart,
                KWlanDefaultBgScanPeakPeriodEnd );
        aSettingsToUse.bgScanPeakStartTime = KWlanDefaultBgScanPeakPeriodStart;
        aSettingsToUse.bgScanPeakEndTime = KWlanDefaultBgScanPeakPeriodEnd;
        }
    if( aSettingsToUse.bgScanIntervalPeak > KWlanBgScanMaxInterval )
        {
        DEBUG1( "CWlanBgScan::CheckSettings() - peak interval invalid, using default value %u",
                KWlanDefaultBgScanIntervalPeakPeriod );
        aSettingsToUse.bgScanIntervalPeak = KWlanDefaultBgScanIntervalPeakPeriod;
        }
    if( aSettingsToUse.bgScanIntervalOffPeak > KWlanBgScanMaxInterval )
        {
        DEBUG1( "CWlanBgScan::CheckSettings() - off-peak interval invalid, using default value %u",
                KWlanDefaultBgScanIntervalOffPeakPeriod );
        aSettingsToUse.bgScanIntervalOffPeak = KWlanDefaultBgScanIntervalOffPeakPeriod;
        }    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::IsIntervalChangeNeeded
// ---------------------------------------------------------------------------
//
TBool CWlanBgScan::IsIntervalChangeNeeded()
    {
    TBool ret( ETrue );
    
    // no need to change interval if both peak and off-peak intervals are the same
    if( iBgScanSettings.bgScanPeakStartTime == iBgScanSettings.bgScanPeakEndTime )
        {
        ret = EFalse;
        }
    
    DEBUG1( "CWlanBgScan::IsIntervalChangeNeeded() - returning %d", ret );
    
    return ret;    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::ScheduleAutoIntervalChange
// ---------------------------------------------------------------------------
//
void CWlanBgScan::ScheduleAutoIntervalChange()
    {
    DEBUG( "CWlanBgScan::ScheduleAutoIntervalChange()" );
    
    iTimerServices.StopTimer( iIntervalChangeRequestId );

    if( IsIntervalChangeNeeded() )
        {
        TTime intervalChangeAt = AutoIntervalChangeAt();
        
        if( KErrNone != iTimerServices.StartTimer( iIntervalChangeRequestId, intervalChangeAt, *this ) )
            {
            DEBUG( "CWlanBgScan::ScheduleAutoIntervalChange() - error: requesting timeout failed, peak <-> off-peak interval will not be changed" );
            }
        }
    else
        {
        DEBUG( "CWlanBgScan::ScheduleAutoIntervalChange() - peak <-> off-peak interval change not needed" );
        }
    
    DEBUG( "CWlanBgScan::ScheduleAutoIntervalChange() - returning" );
    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::AutoIntervalChangeAt
// ---------------------------------------------------------------------------
//
TTime CWlanBgScan::AutoIntervalChangeAt()
    {
    TTime currentTime;
    currentTime.HomeTime();
    TDateTime change_time( currentTime.DateTime() );
    
#ifdef _DEBUG
    change_time = currentTime.DateTime();
    TBuf<KWlanBgScanMaxDateTimeStrLen> timeNow;
    TRAP_IGNORE( currentTime.FormatL( timeNow, KWlanBgScanDateTimeFormat ) );
    DEBUG1( "CWlanBgScan::AutoIntervalChangeAt() - time now: %S", &timeNow );
#endif
    
    switch( TimeRelationToRange( currentTime, iBgScanSettings.bgScanPeakStartTime, iBgScanSettings.bgScanPeakEndTime ) )
        {
        case ESmaller:
            {
            change_time.SetHour( iBgScanSettings.bgScanPeakStartTime / KGetHours );
            change_time.SetMinute( iBgScanSettings.bgScanPeakStartTime % KGetHours );
            change_time.SetSecond( KZeroSeconds );
            currentTime = change_time;
            break;
            }
        case EInsideRange:
            {
            change_time.SetHour( iBgScanSettings.bgScanPeakEndTime / KGetHours );
            change_time.SetMinute( iBgScanSettings.bgScanPeakEndTime % KGetHours );
            change_time.SetSecond( KZeroSeconds );
            currentTime = change_time;
            if( iBgScanSettings.bgScanPeakStartTime > iBgScanSettings.bgScanPeakEndTime )
                {
                DEBUG( "CWlanBgScan::AutoIntervalChangeAt() - peak end happens tomorrow" );
                currentTime += TTimeIntervalDays( KAddOneDay );
                }
            else
                {
                DEBUG( "CWlanBgScan::AutoIntervalChangeAt() - peak end happens today" );
                }
            break;
            }
        case EGreater:
            {
            change_time.SetHour( iBgScanSettings.bgScanPeakStartTime / KGetHours );
            change_time.SetMinute( iBgScanSettings.bgScanPeakEndTime % KGetHours );
            change_time.SetSecond( KZeroSeconds );
            currentTime = change_time;
            currentTime += TTimeIntervalDays( KAddOneDay );
            break;
            }
        }
    
#ifdef _DEBUG
    change_time = currentTime.DateTime();
    TBuf<KWlanBgScanMaxDateTimeStrLen> dbgString;
    TRAP_IGNORE( currentTime.FormatL( dbgString, KWlanBgScanDateTimeFormat ) );
    DEBUG1( "CWlanBgScan::AutoIntervalChangeAt() - interval change to occur: %S", &dbgString );
#endif
    
    return currentTime;
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::TimeRelationToRange
// ---------------------------------------------------------------------------
//
CWlanBgScan::TRelation CWlanBgScan::TimeRelationToRange( const TTime& aTime, TUint aRangeStart, TUint aRangeEnd ) const
    {
#ifdef _DEBUG
    TBuf<KWlanBgScanMaxDateTimeStrLen> dbgString;
    TRAP_IGNORE( aTime.FormatL( dbgString, KWlanBgScanDateTimeFormat2 ) );
    DEBUG1( "CWlanBgScan::TimeRelationToRange() - time:  %S", &dbgString );
#endif
        
    TDateTime dateTime( aTime.DateTime() );
    
    TUint timeToCheck = ( dateTime.Hour() * KGetHours ) + dateTime.Minute();

    DEBUG2( "CWlanBgScan::TimeRelationToRange() - range: %04u - %04u", aRangeStart, aRangeEnd );

    CWlanBgScan::TRelation relation( ESmaller );
    
    if( aRangeStart == aRangeEnd )
        {
        DEBUG( "CWlanBgScan::TimeRelationToRange() - returning: EGreater" );
        relation = EGreater;
        }
    else if( aRangeStart > aRangeEnd )
        {
        DEBUG( "CWlanBgScan::TimeRelationToRange() - range crosses the midnight" );
        /**
         * As range crosses midnight, there is no way for the relation to be ESmaller.
         */
        if( timeToCheck < aRangeEnd || timeToCheck >= aRangeStart )
            {
            DEBUG( "CWlanBgScan::TimeRelationToRange() - returning: EInsideRange" );
            relation = EInsideRange;
            }
        else
            {
            DEBUG( "CWlanBgScan::TimeRelationToRange() - returning: EGreater" );
            relation = EGreater;
            }
        }
    else
        {
        if( timeToCheck < aRangeStart )
            {
            DEBUG( "CWlanBgScan::TimeRelationToRange() - returning: ESmaller" );
            relation = ESmaller;
            }
        else if( timeToCheck >= aRangeStart && timeToCheck < aRangeEnd )
            {
            DEBUG( "CWlanBgScan::TimeRelationToRange() - returning: EInsideRange" );
            relation = EInsideRange;
            }
        else
            {
            DEBUG( "CWlanBgScan::TimeRelationToRange() - returning: EGreater" );
            relation = EGreater;
            }
        }
    
    return relation;
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::CurrentAutoInterval
// ---------------------------------------------------------------------------
//
TUint32 CWlanBgScan::CurrentAutoInterval()
    {
    TUint32 interval( 0 );
    TTime currentTime;
    currentTime.HomeTime();

    if( TimeRelationToRange( currentTime, iBgScanSettings.bgScanPeakStartTime, iBgScanSettings.bgScanPeakEndTime ) == CWlanBgScan::EInsideRange )
        {
        interval = iBgScanSettings.bgScanIntervalPeak;
        }
    else
        {       
        interval = iBgScanSettings.bgScanIntervalOffPeak;
        }
    
    DEBUG1( "CWlanBgScan::CurrentAutoInterval() - current interval: %u", interval );

    return interval;
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::StartAggressiveBgScan
// ---------------------------------------------------------------------------
//
void CWlanBgScan::StartAggressiveBgScan( TUint32& aInterval, TUint32& aTimeout )
    {
    CWlanBgScanStates::StartAggressiveBgScan( aInterval, aTimeout );
    }
    
// ---------------------------------------------------------------------------
// CWlanBgScan::OnTimeout
// ---------------------------------------------------------------------------
//
void CWlanBgScan::OnTimeout()
    {
    DEBUG( "CWlanBgScan::OnTimeout()" );
    
    NextState( CWlanBgScanStates::EBgScanEventIntervalChanged );
    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::DoSetInterval
// ---------------------------------------------------------------------------
//
void CWlanBgScan::DoSetInterval( TUint32 aNewInterval )
    {
    DEBUG1( "CWlanBgScan::DoSetInterval( aNewInterval: %u )", aNewInterval );
        
    SetAutoInterval( aNewInterval );
    DEBUG( "CWlanBgScan::DoSetInterval() - interval stored, giving event to state machine" );
    
    NextState( CWlanBgScanStates::EBgScanEventAwsIntervalChanged );

    }

// ---------------------------------------------------------------------------
// CWlanBgScan::AwsPresence
// --------------------------------------------------------------------------
//
CWlanBgScan::TAwsPresence CWlanBgScan::AwsPresence()
    {
    CWlanBgScan::TAwsPresence ret( CWlanBgScan::EAwsPresent );
    
    // If ( ( iAwsOk == EFalse ) And ( iAwsComms == NULL ) )  -> Return EAwsNotPresent
    // If ( ( iAwsOk == EFalse ) And ( iAwsComms != NULL ) )  -> Return EAwsStarting
    // Otherwise                                              -> return EAwsPresent
    
    if( iAwsOk == EFalse && iAwsComms == NULL )
        {
        ret = CWlanBgScan::EAwsNotPresent;
        }
    if( iAwsOk == EFalse && iAwsComms != NULL )
        {
        ret = CWlanBgScan::EAwsStarting;
        }
    
    DEBUG1( "CWlanBgScan::AwsStatus() - returning %i", ret );
    
    return ret;
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::AwsStartupComplete
// --------------------------------------------------------------------------
//
void CWlanBgScan::AwsStartupComplete( TInt aStatus )
    {
    DEBUG1( "CWlanBgScan::AwsStartupComplete( aStatus: %d )", aStatus );
    
    SetAwsStartupStatus( aStatus );

    if( aStatus == KErrNone )
        {
        // AWS is ok
        iAwsOk = ETrue;
        }
#ifdef _DEBUG
    else
        {
        DEBUG( "CWlanBgScan::AwsStartupComplete() - error: AWS startup completed with error status, AWS not in use" );
        }
#endif
    
    NextState( CWlanBgScanStates::EBgScanEventAwsStartupComplete );
    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::AwsCommandComplete
// --------------------------------------------------------------------------
//
void CWlanBgScan::AwsCommandComplete( MWlanBgScanAwsComms::TAwsCommand& aCommand, TInt aStatus )
    {
    DEBUG2( "CWlanBgScan::AwsCommandComplete( aCommand: %d, aStatus: %d )", aCommand, aStatus );
    
    SetAwsCmdStatus( aCommand, aStatus );

    NextState( CWlanBgScanStates::EBgScanEventAwsCmdComplete );
    
    }

// ---------------------------------------------------------------------------
// CWlanBgScan::AwsCommand
// --------------------------------------------------------------------------
//
void CWlanBgScan::AwsCommand( MWlanBgScanAwsComms::TAwsMessage& aCommand )
    {
    DEBUG( "CWlanBgScan::AwsCommand() - calling SendOrQueueAwsCommand()" );
    iAwsComms->SendOrQueueAwsCommand( aCommand );
    
    }

