/*
* 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:  Implementation of Query state.
*
*/


// INCLUDE FILES
#include <AccPolGenericIdDefinitions.h>     //device type definitions
#include "basrvaccstatequery.h"
#include "basrvaccstateconnect.h"
#include "basrvaccstatedisconnect.h"
#include "basrvaccstateattach.h"
#include "basrvaccstateattached.h"
#include "debug.h"

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

CBasrvAccStateQuery* CBasrvAccStateQuery::NewL(CBasrvAcc& aParent, TBool aConnectingRequest)
    {
    CBasrvAccStateQuery* self=new(ELeave) CBasrvAccStateQuery(aParent, aConnectingRequest);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

CBasrvAccStateQuery::~CBasrvAccStateQuery()
    {
    delete iQuery;
    TRACE_FUNC
    }

void CBasrvAccStateQuery::EnterL()
    {
    StatePrint(_L("Query"));
    // (NOT iConnecting) -> Headset connects to phone, SDP failure is tolerated. 
    iQuery->QueryAccInfoL(AccInfo().iAddr, !iConnecting);    
    Parent().RequestActiveMode();
    Parent().PreventLowPowerMode();
    }

CBasrvAccState* CBasrvAccStateQuery::ErrorOnEntry(TInt aReason)
    {
    TRACE_FUNC
    if (iConnecting)
        {
        TRAP_IGNORE(Parent().AccMan().ConnectCompletedL(AccInfo().iAddr, aReason, 0));
        }
    return NULL;
    }

TBTEngConnectionStatus CBasrvAccStateQuery::ConnectionStatus() const
    {
    return EBTEngConnecting;
    }

void CBasrvAccStateQuery::CancelConnect()
    {
    TRACE_FUNC
    if (iConnecting)
        {
        delete iQuery;
        iQuery = NULL;
        TRAP_IGNORE(Parent().ChangeStateL(NULL));
        }
    }

void CBasrvAccStateQuery::DisconnectL()
    {
    TRACE_FUNC
    delete iQuery;
    iQuery = NULL;
    if (AccInfo().iConnProfiles)
        {
        Parent().ChangeStateL(CBasrvAccStateDisconnect::NewL(Parent()));
        }
    else
        {
        Parent().ChangeStateL(NULL);
        }
    }
    
void CBasrvAccStateQuery::AccessoryDisconnectedL(TProfiles aProfile)
    {
    TRACE_FUNC
    ProfileDisconnected(aProfile);
    AccClosedAudio(aProfile);
    StatePrint(_L("Query"));
    if (!AccInfo().iConnProfiles && !iConnecting)
        {
        delete iQuery;
        iQuery = NULL;
        Parent().ChangeStateL(NULL);
        }
    }

void CBasrvAccStateQuery::GetAccInfoCompletedL(TInt aErr, const TAccInfo* aAcc, const TBTDeviceClass* aCod)
    {
    TRACE_FUNC
    CBasrvAccState* next = NULL;
    if ( ( aErr || !aAcc || !(aAcc->iSuppProfiles & EAnyAudioProfiles) ) &&
        ( iConnecting || !(AccInfo().iConnProfiles ) ))
        {
        TInt err = (aErr) ? aErr : KErrNotSupported;
        Parent().AccMan().ConnectCompletedL(AccInfo().iAddr, err, 0);
        Parent().ChangeStateL(next);
        return;
        }

    if (aAcc)
        {
        TInt exist_conns = AccInfo().iConnProfiles;
        TInt exist_audios = AccInfo().iAudioOpenedProfiles;
        AccInfo() = *aAcc;
        AccInfo().iAudioOpenedProfiles = exist_audios;
        AccInfo().iConnProfiles = exist_conns;
        AccInfo().iSuppProfiles |= exist_conns;
        }
    
    // Take HFP supported feature from BTMAC if it is not available
    if ( ( AccInfo().iConnProfiles & EHFP ) && 
         AccInfo().iSupportedFeatures[TAccInfo::EHFPIndex] == 0 )
        {
        CBTAccPlugin* plugin = Parent().AccMan().PluginMan().Plugin(EHFP);
        AccInfo().SetSupportedFeature((TUint16)plugin->GetRemoteSupportedFeature(), TAccInfo::EHFPIndex);
        }
    
    // if e.g. A2DP is supported by headset but BTSAC is not in ROM, filter it.
    Parent().AccMan().FilterProfileSupport(AccInfo());

    RArray<TBTDevAddr> conflicts;
    CleanupClosePushL(conflicts);
    ResolveConflicts(conflicts);
    if (conflicts.Count())
        {
        if (iConnecting)
            {
            Parent().AccMan().ConnectCompletedL(AccInfo().iAddr, KErrAlreadyExists, 0, &conflicts);
            }
        if (AccInfo().iConnProfiles)
            {
            next = CBasrvAccStateDisconnect::NewL(Parent());
            }
        }
    else
        {
        // Guess if this is a carkit or headset using the CoD info
    	if (aCod && (aCod->MajorServiceClass() & EMajorServiceAudioService) &&
                    (aCod->MajorDeviceClass() & EMajorDeviceAudioDevice ))
        	{
            if (aCod->MinorDeviceClass() == EMinorDeviceAVHandsfree ||
                aCod->MinorDeviceClass() == EMinorDeviceAVCarAudio)
                {
                AccInfo().SetDeviceType(KDTCarKit);
                TRACE_INFO((_L("Device is a carkit.")));
                }
            else
                {
                AccInfo().SetDeviceType(KDTHeadset);
                TRACE_INFO((_L("Device is a headset.")));
                }
        	}
    	else
    		{
    		AccInfo().SetDeviceType(KDTHeadset);
            TRACE_INFO((_L("Device is a headset.")));
    		}
        
        if (iConnecting)
            {
            next = CBasrvAccStateConnect::NewL(Parent());
            }
        else if (AccInfo().iConnProfiles)
            {
            next = CBasrvAccStateAttach::NewL(Parent(), iConnecting);
            }
        }
    CleanupStack::PopAndDestroy(&conflicts);
    Parent().ChangeStateL(next);    
    }

CBasrvAccStateQuery::CBasrvAccStateQuery(CBasrvAcc& aParent, TBool aConnectingRequest)
    : CBasrvAccState(aParent), iConnecting(aConnectingRequest)
    {
    }

void CBasrvAccStateQuery::ConstructL()
    {
    iQuery = CBasrvSdpQuery::NewL(*this);    
    }
    
void CBasrvAccStateQuery::ResolveConflicts(RArray<TBTDevAddr>& aConflicts)
    {
    RPointerArray<const TAccInfo> infos;
    TInt err = Parent().AccMan().AccInfos(infos);
    
    if (err == KErrNone)
        {
        TInt supportedByThis = AccInfo().iSuppProfiles;
        
        // HFP and HSP are mutual exclusive, i.e. can only have one connected at a time.
        if (supportedByThis & EAnyMonoAudioProfiles)
            {
            supportedByThis |= EAnyMonoAudioProfiles;
            }
        
        for (TInt i = 0; i < infos.Count(); i++)
            {
            if ((infos[i]->iAddr != AccInfo().iAddr) &&
                (infos[i]->iSuppProfiles & supportedByThis))
                {
                aConflicts.Append(infos[i]->iAddr);
                }
            }
        
        infos.Close();
        }
    }
