// Copyright (c) 2005-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 SYMBIAN_NETWORKING_CFTRANSPORT_H
#define SYMBIAN_NETWORKING_CFTRANSPORT_H

#include <elements/nm_address.h>
#include <elements/nm_transport.h>
#include <elements/metavirtctor.h>


/** Synchronously dispatching within the same thread is strongly discouraged but relied upon by the legacy EINTSOCK interface.
Between threads the function is supported but the dispatch is not synchronous (risk of deadlock)
*/
#define SYMBIAN_CFTRANSPORT_SUPPORT_SYNC_SEND

namespace NetInterfaces
	{
	class TInterfaceControl;
	}

namespace CommsFW
{

/**
   CFTransport panic category
*/
_LIT(KCFTransportPanic, "CFTransport");

/** Panics generated by the Comms Channels
*/
enum TCFTransportPanics
	{
	/** Peer failing to drain input properly
	*/
	ECFTransPeerDrainFailure = 0,
	/** Invalid interface registration, eg unsupported or duplicate
	*/
	ECFTransBadRegistration = 1,
	/** Cookie invalid or corrupt. If null then common causes are forgetting to initialise at all or with TCFClientSignalBase-derived classes
	failing to chain the metadata attribute table to the parent, eg END_ATTRIBUTE_TABLE_BASE(TCFSignalExtnBase, 0)
	*/
	ECFTransInvalidCookie = 2,
	/** No legacy message handler registered and a legacy message was received
	*/
	ECFTransLegacyHandlerAbsent = 3,
	/** Invalid worker thread id
	*/
	ECFTransInvalidWorkerId = 4,
	/** No transport to worker
	*/
	ECFTransAbsentWorker = 5,
	/** Attempt to register a handler for an interface already registered - must first deregister original
	*/
	ECFTransDuplicateHandler = 6,
	/** Use of a function requiring the global thread register when the pointer to it has not been set.
	Without the global register many operations on peer threads are impossible.
	*/
	ECFTransThreadRegisterUnspecified = 7,
	/** RCFInterfaceBase not opened with recipient peer details
	*/
	ECFTransNotOpened = 8,
	/** Internal fault not directly generated by client activity
	*/
	ECFTransItfClassFault = 8,
	/** After Leave()ing from the original dispatch the recipient dispatch also left from the leave-handling
	dispatch. This is a fatal error because a common requirement for handling the first leave is sending
	an error signal; leaving again suggests that the code is not handling the possibility of a Leave()
	properly, rather than explicitly opting to ignore it.
	*/
	ECFTransLeaveFromLeaveHandler = 9,
	/** Message recieved for an object whose interface cookie has already been deregistered (ie object probably deleted). This indicates a protocol
	failure: the recipient interface's must remain registered until all messages for it have been received. A common approach is the sending of a
	sign-off message guaranteeing no further communication from a node (analogous to a TCP FIN or a CommsChan PIG)
	*/
	ECFTransStaleCookie = 10,
	/** More interfaces have been deregistered than were ever registered. Because the particular cookies aren't tracked it isn't necessarily the
	current deregistration that is at fault
	*/
	ECFExcessInterfaceDeregistration = 11,
	/** Interface registration with a non-null cookie is not allowed, as it's too likely that it represents a fault such as a double registration.
	If it really is a safe and proper use (eg of an aliased cookie) then TCookieOp::SetNull() the cookie first
	*/
	ECFInterfaceRegistrationOverwrite = 12,
	/** Interface not registered for dispatch
	*/
	ECFInterfaceNotRegistered = 13,
	/** Queue sizing error. This is probably an internal fault.
	*/
	ECFInvalidQueueSize = 14,
	};

IMPORT_C void Panic(TCFTransportPanics aReason);

_LIT8(KLogFwTransport, "Trans");

#ifdef SYMBIAN_TRACE_ENABLE
// Markers to enable correlation between utrace and CDU logs.
_LIT8(KLogFwMarker, "Mark");
#endif

typedef TUint16 TWorkerId;
static const TUint16 KInvalidWorkerId = 0xFFFF;
static const TUint16 KMaxWorkerId = 16;

//
// Cookies
//

class TCFMessage;
class RCFChannelMsgQueues;
typedef TInt TId;

//
// Thread knowledge
//

class MGlobalThreadRegister
	{
public:
	virtual TWorkerId UpperBoundWorkerId() const = 0;
	virtual RAllocator& WorkerHeap(TWorkerId aWorkerId) const = 0;
	virtual void PanicWorker(TWorkerId aWorkerId, const TDesC& aCategory, TInt aReason) const = 0;
	};

class MWorkerThreadRegister : public MGlobalThreadRegister
	{
public:
	virtual TWorkerId SelfWorkerId() const = 0;
	};

class TWorkerThreadDataBase
	{
public:
	IMPORT_C void Init(TWorkerId aWorkerId, TBool aHoldingReference);
	IMPORT_C void Clear();
	inline TBool IsValid() const;
public:
	TWorkerId iWorkerId;
	TThreadId iThreadId;
	RThread iThread;
	RAllocator* iHeap;
	TBool iHoldingReference;
	};


class CWorkerThreadDataGlobalsBase : public CBase
	{
protected:
	inline CWorkerThreadDataGlobalsBase();
	inline ~CWorkerThreadDataGlobalsBase();
	inline void ConstructL(TInt aWorkerDataSize, TWorkerId aUpperBoundId);
protected:
	TWorkerThreadDataBase* iWorkers;
	};


template<class TWTD, TInt TUpperThreadIdBound>
class CWorkerThreadDataGlobals : public CWorkerThreadDataGlobalsBase, public MGlobalThreadRegister
	{
public:
	static CWorkerThreadDataGlobals<TWTD, TUpperThreadIdBound>* NewL();

