// 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
 @publishedPartner
 @released
*/

#ifndef SYMBIAN_NM_INTERFACES_H
#define SYMBIAN_NM_INTERFACES_H

//#include <elements/nm_signals.h>
#include <elements/nm_address.h>
#include <elements/nm_log.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_ElemNodeMessIntH, "ElemNodeMessIntH");
#endif

namespace Messages
{

class MTransportSender;
class ANode;
class TSignalBase;


class TClientType
/*
TClientType defines a technology agnostic collection of enums for types and
flags describing various traits of peers. It is also a placeholder for these
values. When implementing a relation with a peer, TClientType (or its technology
specific override) can be used to collect informations about the peers.
@see RNodeInterface
*/
	{
public:
    enum TType
        {
        //-=============================================
        //
        // 1. Types 32bits (a client can only have one type)
        //
        //-=============================================
        EAll            = 0xFFFFFFFF,
        EUnknown        = 0x00000000,
        ERegistrar      = 0x00000001, //Useful general purpose role.
                                      //Used to represent simple non-standard or one-off or transient
                                      //roles which now do not need to be specially defined for these
                                      //simple and/or rare relationships.
        EWorker         = 0x00000002, //Useful general purpose role.

		/**
		@internalTechnology
		Reserved types that can change at any time and must NOT be used under any circumstances.
		Please take care when defining derived client types (do not reference ELastReservedType).
		*/
        ELastReservedType_InternalTechnology_CanBeReusedAtAnyTime_DoNotUseInCode = 0x00000080,
		};

	enum TFlags
		{
        //-=============================================
        //
        // 2. Flags 32bits (a client can have many flags)
        //
        //-=============================================
        ELeaving        = 0x00000001, //Client has been sent the final goodbye message (e.g TClientLeavingRequest)
                             //and we are awaiting the channel clear confirmation (e.g. TLeaveComplete).
                             //No further communication with this client is safe as it assumes we discard
                             //any references to it. For example it could destruct itself etc.
		EAdministrative = 0x00000002, //These clients perform some administrative tasks on our node.

		/**
		@internalTechnology
		Reserved flags that can change at any time and must NOT be used under any circumstances.
		Please take care when defining derived client flags (do not reference ELastReservedFlag).
		*/
        ELastReservedFlag_InternalTechnology_CanBeReusedAtAnyTime_DoNotUseInCode = 0x00000080
        };

public:
	IMPORT_C static const TClientType& NullType();

	TClientType()
	:	iClientType(EUnknown),
	    iClientFlags(0)
		{
		}

	TClientType(TUint aClientType, TUint aClientFlags = 0)
	:	iClientType(aClientType),
		iClientFlags(aClientFlags)
		{
		}

	TClientType(const TClientType& aClientType)
	:	iClientType(aClientType.iClientType),
		iClientFlags(aClientType.iClientFlags)
		{
		}

	TBool IsNull() const
	    {
	    return iClientType == 0 && iClientFlags == 0;
	    }

	TUint32 Type() const
	    {
	    return iClientType;
	    }

	TUint32 Flags() const
	    {
	    return iClientFlags;
	    }

	TUint32 SetFlags(TUint32 aFlags)
	    {
	    return iClientFlags |= aFlags;
	    }

	TUint32 ClearFlags(TUint32 aFlags)
	    {
	    return iClientFlags &= ~aFlags;
	    }

protected:
    TUint32 iClientType;
    TUint32 iClientFlags;
	};

//None of the specified
struct TNoMatchPolicy
	{
	inline static TBool Match(TUint32 aVal, TUint32 aMatch)
		{
        return (aVal & aMatch) == 0;
		}
	};

//Any of the specified, can be more, can be less
struct TAnyMatchPolicy
	{
	inline static TBool Match(TUint32 aVal, TUint32 aMatch)
		{
        return (aVal & aMatch) != 0;
		}
	};

//Any of the specified, both can be null, can be more, can be less
struct TExactOrAnyMatchPolicy
	{
	inline static TBool Match(TUint32 aVal, TUint32 aMatch)
		{
		return (aVal == aMatch) || (aVal & aMatch) != 0;
		}
	};

//All of the specified, can't be more, can be less
struct TSubSetMatchPolicy
	{
	inline static TBool Match(TUint32 aVal, TUint32 aMatch)
		{
        return (aVal & aMatch) == aVal;
		}
	};

//Any of the specified, can be more, can't be less
struct TSuperSetMatchPolicy
	{
	inline static TBool Match(TUint32 aVal, TUint32 aMatch)
		{
        return (aVal & aMatch) == aMatch;
		}
	};

//All of the specified, can't be less, can't be more
struct TExactMatchPolicy
	{
	inline static TBool Match(TUint32 aVal, TUint32 aMatch)
		{
		return aVal == aMatch;
		}
	};

template <typename TTYPEMATCHPOLICY = TSubSetMatchPolicy, typename TFLAGSMATCHPOLICY = TSuperSetMatchPolicy>
struct TMatchPolicy
	{
	inline static TBool Match(const TClientType& aType, const TClientType& aMatch)
		{
		return TTYPEMATCHPOLICY::Match(aType.Type(),aMatch.Type())
			&& TFLAGSMATCHPOLICY::Match(aType.Flags(),aMatch.Flags());
		}
	};

template <typename TINCMATCHPOLICY = TMatchPolicy<TSubSetMatchPolicy,TSuperSetMatchPolicy>, typename TEXCMATCHPOLICY = TMatchPolicy<TExactMatchPolicy,TExactMatchPolicy> >
struct TTypeMatchPolicy
	{
	inline static TBool Match(const TClientType& aType, const TClientType& aInclude, const TClientType& aExclude)
		{
		TBool inc = TINCMATCHPOLICY::Match(aType, aInclude);
		TBool exc = aExclude.IsNull() || !TEXCMATCHPOLICY::Match(aType, aExclude);
		return inc && exc;
		}
	};

//We could be using partially specialised typedefs here,
//if they only were supported by C++ at the time of writing this code...
//                         INCType   |     Type                  Flag              EXCType    |      Type               Flag
//                       ----------------------------------------------           -------------------------------------------------
typedef TTypeMatchPolicy<TMatchPolicy<TSubSetMatchPolicy,  TSuperSetMatchPolicy>, TMatchPolicy<TSuperSetMatchPolicy,TAnyMatchPolicy> > TDefaultClientMatchPolicy;
typedef TTypeMatchPolicy<TMatchPolicy<TSuperSetMatchPolicy,TSuperSetMatchPolicy>, TMatchPolicy<TSuperSetMatchPolicy,TAnyMatchPolicy> > TFlagsOnlyClientMatchPolicy;
typedef TTypeMatchPolicy<TMatchPolicy<TSubSetMatchPolicy,  TNoMatchPolicy>,       TMatchPolicy<TSuperSetMatchPolicy,TAnyMatchPolicy> > TExcludeTypeAndFlagClientMatchPolicy;
typedef TTypeMatchPolicy<TMatchPolicy<TSubSetMatchPolicy, TExactOrAnyMatchPolicy>, TMatchPolicy<TSuperSetMatchPolicy,TAnyMatchPolicy> > TExactOrAnyIncludeFlagsClientMatchPolicy;


class RClientInterface
/*
Represents an opaque recipient of messages with no assumptions about its
type (i.e.: it's not necesserilly a node and the only thing known about it, is its
TRuntimeCtxId). Relations with such recipients should be implemented by declaring
RClientInterface as members.
@see RNodeInterface
*/
	{
public:
	IMPORT_C RClientInterface();
	IMPORT_C virtual ~RClientInterface(); //Derived types may be used via base ptrs
	IMPORT_C void Open(const TRuntimeCtxId& aPostTo, MTransportSender* aSender = NULL);
	IMPORT_C void Close(); // stomp members
	IMPORT_C TBool operator==(const TRuntimeCtxId& aRHS) const;
	IMPORT_C TBool operator==(const RClientInterface& aRHS) const;

	IMPORT_C TBool IsOpen() const;

	inline const TRuntimeCtxId& RecipientId() const
		{
		return *reinterpret_cast<const TRuntimeCtxId*>(iRecipientAddress);
		}

	IMPORT_C static void OpenPostMessageClose(const TRuntimeCtxId& aPostFrom, const TRuntimeCtxId& aPostTo, const TSignalBase& aMessage);
	IMPORT_C void PostMessage(const TRuntimeCtxId& aPostFrom, const TSignalBase& aMessage) const;

protected:
	void PostMessage(const TRuntimeCtxId& aPostFrom, const TRuntimeCtxId& aPostTo, const TSignalBase& aMessage) const;
	inline TRuntimeCtxId& RecipientRef()
		{
		return *reinterpret_cast<TRuntimeCtxId*>(iRecipientAddress);
		}

	inline const TRuntimeCtxId& RecipientRef() const
		{
		return *reinterpret_cast<const TRuntimeCtxId*>(iRecipientAddress);
		}

private:
	MTransportSender* iTransportSender;
	TUint8 iRecipientAddress[__Align8(TRuntimeCtxId::KMaxInlineAddressSize)];
	};


class RNodeInterface : public RClientInterface
/*
RNodeInterface represents a relation with a node (ANode). It does so by:
- storing the node's address (TNodeId)
- collecting informations about the node (::Type & ::Flags).
Classes (typically nodes) should implement relations with another nodes
by declaring RNodeInterface as members.
@see TClientType
*/
	{
	friend class TClientIterBase;

public:
	IMPORT_C void Open(TNodeId aPostTo, const TClientType& aClientType = TClientType::NullType(), MTransportSender* aSender = NULL);
	IMPORT_C void Close();
	IMPORT_C TBool operator==(const RNodeInterface& aRHS) const;

	inline TBool operator==(const TRuntimeCtxId& aRHS) const
		{
		return RClientInterface::operator==(aRHS);
		}

	IMPORT_C void PostMessage(const TRuntimeCtxId& aPostFrom, const TNodeId::TRemainder& aPostTo, const TSignalBase& aMessage) const;
	inline void PostMessage(const TRuntimeCtxId& aPostFrom, const TSignalBase& aMessage) const
		{
		#ifdef SYMBIAN_TRACE_ENABLE
			if(Flags() & TClientType::ELeaving)
				{
				NM_LOG_START_BLOCK(KNodeMessagesSubTag, _L8("ERROR: Post while node is leaving"));
				NM_LOG_ADDRESS_EXT(KNodeMessagesSubTag, aPostFrom, _L8("From:"));
				NM_LOG_ADDRESS_EXT(KNodeMessagesSubTag, RecipientId(), _L8("To:"));
				NM_LOG_MESSAGE_EXT(KNodeMessagesSubTag, aMessage, _L8("Msg:"));
				NM_LOG_END_BLOCK(KNodeMessagesSubTag, KNullDesC8);
				}
		#endif
		__ASSERT_DEBUG(!(Flags() & TClientType::ELeaving), User::Panic(KSpecAssert_ElemNodeMessIntH, 1)); //OOOOOPS! What are you doing??
		RClientInterface::PostMessage(aPostFrom, aMessage);
		}

	inline const TNodeId& RecipientId() const
		{
		return address_cast<TNodeId>(RClientInterface::RecipientId());
		}

	TClientType& ClientType()
		{
		return iClientType;
		}

	TUint32 Type() const
	    {
	    return iClientType.Type();
	    }

	TUint32 Flags() const
	    {
	    return iClientType.Flags();
	    }

	TUint32 SetFlags(TUint32 aFlags)
	    {
#if defined(_DEBUG) && defined(NM_LOG_FLAG_SETTINGS)
	    // Note that this generates quite alot of logging, hence is not turned on by default.
	    // Note also that (a minority of) flag setting may also take place in TNodePeerId.  
	    const TUint32 oldFlags = Flags();
	    const TUint32 newFlags = iClientType.SetFlags(aFlags);
	    if (oldFlags != newFlags)
	    	{
	    	NM_LOG((KNodeMessagesSubTag, _L8("RNI::SetFlags(%x) ANode=%08x, %x->%x"), aFlags, &RecipientId().Node(), oldFlags, newFlags));
	    	}
	    return newFlags;
#else
	    return iClientType.SetFlags(aFlags);
#endif
	    }

	TUint32 ClearFlags(TUint32 aFlags)
	    {
#if defined(_DEBUG) && defined(NM_LOG_FLAG_SETTINGS)
	    const TUint32 oldFlags = Flags();
	    const TUint32 newFlags = iClientType.ClearFlags(aFlags);
	    if (oldFlags != newFlags)
	    	{
	    	NM_LOG((KNodeMessagesSubTag, _L8("RNI::ClearFlags(%x) ANode=%08x, %x->%x"), aFlags, &RecipientId().Node(), oldFlags, newFlags));
	    	}
	    return newFlags;
#else
	    return iClientType.ClearFlags(aFlags);
#endif
	    }

protected:
	TClientType iClientType;
	};

class RRequestOriginator
/*
RRequestOriginator is a helper class that ackowledges the very common scenario
where an activity running at a remote node sends a request and that request
needs to be replied to. RRequestOriginator is associated with a node (holds
a reference to RNodeInterface) as well as stores the TNodeId::TReminder of
the address in order to identify the activity running at that node.
*/
    {
public:
	IMPORT_C TInt Open(RNodeInterface& aNode, const TRuntimeCtxId& aRequestOriginator);
	IMPORT_C void Open(RRequestOriginator& aOriginalRequest);
	IMPORT_C void Close();

	IMPORT_C TBool operator==(const RRequestOriginator& aRHS) const;
	IMPORT_C TBool operator==(const TRuntimeCtxId& aRHS) const;

	IMPORT_C TBool IsOpen() const;

	Messages::RNodeInterface& Node()
		{
		__ASSERT_DEBUG(IsOpen(), User::Panic(Messages::KMessagesPanic,Messages::EClientNotValidPanic));
		return *iNode;
		}
	const Messages::RNodeInterface& Node() const
		{
		__ASSERT_DEBUG(IsOpen(), User::Panic(Messages::KMessagesPanic,Messages::EClientNotValidPanic));
		return *iNode;
		}

	Messages::TNodeId::TRemainder& Remainder()
		{
		Node(); //Make sure 'this' is openned
		return iRemainder;
		}
	const Messages::TNodeId::TRemainder& Remainder() const
		{
		Node(); //Make sure 'this' is openned
		return iRemainder;
		}


	IMPORT_C void PostMessage(const TRuntimeCtxId& aPostFrom, const TSignalBase& aMessage) const;
	IMPORT_C void ReplyTo(const TRuntimeCtxId& aReplyFrom, const TSignalBase& aMessage);

private:
	Messages::TNodeId::TRemainder iRemainder;
	Messages::RNodeInterface* 	  iNode;
    };

//
// TClientIterBase
class TClientIterBase
	{
public:
	typedef RPointerArray<RNodeInterface> TClientArray;

public:
	inline RNodeInterface* operator++(TInt /*aInd*/) //-postfix
	    {
	    RNodeInterface* cli = Find(iIndex, +1, 1);
	    iIndex++;
	    return cli;
	    }

