/*
* Copyright (c) 2002-2005 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:  Factory class for timers
*
*/


#include "wlanscanresultcache.h"
#include "am_debug.h"

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

// -----------------------------------------------------------------------------
// CWlanScanResultCache::NewL
// -----------------------------------------------------------------------------
//
CWlanScanResultCache* CWlanScanResultCache::NewL()
    {
    DEBUG( "CWlanScanResultCache::NewL()" );
    CWlanScanResultCache* self = new(ELeave)CWlanScanResultCache();
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::ConstructL
// -----------------------------------------------------------------------------
//
void CWlanScanResultCache::ConstructL()
    {
    DEBUG( "CWlanScanResultCache::ConstructL()" );

    iScanList = new(ELeave) ScanList();
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::CWlanScanResultCache
// -----------------------------------------------------------------------------
//
CWlanScanResultCache::CWlanScanResultCache() :
    iScanList( NULL ),
    iScanListTimeStamp( 0 ),
    iIapListTimeStamp( 0 )
    {
    DEBUG( "CWlanScanResultCache::CWlanScanResultCache()" );
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::~CWlanScanResultCache
// -----------------------------------------------------------------------------
//
CWlanScanResultCache::~CWlanScanResultCache()
    {
    DEBUG( "CWlanScanResultCache::~CWlanScanResultCache()" );

    delete iScanList;
    iIapList.Close(); 
    iAvailableIapList.Close();
    iAvailableNetworkList.Close();
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::UpdateScanList
// -----------------------------------------------------------------------------
//
void CWlanScanResultCache::UpdateScanList(
    ScanList* aScanList )
    {
    DEBUG( "CWlanScanResultCache::UpdateScanList()" );

    delete iScanList;
    iScanList = aScanList; // take ownership
    iScanListTimeStamp.HomeTime();
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::GetScanList
// -----------------------------------------------------------------------------
//
ScanList* CWlanScanResultCache::GetScanList(
	TUint aCacheLifetime )
    {
    DEBUG( "CWlanScanResultCache::GetScanList()" ); 

    TTime currentTime(0);
    currentTime.HomeTime();
    TTimeIntervalSeconds difference(0);

    TInt overflow = currentTime.SecondsFrom( iScanListTimeStamp, difference );
    if( difference.Int() > aCacheLifetime || difference.Int() < 0 || overflow )
        {
        DEBUG2( "CWlanScanResultCache::GetScanList() - results are too old, difference is %d, overflow is %d",
            difference.Int(), overflow );
        return NULL;
        }
    else
        {
        DEBUG1( "CWlanScanResultCache::GetScanList() - old results are still valid, difference is %u",
            difference.Int() );
        return iScanList;
        }
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::UpdateAvailableNetworksList
// -----------------------------------------------------------------------------
//
void CWlanScanResultCache::UpdateAvailableNetworksList(
    core_type_list_c<u32_t>& aIapIdList,
    RArray<TWlanAvailableNetwork>& aNetworkList,
    TBool& aNewIapsAvailable,
    TBool& aOldIapsLost )
    {
    DEBUG( "CWlanScanResultCache::UpdateAvailableNetworksList()" ); 

    DEBUG1( "CWlanScanResultCache::UpdateAvailableNetworksList() - %u IAP(s) available",
        aIapIdList.count() );
    DEBUG1( "CWlanScanResultCache::UpdateAvailableNetworksList() - %u IAP(s) previously available",
        iAvailableIapList.Count() ); 
    DEBUG1( "CWlanScanResultCache::UpdateAvailableNetworksList() - %u networks(s) available",
        aNetworkList.Count() );
    DEBUG1( "CWlanScanResultCache::UpdateAvailableNetworksList() - %u networks(s) previously available",
        iAvailableNetworkList.Count() );

    const TInt oldIapCount( iAvailableIapList.Count() );
    const TInt newNetworkCount( aNetworkList.Count() );
    const TInt oldNetworkCount( iAvailableNetworkList.Count() );
    aNewIapsAvailable = EFalse;
    aOldIapsLost = EFalse;
    TIdentityRelation<TWlanAvailableNetwork> isEqual( CWlanScanResultCache::IsNetworkEqual );

    // Iterate through previously available IAPs to find lost IAPs.    
    TInt idx( 0 );
    //while( idx < oldIapCount && !aOldIapsLost )
    while( idx < oldIapCount )
        {
        const TUint32* newId = aIapIdList.first();

        while( newId )
            {
            if( *newId == iAvailableIapList[idx] )
                {
                break;
                }

            newId = aIapIdList.next();
            }

        if( !newId )
            {
            DEBUG1( "CWlanScanResultCache::UpdateAvailableNetworksList() - old IAP %u has been lost",
                iAvailableIapList[idx] );
            aOldIapsLost = ETrue;
            }

        ++idx;
        }

    // Iterate through available IAPs to find new IAPs.    
    const TUint32* newId = aIapIdList.first();
    //while( newId && !aNewIapsAvailable )
    while( newId )
        {
        if ( iAvailableIapList.Find( *newId ) == KErrNotFound )
            {
            DEBUG1( "CWlanScanResultCache::UpdateAvailableNetworksList() - new IAP %u has been detected",
                *newId );
            aNewIapsAvailable = ETrue;
            }

        newId = aIapIdList.next();
        }

    // Iterate through previously available networks to find lost networks.
    idx = 0;
    //while ( idx < oldNetworkCount && !aOldIapsLost )
    while ( idx < oldNetworkCount )
        {
        if ( aNetworkList.Find( iAvailableNetworkList[idx], isEqual ) == KErrNotFound )
            {
            DEBUG1S( "CWlanScanResultCache::UpdateAvailableNetworksList() - old network has been lost, SSID ",
                iAvailableNetworkList[idx].ssid.Length(), iAvailableNetworkList[idx].ssid.Ptr() );
            aOldIapsLost = ETrue;
            }
        ++idx;
        }

    // Iterate through available networks to find new networks.        
    idx = 0;
    //while( idx < newNetworkCount && !aNewIapsAvailable )
    while( idx < newNetworkCount )
        {
        if ( iAvailableNetworkList.Find( aNetworkList[idx], isEqual ) == KErrNotFound )
            {
            DEBUG1S( "CWlanScanResultCache::UpdateAvailableNetworksList() - new network has been detected, SSID ",
                aNetworkList[idx].ssid.Length(), aNetworkList[idx].ssid.Ptr() );            
            aNewIapsAvailable = ETrue;
            }
        ++idx;
        }

    // Update the list
    iAvailableIapList.Reset();
    iAvailableNetworkList.Reset();
    iIapListTimeStamp.HomeTime();
        
    newId = aIapIdList.first();
    while( newId )
        {
        iAvailableIapList.Append( *newId );
        newId = aIapIdList.next();
        }
        
    idx = 0;
    while( idx < newNetworkCount )
        {
        iAvailableNetworkList.Append( aNetworkList[idx] );
        ++idx;
        }
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::AvailableIaps
// -----------------------------------------------------------------------------
//
RArray<TUint>* CWlanScanResultCache::AvailableIaps(
    RArray<TWlanLimitedIapData>& aIapList,
    TUint aCacheLifetime )
    {
    DEBUG( "CWlanScanResultCache::AvailableIaps()" );

    /**
     * Take a copy of the previous IAP list since GetIapDataList() will overwrite
     * the member variable.
     */
    RArray<TWlanLimitedIapData> iapList;
    for( TInt idx( 0 ); idx < iIapList.Count(); ++idx )
        {
        iapList.Append( iIapList[idx] );
        }

    TInt ret = GetIapDataList( aIapList );
    if ( ret != KErrNone )
        {
        DEBUG( "CWlanScanResultCache::AvailableIaps() - unable to read list of IAPs, assuming no cache" );
        iapList.Close();

        return NULL;
        }

    TTime currentTime(0);
    currentTime.HomeTime();
    TTimeIntervalSeconds difference(0);

    TInt overflow = currentTime.SecondsFrom( iIapListTimeStamp, difference );
    if( difference.Int() > aCacheLifetime || difference.Int() < 0 || overflow )
        {
        DEBUG2( "CWlanScanResultCache::AvailableIaps() - results are too old, difference is %d, overflow is %d",
            difference.Int(), overflow );
        iapList.Close();

        return NULL;
        }

    /**
     * Check whether the list of IAPs has change since last GetIapDataList().
     */
    if ( !IsIapListEqual( aIapList, iapList ) )
        {
        DEBUG( "CWlanScanResultCache::AvailableIaps() - cache time is still valid but IAPs have changed" );
        iapList.Close();

        return NULL;
        }

    DEBUG1( "CWlanScanResultCache::AvailableIaps() - old results are still valid, difference is %u",
        difference.Int() );
    iapList.Close();

    return &iAvailableIapList;
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::GetIapDataList
// -----------------------------------------------------------------------------
//
TInt CWlanScanResultCache::GetIapDataList(
    RArray<TWlanLimitedIapData>& aIapList )
    {
    DEBUG( "CWlanScanResultCache::GetIapDataList()" );

    aIapList.Reset();

    /**
     * Read all WLAN IAPs from commsdat.
     */
    CWLanSettings wlanSettings;    
    TInt ret = wlanSettings.Connect();
    if ( ret != KErrNone )
        {
        DEBUG1( "CWlanScanResultCache::GetIapDataList() - CWLanSettings::Connect() failed (%d)",
            ret );

        return ret;
        }

    RArray<SWlanIAPId> wlanIapIds;
    TRAP( ret, wlanSettings.GetIAPWlanServicesL( wlanIapIds ) );
    if( ret != KErrNone )
        {
        DEBUG1( "CWlanScanResultCache::GetIapDataList() - CWLanSettings::GetIAPWlanServicesL() failed (%d)",
            ret );
        wlanIapIds.Close();
        wlanSettings.Disconnect();

        return ret;
        }

    /**
     * Retrieve IAP data for each IAP id.
     */
    DEBUG1( "CWlanScanResultCache::GetIapDataList() - reading %u WLAN IAPs",
        wlanIapIds.Count() );

    for( TInt i( 0 ); i < wlanIapIds.Count(); i++ )
        {
        TWlanLimitedIapData iap;
        iap.iapId = wlanIapIds[i].iIAPId;
        iap.serviceId = wlanIapIds[i].iWLANRecordId;

        DEBUG1( "CWlanScanResultCache::GetIapDataList() - reading IAP %u",
            wlanIapIds[i].iIAPId );

        SWLANSettings iapData;
        ret = wlanSettings.GetWlanSettings(
            wlanIapIds[i].iWLANRecordId,
            iapData );
        if( ret != KErrNone )
            {
            DEBUG2( "CWlanScanResultCache::GetIapDataList() - reading of IAP %u failed (%d), ignoring",
                wlanIapIds[i].iIAPId, ret );
            }
        else if( iapData.SSID.Length() ) // filter out EasyWlan IAP
            {
            iap.ssid.Copy( iapData.SSID );
            iap.usedSsid.Copy( iapData.UsedSSID );
            iap.networkType = static_cast<EConnectionMode>( iapData.ConnectionMode );
            iap.securityMode = static_cast<EWlanSecurityMode>( iapData.SecurityMode );
            iap.isPskEnabled = iapData.EnableWpaPsk;
            iap.isHidden = iapData.ScanSSID;

            aIapList.Append( iap );
            }
        }

    UpdateIapList( aIapList );
    wlanIapIds.Close();
    wlanSettings.Disconnect();

    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::IsNetworkEqual
// -----------------------------------------------------------------------------
//
TBool CWlanScanResultCache::IsNetworkEqual(
    const TWlanAvailableNetwork& aFirst,
    const TWlanAvailableNetwork& aSecond )
    {
    if ( aFirst.ssid != aSecond.ssid ||
         aFirst.networkType != aSecond.networkType ||
         aFirst.securityMode != aSecond.securityMode )
        {
        return EFalse;
        }

    return ETrue;
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::IsIapListEqual
// -----------------------------------------------------------------------------
//
TBool CWlanScanResultCache::IsIapListEqual(
    const RArray<TWlanLimitedIapData>& aFirst,
    const RArray<TWlanLimitedIapData>& aSecond )
    {
    /**
     * If the amount of IAPs is different, it's an obvious fault.
     */
    if ( aFirst.Count() != aSecond.Count() )
        {
        DEBUG2( "CWlanScanResultCache::GetIapDataList() - false, amount of IAPs is different (%u vs %u)",
            aFirst.Count(), aSecond.Count() );

        return EFalse;
        }

    /**
     * Some of the settings might have changed.
     */
    for( TInt i( 0 ); i < aFirst.Count(); ++i )
        {
        TBool isFound( EFalse );
        for ( TInt j( 0 ); j < aSecond.Count() && !isFound; ++j )
            {
            if ( aFirst[i].iapId == aSecond[j].iapId )
                {
                isFound = ETrue;
                if ( aFirst[i].ssid != aSecond[j].ssid )
                    {
                    DEBUG1( "CWlanScanResultCache::GetIapDataList() - false, IAP %u has mismatching SSID",
                        aFirst[i].iapId );

                    return EFalse;
                    }
                else if ( aFirst[i].networkType != aSecond[j].networkType )
                    {
                    DEBUG1( "CWlanScanResultCache::GetIapDataList() - false, IAP %u has mismatching network mode",
                        aFirst[i].iapId );

                    return EFalse;
                    }
                else if ( aFirst[i].securityMode != aSecond[j].securityMode ||
                          aFirst[i].isPskEnabled != aSecond[j].isPskEnabled )
                    {
                    DEBUG1( "CWlanScanResultCache::GetIapDataList() - false, IAP %u has mismatching security mode",
                        aFirst[i].iapId );

                    return EFalse;
                    }
                else if ( aFirst[i].isHidden != aSecond[j].isHidden )
                    {
                    DEBUG1( "CWlanScanResultCache::GetIapDataList() - false, IAP %u has mismatching hidden setting",
                        aFirst[i].iapId );

                    return EFalse;
                    }
                }
            }
        if ( !isFound )
            {
            DEBUG1( "CWlanScanResultCache::GetIapDataList() - false, IAP %u not found in previous list",
                aFirst[i].iapId );

            return EFalse;
            }
        }

    DEBUG( "CWlanScanResultCache::GetIapDataList() - true, IAP lists are equal" );

    return ETrue;
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::UpdateIapList
// -----------------------------------------------------------------------------
//  
void CWlanScanResultCache::UpdateIapList(
    const RArray<TWlanLimitedIapData>& aIapDataList )
    {
    iIapList.Reset();
    TUint32 arraySize( 0 );

    for( TInt idx( 0 ); idx < aIapDataList.Count(); ++idx )
        {
        arraySize += sizeof( aIapDataList[idx] );
        iIapList.Append( aIapDataList[idx] );
        }
        
    DEBUG1( "CWlanScanResultCache::UpdateIapList() - total size of IAP list is %u bytes",
        arraySize );
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::CachedIapDataList
// -----------------------------------------------------------------------------
//
const RArray<TWlanLimitedIapData>& CWlanScanResultCache::CachedIapDataList() const
    {
    return iIapList;
    }

// -----------------------------------------------------------------------------
// CWlanScanResultCache::InvalidateAvailabilityCache
// -----------------------------------------------------------------------------
//
void CWlanScanResultCache::InvalidateAvailabilityCache()
    {
    iIapListTimeStamp = 0;
    }
