// 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
 @internalComponent
*/

#ifndef L2CAPFECNEGOTIATOR_H
#define L2CAPFECNEGOTIATOR_H

#include "L2types.h"

class TL2CapFecNegotiator;
class TL2CapSingleDirectionFecNegotiatorBase;

// This encapsulates channel mode dependent FEC option handling behaviors.
// Channel mode policy is applied outside of this.
NONSHARABLE_CLASS(TModeSpecificFecOptionHandlerBase)
	{
public:
	// Checks whether the field values are valid (within specification limits).
	// To be used early, during the parsing of an option received from the peer.
	// Note: valid doesn't mean acceptable.
	virtual TBool IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const = 0;
	virtual TBool IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
										   const TRetransmissionAndFlowControlOption& aPeer) const = 0;
	// The return value says whether the option needs to be included in ConfigRsp.
	// [Only enhanced mode positive responses need to be included as they convey new information
	// on the timers and MPS]
	virtual TBool BuildPositiveResponse(TRetransmissionAndFlowControlOption& aPreferred,
			   							const TRetransmissionAndFlowControlOption& aPeer) const;
	virtual void BuildNegativeResponse(TRetransmissionAndFlowControlOption& aPreferred,
									   const TRetransmissionAndFlowControlOption& aPeer) const;
	// In general configuration procedure (spec chapter 7 General Procedures) if the remote accepts
	// an option from our ConfigReq, it doesn't have to include it in the response. So before
	// processing the options from a ConfigRsp we've received, we go through the preferred values
	// we've sent in ConfigReq and pretend that the remote has accepted them without a hitch. Then
	// options included in the received ConfigRsp are processed. This works well with plain-vanilla
	// negotiated options, but TRetransmissionAndFlowControl for enhanced modes is different:
	// request and response semantics are a bit different, and so the request does not contain all
	// information needed in a response. So we have to take the request we've sent, guess the
	// missing information, and thus create a fake response. And that's what this method does.
	virtual void PrepareImplicitPeerResponse(TRetransmissionAndFlowControlOption& aImplicitResponse,
											 const TRetransmissionAndFlowControlOption& aPreferred) const;

	// Set user-requested MaxTransmit value. The value is given in the enhanced range
	// 0-255, where 0 means infinity and is only valid for ERTM. Deriving classes are
	// responsible for the cut-off of the infinity value on 255 if they can't handle it.
	virtual void SetMaxTransmit(TRetransmissionAndFlowControlOption& aFecOption, TUint8 aMaxTransmit) const;

	virtual void ZeroUnspecifiedRequestFields(TRetransmissionAndFlowControlOption& aFecOption) const;
	virtual void ZeroUnspecifiedResponseFields(TRetransmissionAndFlowControlOption& aFecOption) const;
	};

NONSHARABLE_CLASS(TStreamingFecHandler) : public TModeSpecificFecOptionHandlerBase
	{
public:
	virtual TBool IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const;
	virtual TBool IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
										   const TRetransmissionAndFlowControlOption& aPeer) const;
	virtual TBool BuildPositiveResponse(TRetransmissionAndFlowControlOption& aPreferred,
										const TRetransmissionAndFlowControlOption& aPeer) const;
    virtual void BuildNegativeResponse(TRetransmissionAndFlowControlOption& aPreferred,
                                       const TRetransmissionAndFlowControlOption& aPeer) const;	
	virtual void ZeroUnspecifiedRequestFields(TRetransmissionAndFlowControlOption& aFecOption) const; 
	virtual void ZeroUnspecifiedResponseFields(TRetransmissionAndFlowControlOption& aFecOption) const;
	};