	TWorkerId UpperBoundWorkerId() const;
	TWTD* GetWorkerGlobals(TWorkerId aWorker) const;
	TBool WorkerPresent(TWorkerId aId) const;
	RAllocator& WorkerHeap(TWorkerId aWorkerId) const;
	void PanicWorker(TWorkerId aWorkerId, const TDesC& aCategory, TInt aReason) const;
protected:
	CWorkerThreadDataGlobals();
	inline void ConstructL(TInt aWorkerDataSize, TWorkerId aUpperBoundId);
	};


template<class TWTD, TInt TUpperThreadIdBound>
class CWorkerThreadRegister : public CBase, public MWorkerThreadRegister
	{
public:
	static CWorkerThreadRegister<TWTD, TUpperThreadIdBound>* NewL(TWorkerId aSelfId, CWorkerThreadDataGlobals<TWTD, TUpperThreadIdBound>* aGlobalThreadRegister);
	void SetGlobalThreadRegister(CWorkerThreadDataGlobals<TWTD, TUpperThreadIdBound>* aGlobalThreadRegister);
	TWTD* GetWorkerGlobals(TWorkerId aWorker) const;
	TWorkerId UpperBoundWorkerId() const;
	TBool WorkerPresent(TWorkerId aWorkerId) const;
	RAllocator& WorkerHeap(TWorkerId aWorkerId) const;
	void PanicWorker(TWorkerId aWorkerId, const TDesC& aCategory, TInt aReason) const;
	TWorkerId SelfWorkerId() const;
	TWTD* GetSelfWorkerGlobals() const;
private:
	CWorkerThreadRegister<TWTD, TUpperThreadIdBound>(TWorkerId aSelfId, CWorkerThreadDataGlobals<TWTD, TUpperThreadIdBound>* aGlobalThreadRegister);
private:
	TWorkerId iSelfId;
	CWorkerThreadDataGlobals<TWTD, TUpperThreadIdBound>* iGlobals;
	};

//These APIs have been separated from the transport abstraction (MTransportSender)
//and are here only to satisfy the comms transport implementation
class MCommsTransportSender : public Messages::MTransportSender
	{
public:
	virtual void PostMessage(const Messages::TRuntimeCtxId& aPostFrom, const Messages::TRuntimeCtxId& aPostTo, const Meta::SMetaData& aMessage);