	inline RNodeInterface* operator++()              //-prefix
	    {
	    ++iIndex;
	    return Find(iIndex, +1, 1);
	    }

	inline RNodeInterface* operator--(TInt /*aInd*/) //-postfix
	    {
	    RNodeInterface* cli = Find(iIndex, -1, 1);
	    iIndex--;
	    return cli;
	    }

	IMPORT_C RNodeInterface* operator[](TInt aInd);

	inline RNodeInterface* operator*()
		{
		return Find(iIndex, +1, 0);
		}

    void SetToLast()
        {
        iIndex = iClients.Count() - 1;
        }

	void Reset()
		{
		iIndex = 0;
		}

protected:
	virtual TBool TypeMatch(RNodeInterface& aClient) const = 0;
	explicit TClientIterBase(const RPointerArray<RNodeInterface>& aClients)
    :	iClients(aClients),
    	iIndex(0)
    	{
		}

    IMPORT_C RNodeInterface* Find(TInt& aInd, TInt aDir, TInt aCount);
    const RPointerArray<RNodeInterface>& iClients;
    TInt iIndex;
	};

//
// TClientIter
template<class TTYPEMATCHPOLICY = TDefaultClientMatchPolicy>
class TClientIter : public TClientIterBase
	{
public:
	explicit TClientIter(const RPointerArray<RNodeInterface>& aClients, const TClientType& aInclude, const TClientType& aExclude = TClientType::NullType())
	:	TClientIterBase(aClients), iInclude(aInclude), iExclude(aExclude) {};

private:
	virtual TBool TypeMatch(RNodeInterface& aClient) const
		{
		return TTYPEMATCHPOLICY::Match(aClient.ClientType(), iInclude, iExclude);
		}

	TClientType iInclude;
	TClientType iExclude;
	};


} //namespace Messages

#endif
//SYMBIAN_NM_INTERFACES_H