NONSHARABLE_CLASS(TErtmFecHandler) : public TModeSpecificFecOptionHandlerBase
	{
public:
	virtual TBool IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const;
	virtual TBool IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
										   const TRetransmissionAndFlowControlOption& aPeer) const;
	virtual TBool BuildPositiveResponse(TRetransmissionAndFlowControlOption& aPreferred,
										const TRetransmissionAndFlowControlOption& aPeer) const;
	virtual void BuildNegativeResponse(TRetransmissionAndFlowControlOption& aPreferred,
									   const TRetransmissionAndFlowControlOption& aPeer) const;
	virtual void PrepareImplicitPeerResponse(TRetransmissionAndFlowControlOption& aImplicitResponse,
											 const TRetransmissionAndFlowControlOption& aPreferred) const;
	virtual void SetMaxTransmit(TRetransmissionAndFlowControlOption& aFecOption, TUint8 aMaxTransmit) const;
	virtual void ZeroUnspecifiedRequestFields(TRetransmissionAndFlowControlOption& aFecOption) const; 
	virtual void ZeroUnspecifiedResponseFields(TRetransmissionAndFlowControlOption& aFecOption) const;
	};

NONSHARABLE_CLASS(TLegacyFecHandler) : public TModeSpecificFecOptionHandlerBase
	{
public:
	virtual TBool IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const;
	virtual TBool IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
										   const TRetransmissionAndFlowControlOption& aPeer) const;
	virtual void SetMaxTransmit(TRetransmissionAndFlowControlOption& aFecOption, TUint8 aMaxTransmit) const;
	};

NONSHARABLE_CLASS(TBasicFecHandler) : public TModeSpecificFecOptionHandlerBase
	{
public:
	// Most peers will actually only request Basic implicitly i.e. by not including the FEC
	// option at all, but we treat implicit/explicit values uniformly and will still execute
	// these. And a Basic FEC can be explicit if the peer does new modes, but for some
	// reason doesn't want to use them and has sent us an Unacceptable Params ConfigRsp with
	// Basic in it.
	virtual TBool IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const;
	virtual TBool IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
										   const TRetransmissionAndFlowControlOption& aPeer) const;
	};

// This groups all mode-specific handlers and provides delegator methods that forward calls to
// appropriate FecHandlers.
NONSHARABLE_CLASS(TFecOptionHandlerDelegator)
	{
public:
	inline TBool IsOptionValid(const TRetransmissionAndFlowControlOption& aFecOption) const;
	inline TBool IsPeerResponseAcceptable(const TRetransmissionAndFlowControlOption& aPreferred,
										  const TRetransmissionAndFlowControlOption& aPeer) const;
	inline TBool BuildPositiveResponse(TRetransmissionAndFlowControlOption& aPreferred,
									   const TRetransmissionAndFlowControlOption& aPeer) const;
	inline void BuildNegativeResponse(TRetransmissionAndFlowControlOption& aPreferred,
									  const TRetransmissionAndFlowControlOption& aPeer) const;
	inline void PrepareImplicitPeerResponse(TRetransmissionAndFlowControlOption& aImplicitResponse,
											const TRetransmissionAndFlowControlOption& aPreferred) const;
	inline void SetMaxTransmit(TRetransmissionAndFlowControlOption& aFecOption, TUint8 aMaxTransmit) const;
	inline void ZeroUnspecifiedRequestFields(TRetransmissionAndFlowControlOption& aFecOption) const;
	inline void ZeroUnspecifiedResponseFields(TRetransmissionAndFlowControlOption& aFecOption) const;
private:
	TModeSpecificFecOptionHandlerBase& Handler(const TRetransmissionAndFlowControlOption& aFecOption);
	const TModeSpecificFecOptionHandlerBase& Handler(const TRetransmissionAndFlowControlOption& aFecOption) const;
	
	TModeSpecificFecOptionHandlerBase* ModeToHandler(TL2CapChannelMode aMode);
	const TModeSpecificFecOptionHandlerBase* ModeToHandler(TL2CapChannelMode aMode) const;
	
#ifdef __FLOG_ACTIVE
	void LogCurrentValues(const TRetransmissionAndFlowControlOption& aPreferred,
						  const TRetransmissionAndFlowControlOption& aPeer) const;
#endif
private:
	TStreamingFecHandler	iStreamingFecHandler;
	TErtmFecHandler			iErtmFecHandler;
	TLegacyFecHandler		iLegacyFecHandler;
	TBasicFecHandler		iBasicFecHandler;
	};