	//Legacy & helpers
	virtual void PostMessage(const TCFMessage& aMessage) = 0;
	virtual void PostMessage(const Messages::TRuntimeCtxId& aPostFrom, const Messages::TRuntimeCtxId& aPostTo, const TDesC8& aMessage) = 0;

#ifdef SYMBIAN_CFTRANSPORT_SUPPORT_SYNC_SEND
	/** The caller intends to block for some out-of-band synchronisation mechanism (eg a semaphore) and so dispatch
	must be done immediately as part of this sending call if the peer object is running in the same worker thread, so
	that the recipient can signal the caller if appropriate. Such synchronisation mechanisms are strongly discouraged
	but exist in some legacy code.

	@deprecated
	*/
	virtual void SendMessageSync(const TCFMessage& aMessage) = 0;
#endif
	};


class MLegacyMessageReceiver
	{
public:
	virtual void DispatchL(const TCFMessage& aMessage, TWorkerId aSenderId) = 0;
	/** If the dispatching function Leave()s then control transfers here. This allows a carefully-written object to avoid
	adding its own TRAP and still provide handling for a Leave, ie an efficiency gain.
	*/
	virtual void OnDispatchLeave(const TCFMessage& /*aMessage*/, TWorkerId /*aSenderId*/, TInt /*aFirstDispatchLeaveReason*/)
		{
		}
	};


class CCommsTransportImpl;
class CCFTransportHooks;

class CCommsTransport : public CBase //PS removed inheritance not to export virtuals and
                                     //hide CCommsTransportImpl. Polymorphism is given
                                     //by CCommsTransportImpl.
	{
public:
	IMPORT_C static CCommsTransport* NewL(MWorkerThreadRegister& aThreadRegister, const Meta::CMetaDataVirtualCtorInPlace* aVirtCtor, CCFTransportHooks* aHooksWalker);
	IMPORT_C ~CCommsTransport();

	// From MTransportReceiver
	IMPORT_C TInt RegisteredCount() const;
	IMPORT_C void RegisterAddress(Messages::TRuntimeCtxId& aCookie);
	IMPORT_C void DeregisterAddress(Messages::TRuntimeCtxId& aCookie);

	// TCFMessage + legacy
	IMPORT_C void RegisterLegacyInterface(MLegacyMessageReceiver* aLegacyInterface);
	IMPORT_C void PostMessage(const TCFMessage& aMessage);
	
private:
	// TCFMessage + legacy
	IMPORT_C void PostMessage(const Messages::TRuntimeCtxId& aPostFrom, const Messages::TRuntimeCtxId& aPostTo, const TDesC8& aMessage);

#ifdef SYMBIAN_CFTRANSPORT_SUPPORT_IMPATIENCE
	IMPORT_C TInt ImpatientPostMessage(const TCFMessage& aMessage);
	IMPORT_C TInt ImpatientPostMessage(const Messages::TRuntimeCtxId& aPostFrom, const Messages::TRuntimeCtxId& aPostTo, const TDesC8& aMessage);
#endif
#ifdef SYMBIAN_CFTRANSPORT_SUPPORT_SYNC_SEND
	IMPORT_C void SendMessageSync(const TCFMessage& aMessage);
#endif

public:
	// Transports peer threads via message queues, normally established by the RootServer via binding commands. The Request Id
	// identifies the specific command being responded to; some callers need to store this for delayed reporting
	IMPORT_C TInt EstablishTransportToPeer(TWorkerId aPeerId, const RCFChannelMsgQueues& aInboundQueues, const RCFChannelMsgQueues& aOutboundQueues);
	IMPORT_C TInt DropTransportToPeer(TWorkerId aPeerId);
	IMPORT_C TBool PeerReachable(TWorkerId aPeerId) const;
	IMPORT_C void SetLastRequestIdConcerningPeer(TWorkerId aPeerId, CommsFW::TId aRequestId);
	IMPORT_C CommsFW::TId LastRequestIdConcerningPeer(TWorkerId aPeerId) const;
	IMPORT_C void SetDropTransportPending(TWorkerId aPeerId, TBool aDropPending);
	IMPORT_C TBool IsDropTransportPending(TWorkerId aPeerId) const;

	IMPORT_C TWorkerId WorkerId() const;
	IMPORT_C Messages::MTransportSender& GetSender();
	IMPORT_C Messages::MTransportReceiver& GetReceiver();

private:
	CCommsTransport()
		{
		}
private:
	CCommsTransportImpl* iImpl;
	};

#include <elements/cftransport.inl>

} // namespace CommsFW

#endif // SYMBIAN_NETWORKING_CFTRANSPORT_H

