/*
* Copyright (c) 2006 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:  Bluetooth Engine API for managing device settings.
*
*/



#include <centralrepository.h>
#include <featmgr.h>
#include <utf.h>

#include "btengsettings.h"
#include "btengprivatecrkeys.h"
#include "btengsettingsnotify.h"
#include "btengclient.h"
#include "debug.h"


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

// ---------------------------------------------------------------------------
// C++ default constructor
// ---------------------------------------------------------------------------
//
CBTEngSettings::CBTEngSettings( MBTEngSettingsObserver* aObserver )
:   iObserver( aObserver )
    {
    }


// ---------------------------------------------------------------------------
// Symbian 2nd-phase constructor
// ---------------------------------------------------------------------------
//
void CBTEngSettings::ConstructL()
    {
    TRACE_FUNC_ENTRY
        // Check if BT is supported at all
    FeatureManager::InitializeLibL();
    TBool btSupported = FeatureManager::FeatureSupported( KFeatureIdBt );
    FeatureManager::UnInitializeLib();
    if( !btSupported )
        {
        TRACE_INFO( ( _L( "[BTENGSETTINGS]\t ConstructL: BT not supported!!" ) ) )
        User::Leave( KErrNotSupported );
        }
    if( iObserver )
        {
        iSettingsWatcher = CBTEngSettingsNotify::NewL( iObserver );
        }
    TRACE_FUNC_EXIT
    }


// ---------------------------------------------------------------------------
// NewL
// ---------------------------------------------------------------------------
//
EXPORT_C CBTEngSettings* CBTEngSettings::NewL( MBTEngSettingsObserver* aObserver )
    {
    CBTEngSettings* self = CBTEngSettings::NewLC( aObserver );
    CleanupStack::Pop( self );
    return self;
    }


// ---------------------------------------------------------------------------
// NewLC
// ---------------------------------------------------------------------------
//
EXPORT_C CBTEngSettings* CBTEngSettings::NewLC( MBTEngSettingsObserver* aObserver )
    {
    CBTEngSettings* self = new( ELeave ) CBTEngSettings( aObserver );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }


// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CBTEngSettings::~CBTEngSettings()
    {
    TRACE_FUNC_ENTRY
    delete iSettingsWatcher;
    }


// ---------------------------------------------------------------------------
// Gets the current BT hardware power state 
// (on or off) from central repository.
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::GetPowerState( TBTPowerStateValue& aState )
    {
    TRACE_FUNC_ENTRY
    aState = EBTPowerOff;
    TRAPD( err, GetCenRepKeyL( KCRUidBluetoothPowerState, KBTPowerState, 
                               (TInt&) aState, EBTPowerOff, EBTPowerOn ) );
    return err;
    }


// ---------------------------------------------------------------------------
// Sets the Bluetooth hardware power state.
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::SetPowerState( TBTPowerStateValue aState )
    {
    TRACE_FUNC_ENTRY
    if( aState != EBTPowerOff && aState != EBTPowerOn )
        {
        return KErrArgument;
        }
    RBTEng bteng;
    TInt err = bteng.Connect();
    if( !err )
        {
        err = bteng.SetPowerState( aState, EFalse );
        bteng.Close();
        }
    TRACE_FUNC_EXIT
    return err;
    }


// ---------------------------------------------------------------------------
// Gets the current Bluetooth hardware visibility mode 
// (visible or hidden) from central repository.
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::GetVisibilityMode( TBTVisibilityMode& aMode )
    {
    TRACE_FUNC_ENTRY
    aMode = EBTVisibilityModeGeneral;
    TRAPD( err, GetCenRepKeyL( KCRUidBTEngPrivateSettings, KBTDiscoverable, 
                               (TInt&) aMode, EBTVisibilityModeHidden, 
                               EBTVisibilityModeGeneral, 
                               EBTVisibilityModeTemporary, 
                               EBTVisibilityModeTemporary ) );
    return err;
    }


// ---------------------------------------------------------------------------
// Sets the Bluetooth hardware visibility mode. The visible mode can also 
// be temporary visible, specified by the aTime parameter (time in seconds).
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::SetVisibilityMode( TBTVisibilityMode aMode, TInt aTime )
    {
    TRACE_FUNC_ENTRY
    if( aMode != EBTVisibilityModeHidden && aMode != EBTVisibilityModeGeneral && 
        aMode != EBTVisibilityModeTemporary )
        {
        return KErrArgument;
        }
    RBTEng bteng;
    TInt err = bteng.Connect();
    if( !err )
        {
        err = bteng.SetVisibilityMode( aMode, aTime );
        bteng.Close();
        }

    TRACE_FUNC_EXIT
    return err;
    }