NONSHARABLE_CLASS(TL2CapSingleDirectionFecNegotiatorBase) : public TL2CapConfigurationOptionGroupBase
{
public:
	inline TL2CapSingleDirectionFecNegotiatorBase(const TL2CapFecNegotiator& aFecNegotiator);
	
	inline void SetPeer(const TRetransmissionAndFlowControlOption& aFecOption);
	inline void SetPreferred(const TRetransmissionAndFlowControlOption& aFecOption);

	void SetupForRenegotiation();
	
	inline TOptionConfigStatus ConfigOptionStatus() const;
	
	inline const TRetransmissionAndFlowControlOption& Peer() const;
	inline const TRetransmissionAndFlowControlOption& Preferred() const;


protected:
	TBool IsPeerModeAcceptable(TL2CapChannelMode aPeerMode, TBool& aDisconnect) const;

#ifdef __FLOG_ACTIVE
	void LogState() const;
#endif

protected:
	const TL2CapFecNegotiator&			iFecNegotiator;

	TOptionConfigStatus					iConfigStatus;
	TRetransmissionAndFlowControlOption iPeer;
	TRetransmissionAndFlowControlOption iPreferred;
	};

NONSHARABLE_CLASS(TL2CapIncomingFecNegotiator) : public TL2CapSingleDirectionFecNegotiatorBase
	{
public:
	inline TL2CapIncomingFecNegotiator(const TL2CapFecNegotiator& aFecNegotiator);
	void ProcessPeerValue(const TRetransmissionAndFlowControlOption& aFecOption,
						  TBool aIsUnacceptableParameters);
	void ProcessImplicitPeerValue();
    void Setup();
protected:
    void BuildRequest(TL2CapChannelMode aMode, TRetransmissionAndFlowControlOption& aFecOption);
	};

NONSHARABLE_CLASS(TL2CapOutgoingFecNegotiator) : public TL2CapSingleDirectionFecNegotiatorBase
	{
public:
	inline TL2CapOutgoingFecNegotiator(const TL2CapFecNegotiator& aFecNegotiator);
	TInt ProcessPeerValue(const TRetransmissionAndFlowControlOption& aFecOption);
    void Setup();	
	inline TBool NeedToIncludeInPositiveConfigResponse();
	inline void SetIncludeInPositiveConfigResponse(TBool aVal);
private:
	TBool iIncludeValueInPositiveConfigResponse;
	};

