/*
* Copyright (c) 2005-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:  This class listens to notification coming from Remote Control framework.
*				 BTRCC is interested in only those of initiated by BT Remote Control Plugin.	
*
*/


// INCLUDE FILES
#include <remconbeareravrcp.h> //	KRemConBearerAvrcpImplementationUid = 0x1020685f
#include <MediaStreamPropertiesPSKeys.h>
#include <remconinterfaceselector.h> 
#include <remconaddress.h>

#include "btrccAbsoluteVolumeLevelController.h"
#include "debug.h"

const TInt KAbsoluteVolumeLevelSetServiceId = 0x02; 
const TInt KMaxRetries = 3;
const TInt KDefaultStep = 1000;

// MODULE DATA STRUCTURES

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

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CBTRCCAbsoluteVolumeLevelController* CBTRCCAbsoluteVolumeLevelController::NewL(CRemConInterfaceSelector& aInterfaceSelector, MBTRCCVolumeControllerObserver& aObserver)
	{
	CBTRCCAbsoluteVolumeLevelController* self = new (ELeave) CBTRCCAbsoluteVolumeLevelController(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL(aInterfaceSelector);
	CleanupStack::Pop(self);
	return self;
	}

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::CBTRCCAbsoluteVolumeLevelController
// C++ constructor.
// -----------------------------------------------------------------------------
//
CBTRCCAbsoluteVolumeLevelController::CBTRCCAbsoluteVolumeLevelController(MBTRCCVolumeControllerObserver& aObserver)
: CBTRCCVolumeLevelControllerBase(aObserver), iStep(KDefaultStep)
	{
	}

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::ConstructL
// Symbian 2nd phase constructor.
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::ConstructL(CRemConInterfaceSelector& aInterfaceSelector)
	{
   	TRACE_FUNC
   	CBTRCCVolumeLevelControllerBase::ConstructL();
   	iLocalMaxVolume = GetLocalMaxVolumeL();  
    iInterfaceSelector = &aInterfaceSelector;
   	
    iRemConAbsoluteVolumeController = CRemConAbsoluteVolumeController::NewL(aInterfaceSelector, 
                                                                     *this, 
                                                                     iLocalMaxVolume);    
    TRACE_INFO((_L("CRemConAbsoluteVolumeController created")))
    iBtrccAbsoluteVolumeActive = CBTRCCActive::NewL(*this, KAbsoluteVolumeLevelSetServiceId);
    TRACE_INFO((_L("CBTRCCActive created")))
	}

// -----------------------------------------------------------------------------
// Destructor.
// -----------------------------------------------------------------------------
//
CBTRCCAbsoluteVolumeLevelController::~CBTRCCAbsoluteVolumeLevelController()
	{
	TRACE_FUNC
	delete iBtrccAbsoluteVolumeActive; 
	}

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::DoStart
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::DoStart(TInt aInitialVolume) 
    {
    TRACE_FUNC
    AdjustRemoteVolume(aInitialVolume); 
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::DoStop
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::DoStop() 
    {
    TRACE_FUNC
    iRemConAbsoluteVolumeController->CancelAbsoluteVolumeNotification();
    iRemConAbsoluteVolumeController->CancelSetAbsoluteVolume(); 
    iBtrccAbsoluteVolumeActive->Cancel();
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::DoReset
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::DoReset()
    {
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::AdjustRemoteVolume
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::AdjustRemoteVolume(TInt aVolume)
    {
	TRACE_INFO((_L("Setting volume to %d"), aVolume))
	
	if(iBtrccAbsoluteVolumeActive->IsActive()) 
        {
        iRemConAbsoluteVolumeController->CancelSetAbsoluteVolume(); 
        iBtrccAbsoluteVolumeActive->Cancel();
        }
	
    //Try to register volume change notification
	RegisterVolumeChangeNotification();
    
	// iNumRemotes is not needed by us, but the method requires it. 
    iRemConAbsoluteVolumeController->SetAbsoluteVolume(iBtrccAbsoluteVolumeActive->iStatus, 
            aVolume, iNumRemotes); 
    iBtrccAbsoluteVolumeActive->GoActive();
    }


// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::RegisterVolumeChangeNotification
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::RegisterVolumeChangeNotification()
    {

    iRemConAbsoluteVolumeController->CancelAbsoluteVolumeNotification(); 
    iRemConAbsoluteVolumeController->RegisterAbsoluteVolumeNotification();
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::RoundRemoteVolume
// -----------------------------------------------------------------------------
//
TInt CBTRCCAbsoluteVolumeLevelController::RoundRemoteVolume(TInt aPrevPhVol)
    {
    TRACE_FUNC
    TInt phoneVol = GetCurrentLocalVolume();
    TInt remoteVol = GetCurrentRemoteVolume();
    TRACE_INFO((_L("Volume to be rounded %d"), remoteVol))
    // Update step
    if( phoneVol != -1 && aPrevPhVol != -1 )
        {
        TInt step = Abs( phoneVol - aPrevPhVol );
        if( step )
            {
            TRACE_INFO((_L("Step %d"), step))
            // Only update step value if it is not equal to zero
            iStep = step;
            }
        }    
    if( remoteVol >= 0 && remoteVol <= iLocalMaxVolume )
        {        
        TInt lowLimit = 0;
        TInt highLimit = 0;
        
        for( TInt i = 0 ; highLimit < iLocalMaxVolume ; i++ )
            {
            lowLimit = i*iStep;
            highLimit = (i+1)*iStep;
            // Find the correct low and high value pair in which volume
            // belongs.
            if( remoteVol >= lowLimit && remoteVol <= highLimit )
                {
                break;
                }
            }
        TInt diff1 = Abs( remoteVol - lowLimit );
        TInt diff2 = Abs( remoteVol - highLimit );
        remoteVol = diff1 <= diff2 ? lowLimit : highLimit;
        }
    TRACE_INFO((_L("Rounded volume %d"), remoteVol))
    return remoteVol;
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::RequestCompletedL
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::RequestCompletedL(CBTRCCActive& aActive, TInt aErr)
    {
    TRACE_FUNC
    if( aActive.ServiceId() != KAbsoluteVolumeLevelSetServiceId )
        {
        // This is something the base class can handle. 
        CBTRCCVolumeLevelControllerBase::RequestCompletedL(aActive, aErr);
        }
    else if(aErr && iRetryCounter < KMaxRetries) // else it is KAbsoluteVolumeLevelSetServiceId
        {
        TRACE_INFO((_L("Sending abs vol cmd failed with error %d"), aErr))
        iRetryCounter++;
        AdjustRemoteVolume(GetCurrentLocalVolume());
        }
    else
        {
        if(iRetryCounter >= KMaxRetries)
            {
            VolumeControlError(EVolumeAdjustmentFailed); 
            }
        iRetryCounter = 0;
        }
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::CancelRequest
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::CancelRequest(TInt aServiceId) 
    {
    TRACE_FUNC
    if( aServiceId != KAbsoluteVolumeLevelSetServiceId )
        {
        // This is something the base class can handle. 
        CBTRCCVolumeLevelControllerBase::CancelRequest(aServiceId);
        } 
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::MrcavcoCurrentVolume
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::MrcavcoCurrentVolume(TUint32 aVolume, 
        TUint32 aMaxVolume, 
        TInt aError)
    {
    TRACE_FUNC
    AccessoryChangedVolume(aVolume, aMaxVolume, aError);
    /*if(aError != KErrNone) 
        {
        // Register again to the notifications. 
        iRemConAbsoluteVolumeController->RegisterAbsoluteVolumeNotification();     
        }*/
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::MrcavcoSetAbsoluteVolumeResponse
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::MrcavcoSetAbsoluteVolumeResponse(TUint32 aVolume, 
        TUint32 aMaxVolume, 
        TInt aError)
    {
    TRACE_FUNC
    TRACE_INFO((_L("Accessory volume is %d / %d"), aVolume, aMaxVolume))
    AccessoryChangedVolume(aVolume, aMaxVolume, aError);
    }

// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::MrcavcoAbsoluteVolumeNotificationError
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::MrcavcoAbsoluteVolumeNotificationError()
    {
    TRACE_FUNC
    // This is called when there's some error with writing the commands. 
    // It means that we can't send notifications or responses to those, so 
    // it's better to stop volume controlling and reset the link.  
    VolumeControlError(ERegisterNotificationsFailed); 
    }
// -----------------------------------------------------------------------------
// CBTRCCAbsoluteVolumeLevelController::AccessoryChangedVolume
// -----------------------------------------------------------------------------
//
void CBTRCCAbsoluteVolumeLevelController::AccessoryChangedVolume(TUint32 aVolume, TUint32 aMaxVolume, TInt aError)
    {
    TRACE_FUNC
    TRACE_INFO((_L("Accessory volume is %d / %d"), aVolume, aMaxVolume))
    if( ValidVolumeParams(aVolume, aMaxVolume) && (aError == KErrNone) )
        {
        // Convert volume scale to phone's volume scale. (User reals and round in the end.)  
        TInt volumeInPhoneScale = (TReal)aVolume * ((TReal)iLocalMaxVolume / (TReal)aMaxVolume) + 0.5;
        ASSERT(volumeInPhoneScale <= iLocalMaxVolume);
        TRACE_INFO((_L("Volume in phone scale is %d"), volumeInPhoneScale))
        CBTRCCVolumeLevelControllerBase::AccessoryChangedVolume(volumeInPhoneScale); 
        }
    }

TBool CBTRCCAbsoluteVolumeLevelController::ValidVolumeParams(TUint32 aVolume, TUint32 aMaxVolume)
    {
    return (( aMaxVolume > 0 ) && (aVolume <= aMaxVolume));
    }

//  End of File  