// ---------------------------------------------------------------------------
// Gets the Bluetooth friendly name the Bluetooth stack.
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::GetLocalName( TDes& aName )
    {
    TRACE_FUNC_ENTRY
    aName.Zero();
    TBool nameStatus = EFalse;
    TBuf<KHCILocalDeviceNameMaxLength> tmpName;
    
    TInt err = GetLocalNameModified( nameStatus );
    
    if( err || nameStatus == EBTLocalNameDefault )
        {
        return err;
        }

    err = RProperty::Get( KPropertyUidBluetoothCategory, 
                           KPropertyKeyBluetoothGetDeviceName, aName );
    if( !err )
        {
        err = RProperty::Get( KPropertyUidBluetoothCategory, 
                               KPropertyKeyBluetoothSetDeviceName, tmpName );
        if ( tmpName.Compare( aName ) )
            {
                // The name has not yet been updated. Use the new one.
            aName.Copy( tmpName );
            }
        }
        
    if( err || !aName.Length() )
        {
            // An error occured, try still registry
        aName.Zero();
        err = HandleBTRegistryNameSetting( const_cast<TDes&>( aName ) );
        TRACE_INFO( ( _L( "[BTENGSETTINGS]\t registry set result: %d"), err) );
        }                            
                                     	        	                               
    TRACE_INFO( ( _L( "[BTENGSETTINGS]\t GetLocalName - result %d, name: %S" ),
                        err, &aName ) )
    return err;
    }

// ---------------------------------------------------------------------------
// Sets the Bluetooth friendly name.
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::SetLocalName( const TDes& aName )
    {
    TRACE_FUNC_ENTRY
    TRACE_FUNC_ARG( ( _L( "aName lenght: %d" ), aName.Length() ) )
    if( aName.Length() == 0 || aName.Length() > KMaxBluetoothNameLen )
        {
            // Zero-length name means that the name has not been set;
            // such reset should not be done through BTEngSettings.
        return KErrArgument;
        }
        
    TInt err = RProperty::Set( KPropertyUidBluetoothCategory, 
                           KPropertyKeyBluetoothSetDeviceName, aName );
    TRACE_INFO( ( _L( "[BTENGSETTINGS]\t RProperty::Set: %d"), err) );
    
        // Always update the name to registry
    TInt err2 = HandleBTRegistryNameSetting( const_cast<TDes&>( aName ) );

    TBool nameChanged = EFalse;
    if( !err || !err2 )
        {				       	
            // Check if this is the first time name setting.
        err = GetLocalNameModified( nameChanged );
        TRACE_INFO( ( _L( "[BTENGSETTINGS]\t GetLocalNameModified: err %d, namechanged %d"), 
        		err, nameChanged) );               	
        }
        
    if( ( !err || !err2 )  && nameChanged == EBTLocalNameDefault )
        {
            // Set the name as changed.
        CRepository* cenRep = NULL;
        TRAP( err, cenRep = CRepository::NewL( KCRUidBTEngPrivateSettings ) );
        if( !err )
            { 
            err = cenRep->Set( KBTLocalNameChanged, EBTLocalNameSet );
            TRACE_INFO( ( _L( "[BTENGSETTINGS]\t cenRep->Set( KBTLocalNameChanged, EBTLocalNameSet ): %d"), err) ); 
            }
        delete cenRep;
        }
        
    TRACE_FUNC_RES( ( _L( "result %d" ), err ) )
    return err;
    }


// ---------------------------------------------------------------------------
// Sets the Bluetooth hardware power state for temporary connections.
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::ChangePowerStateTemporarily()
    {
    TRACE_FUNC_ENTRY
    TInt err = KErrNone;
    if( !iSettingsWatcher )
        {
        TRAP( err, iSettingsWatcher = CBTEngSettingsNotify::NewL( NULL ) );
        }
    if( !err )
        {
        err = iSettingsWatcher->TogglePowerTemporarily();
        }
    TRACE_FUNC_EXIT
    return err;
    }

// ---------------------------------------------------------------------------
// Get the Bluetooth offline mode settings
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CBTEngSettings::GetOfflineModeSettings( 
        TCoreAppUIsNetworkConnectionAllowed& aOffline, 
        TBTEnabledInOfflineMode& aOfflineAllowed )
    {
    TRACE_FUNC_ENTRY
    aOffline = ECoreAppUIsNetworkConnectionAllowed;
    aOfflineAllowed = EBTEnabledInOfflineMode;
    TRAPD( err, GetCenRepKeyL( KCRUidCoreApplicationUIs, 
                               KCoreAppUIsNetworkConnectionAllowed, (TInt&) aOffline, 
                               ECoreAppUIsNetworkConnectionNotAllowed, 
                               ECoreAppUIsNetworkConnectionAllowed ) );
    if( !err && aOffline == ECoreAppUIsNetworkConnectionNotAllowed )
        {
        TRAP( err, GetCenRepKeyL( KCRUidBluetoothEngine, KBTEnabledInOffline, 
                                  (TInt&) aOfflineAllowed, EBTDisabledInOfflineMode, 
                                  EBTEnabledInOfflineMode ) );
        }
    return err;
    }