// This is the interface to the FEC option negotiation functionality, through which
// all interaction with the external world should be done.
NONSHARABLE_CLASS(TL2CapFecNegotiator)
	{
public:
	// Variants of the negotiation procedure defined in 2.1 Core Spec Addendum 1,
	// chapter 5.4 Retransmission And Flow Control Option.
	enum TNegotiationBehavior
		{
		// Allows all spec-allowed fallback options: Streaming -> ERTM, ERTM -> Basic and
		// Streaming -> Basic. E.g. if our preferred mode is ERTM and peer proposes Basic,
		// we'll fall back to it. The negotiation procedure is a simple minimization
		// algorithm, where the party with the higher-precedence mode wins.
		// Also note that we try to apply the same hierarchy to RTM and FC, so effectively
		// you can read this as Unreliable -> Reliable, Reliable -> Basic and Unreliable -> Basic,
		// where Reliable = { ERTM, RTM } and Unreliable = { Streaming, FC }
		// For the precedence of modes, see channel mode <= operator or the spec, same section.
		EState1,
		// Like State1, only Unreliable -> Reliable is not allowed (Unreliable -> Basic and
		// Reliable -> Basic still are).
		EState1NoUnreliableToReliable,
		// No fallback - disconnect immediately if peer proposes a different channel mode than
		// we want.
		EState2
		};

public:
	inline TL2CapFecNegotiator();

	TBool Setup(TL2CapConfig::TChannelReliability aChannelReliability,
				TBool aLegacyModesDisallowed,
				const TL2CapEntityInfo& aPeerEntityConfig,
				TUint8 aMaxTransmit);

	// Can't renegotiate FEC currently but with Generic AMP time-outs will be renegotiable.
	void SetupForRenegotiation();

	// Methods below implement events in option state machine - they're identical
	// to the ones in the generic state machine in TL2CapConfigurationOptionGroup.
	inline TInt PeerRequestsOption(const TRetransmissionAndFlowControlOption& aFecOption);
	inline TInt PeerRequestsLastAcceptedValue();
	
	inline TInt PeerRejectsOption(const TRetransmissionAndFlowControlOption& aFecOption);
	inline TInt PeerAcceptsOption(const TRetransmissionAndFlowControlOption& aFecOption);
	inline void PeerAcceptsOption();

	inline TBool NeedToIncludeInPositiveConfigResponse();
	
	void DowngradeIncomingToBasicIfOutgoingIsBasic();
	TInt CheckNegotiatedChannelMode(TBool& aDowngrade);

	// [Methods below add a lot of fluff but they provide strong decoupling and encapsulation.
	// None of the internal classes are exposed.]
	
	inline const TRetransmissionAndFlowControlOption& IncomingPreferred() const;
	inline const TRetransmissionAndFlowControlOption& OutgoingPreferred() const;

	inline TL2CapConfigurationOptionGroupBase::TOptionConfigStatus IncomingConfigOptionStatus() const;
	inline TL2CapConfigurationOptionGroupBase::TOptionConfigStatus OutgoingConfigOptionStatus() const;

	inline TL2CapDataControllerConfig DataControllerConfig() const;

	// These abstract away the fact that some parameters are negotiated in opposite paths depending
	// on the modes used (eg Monitor TO), and which fields the values should be taken from (Peer or
	// Preferred).
	inline TL2CapChannelMode OutgoingLinkMode() const;
	inline TL2CapChannelMode IncomingLinkMode() const;
	inline TUint8 OutgoingLinkModeAsUnsignedByte() const;
	inline TUint8 IncomingLinkModeAsUnsignedByte() const;
	inline TUint8 OutgoingTxWindowSize() const;
	inline TUint8 IncomingTxWindowSize() const;
	inline TUint8 OutgoingMaxTransmit() const;
	inline TUint8 IncomingMaxTransmit() const;
	inline TUint16 OutgoingRetransmissionTimeout() const;
	inline TUint16 IncomingRetransmissionTimeout() const;
	inline TUint16 OutgoingMonitorTimeout() const;
	inline TUint16 IncomingMonitorTimeout() const;
	inline TUint16 OutgoingMaximumPDUSize() const;
	inline TUint16 IncomingMaximumPDUSize() const;

	// These are for the internal per-direction negotiator objects.
	TBool IsModeLegal(TL2CapChannelMode aPeerMode) const;
	inline const TL2CapEntityInfo& PeerSupportedModes() const;
	inline TNegotiationBehavior NegotiationBehavior() const;
	inline TL2CapChannelMode DesiredMode() const;
    inline TUint8 MaxTransmit() const;	
	inline const TFecOptionHandlerDelegator& ModeSpecificHandlers() const;
private:
	TL2CapIncomingFecNegotiator		iIncomingNegotiator;
	TL2CapOutgoingFecNegotiator		iOutgoingNegotiator;

	TL2CapEntityInfo				iPeerSupportedModes;
	TNegotiationBehavior			iNegotiationBehavior;
	TL2CapChannelMode				iDesiredMode;
    TUint8                          iMaxTransmit;
    
	TFecOptionHandlerDelegator		iModeSpecificHandlers;
	};

#include "L2CapFecNegotiator.inl"

#endif /*L2CAPFECNEGOTIATOR_H*/
