// Copyright (c) 2006-2009 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:
//

/**
 @file
 @internalTechnology
 @prototype
*/


#include <comms-infras/ss_log.h>
#include <comms-infras/linkmessages.h>
#include <comms-infras/nifif.h>
#include <elements/nm_messages_base.h>
#include <comms-infras/ss_nodemessages_dataclient.h>

#include "agentscpr.h"
#include "agentscprstates.h"
#include "CAgentAdapter.h"
#include "agentmessages.h"


#ifdef _DEBUG
// Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module
// (if it could happen through user error then you should give it an explicit, documented, category + code)
_LIT(KSpecAssert_NifManAgtPrCSSta, "NifManAgtPrCSSta");
#endif


using namespace Messages;
using namespace ESock;


#ifdef __CFLOG_ACTIVE
#define KAgentSCprTag KESockSubConnectionTag
_LIT8(KAgentSCprSubTag, "agentscprstates");
#endif


namespace AgentSCprStates
{

// ---------------- Start Connection Activity Overrides ----------------
EXPORT_DEFINE_SMELEMENT(TStartAgent, NetStateMachine::MStateTransition, AgentSCprStates::TContext)
EXPORT_C void TStartAgent::DoL()
   {
   __CFLOG_VAR((KAgentSCprTag, KAgentSCprSubTag, _L8("TStartAgent::DoL()")));

   iContext.Node().SetActivityIdForAdapter(iContext.iNodeActivity->ActivityId());
   iContext.Node().StartAgentL();
   }


// ---------------- Notifications From Flow Activity ----------------
EXPORT_DEFINE_SMELEMENT(TAwaitingNotificationFromFlow, NetStateMachine::MState, AgentSCprStates::TContext)
EXPORT_C TBool TAwaitingNotificationFromFlow::Accept()
    {
    return iContext.iMessage.IsMessage<TLinkMessage::TFlowToAgentNotification>();
    }

EXPORT_DEFINE_SMELEMENT(TNotifyAgent, NetStateMachine::MStateTransition, AgentSCprStates::TContext)
EXPORT_C void TNotifyAgent::DoL()
    {
    TLinkMessage::TAgentToFlowNotification& msg = message_cast<TLinkMessage::TAgentToFlowNotification>(iContext.iMessage);
    iContext.Node().NotificationFromFlow(static_cast<TFlowToAgentEventType>(msg.iValue));
    }


// ---------------- Unsolicited Flow Down Activity ----------------
EXPORT_DEFINE_SMELEMENT(TProcessDataClientGoneDown, NetStateMachine::MStateTransition, AgentSCprStates::TContext)
EXPORT_C void TProcessDataClientGoneDown::DoL()
    {
    TCFControlProvider::TDataClientGoneDown& msg = message_cast<TCFControlProvider::TDataClientGoneDown>(iContext.iMessage);
    TInt error = msg.iValue1;
    MNifIfNotify::TAction action = static_cast<MNifIfNotify::TAction>(msg.iValue2);

    RNodeInterface* dataClient = iContext.Node().FindClient(iContext.iSender);
    dataClient->ClearFlags(TCFClientType::EStarted);

    iContext.Node().NotificationToAgent (EFlowToAgentEventTypeLinkLayerDown, &error);

    if (action == MNifIfNotify::EReconnect)
        {
        CAgentProvisionInfo::TAgentReconnectOption option = iContext.Node().AgentProvisionInfo()->ReconnectOption();
    	TUint32 attempts = iContext.Node().AgentProvisionInfo()->ReconnectAttempts();

		if (option == CAgentProvisionInfo::EDoNotAttemptReconnect
			|| attempts == 0)
			{
            iContext.Node().AgentProvisionInfo()->AgentAdapter()->DisconnectAgent(error);
			}
		else
			{
			const_cast<CAgentProvisionInfo*>(iContext.Node().AgentProvisionInfo())->SetReconnectAttempts(attempts - 1);

	        switch (option)
	            {
	            case CAgentProvisionInfo::EAttemptReconnect:
            		iContext.Node().ConnectAgent(EAgentReconnect);
	                break;

	            case CAgentProvisionInfo::EPromptForReconnect:
	                // This causes the agent Reconnect() to begin
	                // If the CAgentBase::Reconnect() hasn't been overridden the
	                // user will be prompted
	                iContext.Node().AgentProvisionInfo()->AgentAdapter()->PromptForReconnect();
	                break;

	            default:
	                // No other options are available
	                __ASSERT_DEBUG(EFalse, User::Panic(KSpecAssert_NifManAgtPrCSSta, 1));
	                break;
	            }
			}
        }
    else
        {
        iContext.Node().AgentProvisionInfo()->AgentAdapter()->DisconnectAgent(error);
        }
    }


// ---------------- Authentication Activity ----------------

EXPORT_DEFINE_SMELEMENT(TAwaitingAuthenticate, NetStateMachine::MState, CoreStates::TContext)
EXPORT_C TBool TAwaitingAuthenticate::Accept()
	{
	return iContext.iMessage.IsMessage<TLinkMessage::TAuthenticate>();
	}

EXPORT_DEFINE_SMELEMENT(TAwaitingAuthenticateComplete, NetStateMachine::MState, AgentSCprStates::TContext)
EXPORT_C TBool TAwaitingAuthenticateComplete::Accept()
	{
	return iContext.iMessage.IsMessage<TLinkMessage::TAuthenticateResponse>();
	}

EXPORT_C void TAwaitingAuthenticateComplete::Cancel()
    {
    __CFLOG_VAR((KAgentSCprTag, KAgentSCprSubTag, _L8("AAwaitingAuthenticateComplete::Cancel()")));

    CAgentSubConnectionProvider& provider = iContext.Node();
    provider.SetActivityIdForAdapter(iContext.iNodeActivity->ActivityId());
    __ASSERT_DEBUG(provider.AgentProvisionInfo()->AgentAdapter(), User::Panic(KSpecAssert_NifManAgtPrCSSta, 2));

    if (provider.iAuthenticateInProgress)
        {
        provider.iAuthenticateInProgress = EFalse;
        provider.AgentProvisionInfo()->AgentAdapter()->CancelAuthenticate();
        }
    }

EXPORT_DEFINE_SMELEMENT(TSendAuthenticate, NetStateMachine::MStateTransition, AgentSCprStates::TContext)
EXPORT_C void TSendAuthenticate::DoL()
    {
    __CFLOG_VAR((KAgentSCprTag, KAgentSCprSubTag, _L8("ASendAuthenticate::DoL()")));

    CAgentSubConnectionProvider& provider = iContext.Node();
    __ASSERT_DEBUG(provider.AgentProvisionInfo()->AgentAdapter(), User::Panic(KSpecAssert_NifManAgtPrCSSta, 3));

    provider.iAuthenticateInProgress = ETrue;
    provider.AgentProvisionInfo()->AgentAdapter()->Authenticate(provider.iUsername, provider.iPassword);
    provider.SetActivityIdForAdapter(iContext.iNodeActivity->ActivityId());
    iContext.iNodeActivity->SetPostedTo(provider.Id());
    }

EXPORT_DEFINE_SMELEMENT(TSendAuthenticateComplete, NetStateMachine::MStateTransition, CoreStates::TContext)
EXPORT_C void TSendAuthenticateComplete::DoL()
    {
    __CFLOG_VAR((KAgentSCprTag, KAgentSCprSubTag, _L8("ASendAuthenticateComplete::DoL()")));
    TLinkMessage::TAuthenticateResponse msg;
    if (iContext.iNodeActivity)
        {

        iContext.iNodeActivity->PostToOriginators(msg);
        }
    else
        {
        iContext.PostToSender(msg);
        }
    }


// ---------------- Stop Connection Activity Overrides ----------------

EXPORT_DEFINE_SMELEMENT(TNoTagOrProviderStopped, NetStateMachine::MStateFork, AgentSCprStates::TContext)
EXPORT_C TInt TNoTagOrProviderStopped::TransitionTag()
	{
	if (iContext.Node().AgentProvisionInfo()->AgentAdapter() == NULL ||
        iContext.Node().AgentProvisionInfo()->AgentAdapter()->AgentState() == CAgentAdapter::EDisconnected)
        {
        return CoreNetStates::KProviderStopped;
        }
    return MeshMachine::KNoTag;
    }

EXPORT_DEFINE_SMELEMENT(TNoTagOrProviderStarted, NetStateMachine::MStateFork, AgentSCprStates::TContext)
EXPORT_C TInt TNoTagOrProviderStarted::TransitionTag()
	{
	RNodeInterface* flow = iContext.Node().GetFirstClient<TDefaultClientMatchPolicy>(TClientType(TCFClientType::EData), TClientType(TCFClientType::EStarted));
	return flow ? CoreNetStates::KProviderStarted : MeshMachine::KNoTag;
    }

EXPORT_DEFINE_SMELEMENT(TStopAgent, NetStateMachine::MStateTransition, AgentSCprStates::TContext)
EXPORT_C void TStopAgent::DoL()
    {
    __CFLOG_VAR((KAgentSCprTag, KAgentSCprSubTag, _L8("AStopAgent::DoL()")));

    TInt reason = KErrCancel;
    if (iContext.iMessage.IsMessage<TCFDataClient::TStopped>())
        {
        // This supports the logic from the legacy CNifAgentRef::LinkLayerDown()
        // During the processing of LinkLayerDown the Agent would be notified.
        TCFDataClient::TStopped& msg = message_cast<TCFDataClient::TStopped>(iContext.iMessage);
        iContext.Node().AgentProvisionInfo()->AgentAdapter()->NotificationToAgent (EFlowToAgentEventTypeLinkLayerDown, &msg.iValue);
        reason = msg.iValue;
        }
    else if (iContext.iMessage.IsMessage<TCFDataClient::TStop>())
        {
        // Receiving TCFDataClient::TStop when we don't have a flow will get us here
        // Not sure that should ever happen though
        TCFDataClient::TStop& msg = message_cast<TCFDataClient::TStop>(iContext.iMessage);
        reason = msg.iValue;
        }
    else
        {
		// coming from destroy
        reason = KErrAbort;
        }

    iContext.Node().SetActivityIdForAdapter(iContext.iNodeActivity->ActivityId());
    iContext.Node().StopAgent(reason);
    iContext.iNodeActivity->SetPostedTo(iContext.NodeId());
    }

EXPORT_DEFINE_SMELEMENT(TAwaitingAgentDown, NetStateMachine::MState, AgentSCprStates::TContext)
EXPORT_C TBool TAwaitingAgentDown::Accept()
	{
	return iContext.iMessage.IsMessage<TCFServiceProvider::TStopped>();
	}

EXPORT_DEFINE_SMELEMENT(TSendDataClientGoneDown, NetStateMachine::MStateTransition, AgentSCprStates::TContext)
EXPORT_C void TSendDataClientGoneDown::DoL()
	{
	RNodeInterface *cp = iContext.Node().ControlProvider();
	cp->PostMessage(TNodeCtxId(iContext.ActivityId(), iContext.NodeId()),
		TCFControlProvider::TDataClientGoneDown(iContext.Node().iStoppingReason).CRef());
	}

EXPORT_DEFINE_SMELEMENT(TSendError, NetStateMachine::MStateTransition, AgentSCprStates::TContext)
EXPORT_C void TSendError::DoL()
	{
	RNodeInterface *cp = iContext.Node().ControlProvider();
	cp->PostMessage(TNodeCtxId(iContext.ActivityId(), iContext.NodeId()),
		TEBase::TError(TCFDataClient::TStart::Id(), iContext.Node().iStoppingReason).CRef());
	}

} // namespace AgentSCprStates