// ---------------------------------------------------------------------------
// Checks from central repository whether the Bluetooth friendly name 
// has been modified .
// ---------------------------------------------------------------------------
//
TInt CBTEngSettings::GetLocalNameModified( TBool& aStatus )
    {
    TRACE_FUNC_ENTRY

    aStatus = (TBool) EBTLocalNameDefault;
    TRAPD( err, GetCenRepKeyL( KCRUidBTEngPrivateSettings, KBTLocalNameChanged, 
                               (TInt&) aStatus, EBTLocalNameDefault, 
                               EBTLocalNameSet ) );
    return err;
    }


// ---------------------------------------------------------------------------
// Gets the specified CenRep key and checks if it is within a valid range; 
// if not, it it reset to the default value.
// ---------------------------------------------------------------------------
//
void CBTEngSettings::GetCenRepKeyL( const TUid aUid, const TInt aKey, TInt& aValue, 
                                    const TInt aMinRange1, const TInt aMaxRange1, 
                                    const TInt aMinRange2, const TInt aMaxRange2 )
    {
    TRACE_FUNC_ARG( ( _L( "aKey: %d" ), aKey ) )
// [SvV] Test that both logging macro do the same:
    TRACE_INFO( ( _L( "[BTENGSETTINGS]\t GetCenRepKeyL - start (aKey: %d)" ), aKey ) )
    TInt val = aValue;

    CRepository* cenRep = CRepository::NewL( aUid );
    TInt err = cenRep->Get( aKey, val );
        // Check that the value is within the allowed ranges.
    TBool validRange = ETrue;
    if( val < aMinRange1 || val > aMaxRange1 )
        {
            // The value is not within the first range
        if( !aMinRange2 && !aMaxRange2 )
            {
                // No second range is specified, so the value is out-of-range.
            validRange = EFalse;
            }
        else if( !( val >= aMinRange2 && val <= aMaxRange2 ) )
            {
                // The value is not within the second range either, 
                // so the value is out-of-range.
            validRange = EFalse;
            }
        }
    if( err || !validRange )
        {
        TRACE_INFO( ( _L( "Error occured! err(%d) val(%d), range %d-%d (2nd: %d-%d)" ), 
                       err, val, aMinRange1, aMaxRange1, aMinRange2, aMaxRange2 ) )
            // If an error occured, we cannot trust the central repository,
            // so return to default value to be safe.
        val = aValue;   // Revert to the original value
        (void) cenRep->Set( aKey, val );    // Just try once, ignore return value
        }
    delete cenRep;
    aValue = val;

    TRACE_INFO( ( _L( "[BTENGSETTINGS]\t GetCenRepKeyL - key value: %d; err: %d" ), 
                val, err ) )
    TRACE_FUNC_RES( ( _L( "key value: %d; err: %d" ), val, err ) )
    User::LeaveIfError( err );
    }


// ---------------------------------------------------------------------------
// Gets or sets the BT local name from Bluetooth Registry.
// ---------------------------------------------------------------------------
//
TInt CBTEngSettings::HandleBTRegistryNameSetting( TDes& aName )
    {
    TRACE_FUNC_ENTRY
    RBTRegServ btRegServ;
    RBTLocalDevice btReg;
    TInt err = btRegServ.Connect();
    if( !err )
        {
        err = btReg.Open( btRegServ );
        }
    TBTLocalDevice localDev;
    if( !err )
        {
            // Read the BT local name from BT Registry.
        err = btReg.Get( localDev );
        }
           
    if( !err )
        {
            // BT registry keeps the device name in UTF-8 format.
        TBuf8<KHCILocalDeviceNameMaxLength> utf8Name;
        if( aName.Length() == 0 )
            {
                // The error can be > 0 if there are unconverted characters.
            err = CnvUtfConverter::ConvertToUnicodeFromUtf8( aName, 
                                                        localDev.DeviceName() );
            TRACE_INFO( ( _L( "[BTENGSETTINGS]\t getting name from registry: err %d %S" ), 
            		err, &aName))
            }
        else
            {
                // Write the supplied  name to BT Registry.
                // The error can be > 0 if there are unconverted characters.
            err = CnvUtfConverter::ConvertFromUnicodeToUtf8( utf8Name, aName );
            if( !err )
                {
                localDev.SetDeviceName( utf8Name );
                err = btReg.Modify( localDev );
                }
            TRACE_INFO( ( _L( "[BTENGSETTINGS]\t setting name to registry:err %d, %S" ), 
            		err, &aName))
            }
        }
                
    btReg.Close();
    btRegServ.Close();
    TRACE_FUNC_EXIT
    
    return err;
    }
