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

#include "mm_node.h"
#include "mm_nodepeer.h"
#include "mm_activities.h"
#include <elements/mm_context_internal.h>
#include <elements/mm_log.h>

#include <elements/nm_messages_base.h>
#include <elements/nm_messages_child.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_ElemMeshMachNodC, "ElemMeshMachNodC");
#endif

using namespace Messages;
using namespace MeshMachine;
using namespace NetStateMachine;

//By default we reserve the space generously, to fit even a synchronised activity preallocating space for up to 4 originators.
//Any node, hosting specific activities that may need the preallocation mechanism can choose a more optimal amounts.
//For efficiency reasons it is strongly recommended that any node (or family of nodes) is revisited and an optimal
//amount of space is specified (could be specified in the base class for those nodes or in every type of node separatelly).
static const TUint KDefaultMaxPreallocatedActivityCount = 1;
static const TUint KMaxPreallocatedActivitySize = sizeof(CNodeRetryParallelActivity) + sizeof(APreallocatedOriginators<4>);
static const TUint KDefaultPreallocatedActivityBufferSize = KDefaultMaxPreallocatedActivityCount * KMaxPreallocatedActivitySize;

//-=========================================================
//
//Panics
//
//-=========================================================
_LIT (KMMNodePanic,"MMNodePanic");
enum
	{
	EPanicPreallocatedSpaceAlreadyTaken = 1,
	EPanicPreallocatedSpaceReturnedOther = 2,
	EPanicInvalidClientId = 3,
	EPanicOriginatorIsNotAPeer = 4,
	EPanicNotSynchronisedActivity = 5,
	};

//-=========================================================
//
//AMMNodeBase
//
//-=========================================================
EXPORT_C AMMNodeBase::AMMNodeBase(const TNodeActivityMap& aActivityMap, const TNodeId& aNodeId)
:	ANodeBase(aNodeId),
	TIfStaticFetcherNearestInHierarchy(this),
	iActivityMap(aActivityMap),
	iPreallocatedSpace(NULL)
	{
	};

EXPORT_C void AMMNodeBase::ReturnInterfacePtrL(AMMNodeBase*& aInterface)
    {
    aInterface = this;
    }

EXPORT_C TUint AMMNodeBase::CountActivities(TUint8 aActivitySigId) const
    {
	TInt i = iActivities.Count();
	TUint count = 0;
	while (i>0)
		{
		CNodeActivityBase* a = iActivities[--i];
		if (aActivitySigId == a->ActivitySigId() && !a->IsIdle())
			{
			count++;
			}
		}
	return count;
    }

EXPORT_C TUint AMMNodeBase::CountAllActivities(TUint8 aActivitySigId) const
    {
	TInt i = iActivities.Count();
	TUint count = 0;
	while (i>0)
		{
		CNodeActivityBase* a = iActivities[--i];
		if (aActivitySigId == a->ActivitySigId())
			{
			count++;
			}
		}
	return count;
    }

EXPORT_C TUint AMMNodeBase::CountAllActivities() const
	{
	return iActivities.Count();
	}

EXPORT_C const RPointerArray<CNodeActivityBase>& AMMNodeBase::Activities() const
	{
	return iActivities;
	}

EXPORT_C void AMMNodeBase::ConstructL(TInt aSize)
    {
    //Preallocate space for preallocated activities
    if (aSize==KUseDefaultPreallocatedSize)
    	{
    	//Nodes will usually use the default parameter (-1) and rely on KDefaultPreallocatedActivityBufferSize.
    	//We do not use KDefaultPreallocatedActivityBufferSize as the default argument to avoid publishing of
    	//this constant or KDefaultMaxPreallocatedActivityCount/KMaxPreallocatedActivitySize (either can be
    	//freely changed at any time).
    	aSize = KDefaultPreallocatedActivityBufferSize;
    	}

    if (aSize>0)
    	{
    	PreallocateSpaceL(aSize);
    	}
	}

EXPORT_C AMMNodeBase::~AMMNodeBase()
	{
	//We may still have some idle activities in the iActivities array.
	//This may be considered normal since there is no strict requirement
	//for a node to destroy all of them before going into destruction phase.
	//What is crutial however, is that there are no running activities left.


	for (TInt i = iActivities.Count() - 1; i >= 0; i--)
		{
		CNodeActivityBase* activity = iActivities[i];
		if (activity->IsIdle())
			{
			iActivities.Remove(i);
			activity->Destroy();
			}
		else
			{
			MESH_LOG((KMeshMachineSubTag, _L8("ERROR: ~AMMNodeBase(%08x) - Running activity found! (%08x) %s (%d)"),
			    this, activity, activity->ActivityName(), activity->ActivityId()));
			}
		}

#ifdef _DEBUG
	for (TInt i = iActivities.Count() -1; i >= 0; i--)
		{
		CNodeActivityBase* activity = iActivities[i];
		if (!activity->IsIdle())
			{
			User::Invariant(); //Running activities still present! Please fix your node's cleanup!
			}
		}
#endif

    for (TInt i = iActivities.Count() -1; i >= 0; i--)
		{
		CNodeActivityBase* activity = iActivities[i];
		iActivities.Remove(i);
		activity->Destroy();
		}
    iActivities.Close();

    User::Free(iPreallocatedSpace);
    iPreallocatedSpace = NULL;

	}

EXPORT_C void AMMNodeBase::RemoveClient(const TRuntimeCtxId& aClientId)
	{
	TNodeNullContext ctx(*this);
    RemoveClient(aClientId, ctx);
	}

EXPORT_C void AMMNodeBase::RemoveClient(const TRuntimeCtxId& aClientId, TNodeContextBase& aContext)
	{
	TInt foundAt = 0;
	RNodeInterface* client = DoFindClient(aClientId, foundAt);
	__ASSERT_DEBUG(client, User::Panic(KMMNodePanic, EPanicInvalidClientId));

	//here we need to cancel all activities originating aClientId (except the current one)
	__ASSERT_DEBUG(!client->RecipientId().IsNull(), User::Panic(KSpecAssert_ElemMeshMachNodC, 1));
	AbortActivitiesOriginatedBy(aContext, client->RecipientId());

	ANodeBase::RemoveClient(foundAt);
   	if (aContext.iSender == aClientId)
   	    {
   	    aContext.iPeer = NULL;
   	    }
	}

EXPORT_C CNodeActivityBase* AMMNodeBase::FindActivityById(TUint aActivityId) const
	{
	CNodeActivityBase* a = NULL;
	for (TInt i = iActivities.Count() - 1; i>=0 && a==NULL; i--)
		{
		CNodeActivityBase* comp = iActivities[i];
		if (!comp->IsIdle()	&& comp->ActivityId() == aActivityId)
			{
			a = comp;
			}
		}
	return a;
	}

EXPORT_C CNodeActivityBase* AMMNodeBase::FindAddressedActivity(const TNodeContextBase& aContext) const
	{
	const TNodeCtxId* recipient = address_cast<const TNodeCtxId>(&aContext.iRecipient);
	if (recipient==NULL)
		{
		return NULL;
		}

	CNodeActivityBase* a = NULL;
	for (TInt i = iActivities.Count() - 1; i>=0 && a==NULL; i--)
		{
		CNodeActivityBase* act = iActivities[i];
		const TNodeId& postedTo = act->iPostedToId;
		if (!act->IsIdle()
			&& (postedTo.IsNull() || aContext.iSender == postedTo)
				&& (recipient->NodeCtx() == act->ActivityId()))
			{
			a = act;
			}
		}
	return a;
	}

EXPORT_C void AMMNodeBase::Received(const TNodeSignal::TMessageId aNoPeerMsgIds[], TNodeContextBase& aContext)
    {
	aContext.iReturn = KErrNone; //Clear the return value.
    CNodeActivityBase* sendersActivity = NULL;
    if (!Accept(aContext, sendersActivity) && aContext.iReturn == KErrNone )
        {//start new ones? but only if we know who sent a message or if it's a noPeer message
        //or if it's us sending message to ourselves

	    //is it a message that must have a peer
	    TBool noPeer = EFalse;
	    for (const TNodeSignal::TMessageId* p = &aNoPeerMsgIds[0] ; !p->IsNull() ; ++p)
	    	{
	    	noPeer = (*p == aContext.iMessage.MessageId());
	    	if (noPeer)
	    		{
	    		break;
	    		}
	    	}
        if (noPeer || aContext.IsPeer() || aContext.IsSelf())
            {
			//At this stage we are about to add the peer to the activiy originators.
			//For all "peer requests" we must ensure the peer is in the client's list.
			//If this assertion fires, your environment is critically corrupted.
			//You must repair it by reconsidering your protocols.
			__ASSERT_ALWAYS(noPeer || aContext.iPeer!=NULL, User::Panic(KMMNodePanic, EPanicOriginatorIsNotAPeer));
           	TRAP(aContext.iReturn, StartNewActivitiesL(aContext, sendersActivity));
            }
        else
            {
            //unknown sender => ignore the message
			NM_LOG_ADDRESS_EXT(KMeshMachineSubTag, aContext.iSender, _L8("AMMNodeBase:\tReceived() - Unknown sender. Message must have a peer => ignoring message"));
            }
        }
    else
        {
        __ASSERT_DEBUG(aContext.iMessage.MessageId().IsNull() || aContext.iReturn != KErrNone, User::Panic(KSpecAssert_ElemMeshMachNodC, 2)); //message must be processed
        }
	}

EXPORT_C void AMMNodeBase::HandlePassThroughMessage(TNodeContextBase& aContext)
    {
	if (aContext.iMessage.IsMessage<TEBase::TCancel>())
	    {
	    ASSERT(aContext.iNodeActivity == NULL); //by definition, since nobody accepted this;
	    TInt originatorIndex = KErrNotFound;
		//Please note:
		//The same originator must not attempt to start more than one activities on the same node
		//(so there is only ever one activity on the node with the originator == that this TCancel is addressed to)!
		//In general one originator (identified by a unique channel id) must never originate more
		//than one activities at all.
	    for (TInt i = 0; i < iActivities.Count(); i++)
	    	{
	    	if ((originatorIndex = iActivities[i]->FindOriginator(aContext.iSender)) != KErrNotFound)
	    		{
	    		aContext.iNodeActivity = iActivities[i];
	    		break;
	    		}
	    	}
	    
	    if (aContext.iNodeActivity)
    	    {
    	    ASSERT(originatorIndex != KErrNotFound);
     	    if(aContext.iNodeActivity->iOriginators.Count() == 1) // only if this is the final originator
				{
				aContext.iNodeActivity->Cancel(aContext);
				}
			else
				{
				// just tell this originator that we've stopped with respect to it.. otherwise keep running
				TEBase::TError err(TEBase::TCancel::Id(), KErrCancel);
				aContext.PostToSender(err);
				aContext.iNodeActivity->RemoveOriginator(originatorIndex);
				}
    		}
	    }    
	else if (aContext.iReturn==KErrNone && !aContext.iMessage.MessageId().IsNull())
	    { //this is the pass-through message, forward to .....
        //Find the activity being addressed, if any
		aContext.iNodeActivity = FindAddressedActivity(aContext);
		if (aContext.iNodeActivity)
			{ //Peer message, forward to originators
			//aContext.iSender = Id();
			NM_LOG_MESSAGE_EXT(KMeshMachineSubTag, aContext.iMessage, _L8("AMMNodeBase::HandlePassThroughMessage - message not served by the node but matching activity found=>SEND TO ACTIVITY'S ORIGINATORS"));
			aContext.iNodeActivity->PostToOriginators(aContext.iMessage);
			if (aContext.iMessage.IsMessage<TEBase::TError>())
			    {
			    //An activity received an error=>remove it from active array
			    //This scenario will usually happen when there is no error activity on the node,
			    //or when the error activity decides not to handle this particular error.
			    aContext.iNodeActivity->SetIdle();
			    }
			aContext.iMessage.ClearMessageId();
			}
		else
			{ //A stray message
			//Please note that we should not implement forwarding for non-response messages here.
			//Any non-response message that needs forwarding should use TMessageDispatcher or other
			//supported mechanisms.
			NM_LOG_MESSAGE_EXT(KMeshMachineSubTag, aContext.iMessage, _L8("ERROR: AMMNodeBase::HandlePassThroughMessage - A stray message received!"));
			}
        }
    }


EXPORT_C void AMMNodeBase::HandleMessageReturnValue(TNodeContextBase& aContext)
    {
	if (aContext.iReturn != KErrNone)
		{
		//keep the original activity in case no aCurrentActivity
		if(aContext.iNodeActivity && aContext.iNodeActivity->iOriginators.Count() > 0)
			{
			aContext.iNodeActivity->SetError(aContext.iReturn);
			// Mark the activity for destruction
			// When the activity is destroyed the Originators will be errored
			aContext.iNodeActivity->SetIdle();
			}

		// Check to see if the node was unable to handle an error (e.g., an error recovery activity failed to start
		// because there was insufficient memory).  If so, we manually error the activity and set it to idle causing
		// its originators to be errored.  This is the most we can do if an error activity cannot be run.
		else if(aContext.iMessage.IsMessage<TEBase::TError>())
			{
			CNodeActivityBase* activity = FindAddressedActivity(aContext);
			if(activity)
				{
				activity->SetError(static_cast<TEBase::TError&>(aContext.iMessage).iValue);
				// Mark the activity for destruction
				// When the activity is destroyed the Originators will be errored
				activity->SetIdle();
				}
			}

		else
			{ //No activity == single triple activity or an error from the first transition, so use the sender's message id.
			TEBase::TError err(aContext.iMessage.MessageId(), aContext.iReturn);
			aContext.PostToSender(err);
			}
		aContext.iReturn = KErrNone;
		aContext.iMessage.ClearMessageId();
		}
    }


//N.B. This function may delete the object pointed to by aCurrentActivity.
//So don't use it after this function is called.
//Also, it may delete the node itself, so don't use it after this function is called.
EXPORT_C void AMMNodeBase::PostReceived(TNodeContextBase& aContext)
	{
    HandleMessageReturnValue(aContext);
	HandlePassThroughMessage(aContext);
    SignalActivities();
    }

void AMMNodeBase::SignalActivities()
	{
	TBool awoke = ETrue;
	TInt c = iActivities.Count();
	TNodeNullContext context(*this);

	//Clean up..
	while (awoke)
		{
		awoke = EFalse;
		for (TInt i = iActivities.Count() - 1 ; i>=0 ; --i)
			{
			//Signal to waiting activities if:
			//1) an event was received (& the state has potentialy changed as a result)
			//- remember that an activity does not have to get idle as a result of processing an event which changes the state!!!!
			//2) another waiting activity awoke == reacted to the signal (& the state has potentialy changed as a result)
			//If any signalled activity reacted, the state could change and all other activities need to be signalled again.
			context.iNodeActivity = iActivities[i];
			awoke |= context.iNodeActivity->Signal(context);
			}
		}

	for (TInt i = iActivities.Count() - 1 ; i>=0 ; --i)
		{
		context.iNodeActivity = iActivities[i];
		if(context.iNodeActivity->IsIdle())
			{
			iActivities.Remove(i);
			context.iNodeActivity->Destroy();
			context.iNodeActivity = NULL;
			}
		}
	}

EXPORT_C void AMMNodeBase::AbortActivitiesOriginatedBy(TNodeContextBase& aContext, const TNodeId& aCommsId, TBool aIsNodeBeingDestroyed)
    {
    CNodeActivityBase* caller = aContext.iNodeActivity;
    TBool abortAll = aCommsId.IsNull();

    for (TInt i = iActivities.Count() - 1; i>=0; i--)
        {
        aContext.iNodeActivity = iActivities[i];
		
		// We dont want to abort already idle activities or they may error.		
		if(aContext.iNodeActivity->IsIdle())
			{
			continue;
			}
		
        if (caller != aContext.iNodeActivity)
            {
        	if (abortAll)
        	    {
        	    //Abort the whole activity (Cancel it & error all originators)
        	    aContext.iNodeActivity->Abort(aContext,aIsNodeBeingDestroyed);
        	    }
        	else
      	        {
      	        //Abort for one originator only (Cancel the activity if last originator & error just this one originator)
      	        TInt idx = aContext.iNodeActivity->FindOriginator(aCommsId);
      	        if (KErrNotFound!=idx)
      	        	{
                    TBool canSend = ETrue;
					if(aContext.iNodeActivity->iOriginators.Count() == 1) // only if this is the final originator
						{
						aContext.iNodeActivity->SetError(KErrAbort);
           	        	aContext.iNodeActivity->Cancel(aContext);
           	        	//This is a workaround for CCommsBinderRequest. The proper fix is to abolish the concept of aborting activities.
                        //Aborting activities is a bad idea as an aborted activity isn't given a chance to perform graceful cleanup.
           	        	//Today activities get aborted because their orinators urgently leave. I.e.: they are trully leaving now! Last orders!
           	        	//It is then incorrect to leave the activity d'tor to finish the wrap up - because the node will be gone by then.
           	        	//So whether and when to send an error must be decided here, by this generic code that has no clue on the subtleties
           	        	//of individual activities. If there is no abort - there is urgent leavers. They send TLeaveRequest and they politely
           	        	//wait for the completion and all this code is unnecessary.
           	        	canSend = (aContext.iNodeActivity->Error() != KErrNone);
						}
                    
                    //In the "quiet mode", when the hosting node is being destroyed, we can not afford sending
                    //an error to the node as it would hit void.
                    TNodePeerId& originator = aContext.iNodeActivity->iOriginators[idx];
                    canSend &= !((aIsNodeBeingDestroyed && originator == aContext.NodeId())
                        || aContext.iMessage.IsMessage<TEChild::TLeft>()); 
                    if (canSend)
                        {
                        aContext.iNodeActivity->PostToOriginator(originator, TEBase::TError(aContext.iMessage.MessageId(), KErrAbort).CRef());
                        }
                    aContext.iNodeActivity->RemoveOriginator(idx);
      	        	}
      	        }
            }
        }
    aContext.iReturn = KErrNone;
    aContext.iNodeActivity = caller;
    }

//This fn finds the (first) activity that has an originator matching that passed in
EXPORT_C CNodeActivityBase* AMMNodeBase::FindActivityOriginatedBy(const TRuntimeCtxId& aPeerId)
    {
    for (TInt i = 0; i < iActivities.Count(); i++)
		{
		CNodeActivityBase* a = iActivities[i];
		if (!a->IsIdle()
			&& a->FindOriginator(aPeerId) != KErrNotFound)
		    {
			return a;
		    }
		}
	return NULL;
    }

TBool AMMNodeBase::Accept(TNodeContextBase& aContext, CNodeActivityBase*& aFoundActivity)
    {
//    NM_LOG_MESSAGE_EXT(KMeshMachineSubTag, aContext.iMessage, _L8("AMMNodeBase:\tAccept"));

    //process the existing ones
    for (TInt i = 0; i < iActivities.Count(); i++)
		{
		CNodeActivityBase* a = aContext.iNodeActivity = iActivities[i];
		if(a->Next(aContext))
		    {
	    	if (KErrNone == aContext.iReturn)
				{
        		aContext.iMessage.ClearMessageId(); // message processed and value no longer needed
	        	}

			// If state went idle, aFoundActivity will be deleted later by PostReceived,
			//  this gives us a chance to report error to originators if necessary.
			aFoundActivity = a;
			return ETrue;
		    }

		if (a->FindOriginator(aContext.iSender) != KErrNotFound)
		    {
		    aFoundActivity = a;
		    }
	    }
	return EFalse;
    }

void AMMNodeBase::StartNewActivitiesL(TNodeContextBase& aContext, CNodeActivityBase* aSendersActivity)
    {
    //new activity can only be started if the senders channel hasn't started any yet
    //but we need to check so that we can return an error
    TNodeActivityIter activityIter(iActivityMap);

	const TNodeActivity* activity;
    while (NULL!=(activity = activityIter++) && aContext.iReturn == KErrNone)
        {
		const TStateTriple* first;
		aContext.iNodeActivity = NULL;
		TInt i = iActivities.Count();
		while (i>0 && !aContext.iNodeActivity)
			{
			if (activity->iId == iActivities[--i]->ActivityId())
				{
				aContext.iNodeActivity = iActivities[i];
				}
			}

        if ( (first = CNodeActivityBase::Accept( aContext,
                *activity, NetStateMachine::KExecuteAlways)) != NULL )
            { //we do need to have a state here to go to otherwise we assume it is an activity
            //with one triple only
            if ((first+1)->iTCtor != NULL)
                {
                //is it any of the existing ones?
    	        if (aContext.iNodeActivity)
    	            {
                    __ASSERT_DEBUG(aSendersActivity == NULL || aSendersActivity == aContext.iNodeActivity, User::Panic(KSpecAssert_ElemMeshMachNodC, 3));
                    if (aSendersActivity == NULL)
                        {
                        XNodePeerId originator(aContext.iSender, aContext.iPeer);
        	            aContext.iNodeActivity->StartL(aContext, originator, *first);
                        }
    	            }
    	        else
    	            {

#ifdef SYMBIAN_TRACE_ENABLE
                    if (aSendersActivity)
                        {
						//The sender is already running an activity on this node.
						//There is no definitive reason why to disallow starting of a next one,
						//it is however a sign that something _may_ be going horribly wrong.

						//Usually, this is the case when the same originator sends several requests without
						//caring of the order in which they are going to be completed and the possibility of
						//cancelling them (there exist no way of targetting TCancel to more than one activity).

						//Also, an error activity may be starting and producing this warning (but please note that
						//error activity has no originators).

						//Please also see note in CNodeActivityBase::Next()!
                        MESH_LOG_CONTEXT_EXT(KMeshMachineSubTag, aContext, _L8("WARNING: AMMNodeBase:\tStartNewActivitiesL - sender is starting an extra activity - TCancel will not work!"));
						TInt i = iActivities.Count();
                        MESH_LOG((KMeshMachineSubTag, _L8("\tThe sender has already started %d activities:"), i));
						while (i>0)
							{
							if (iActivities[--i]->FindOriginator(aContext.iSender) >= 0)
								{
		                        MESH_LOG((KMeshMachineSubTag, _L8("\t(%08x) %s (%d)"), aSendersActivity, aSendersActivity->ActivityName(), aSendersActivity->ActivityId()));
								}
							}
                        }
#endif
    	            //create a new one then
    	            TRAPD(err, StartActivityL(aContext, *activity, *first));
    	            if(err!=KErrNone)
    	            	{
	                    MESH_LOG((KMeshMachineSubTag,
	                        _L8("ERROR: AMMNodeBase %08x:\tStartNewActivitiesL - Activity failed to start due to error %d!"),
	                        this, err));

	                    User::Leave(err);
    	            	}
    	            }
                }
        	aContext.iMessage.ClearMessageId(); //message processed
    	    break;//only one activity can start since we have one activity per
    	    //channelId. (ChannelId == CommsId + activityId)
            }
        }
    }

void AMMNodeBase::StartActivityL(TNodeContextBase& aContext, const TNodeActivity& aActivitySig, const NetStateMachine::TStateTriple& aFirst)
    {
    CNodeActivityBase* a = aActivitySig.iCtor(aActivitySig,*this);
    if (iActivities.Find(a)==KErrNotFound)
    	{
    	//The activity did not add itself to the list in any special way, append it here
	    CleanupStack::PushL(a);
   		a->AppendActivityL();
	    CleanupStack::Pop(a);
		}
	//assign only after the activity is successfully appended
	aContext.iNodeActivity = a;

    //if StartL leaves the "a" will be removed from the array and deleted in ::PostReceived
    //since it will be idle
    XNodePeerId originator(aContext.iSender, aContext.iPeer);
    a->StartL(aContext, originator, aFirst);
    }

void AMMNodeBase::PreallocateSpaceL(TUint aSize)
	{
	__ASSERT_DEBUG(aSize>0, User::Panic(KSpecAssert_ElemMeshMachNodC, 4));
	// Reserve extra space for the maximum number of preallocated activities supported by this node
	// to ensure they can be added to the activities list while the system is out of memory.  We also
	// add enough space for extra data strucures to store the free list - the caller cannot reserve this
	// space without knowledge of the preallocation implementation so we do this ourselves.
	TUint maxPreallocatedActivities = aSize / KMaxPreallocatedActivitySize;
	iActivities.ReserveL(iActivities.Count() + maxPreallocatedActivities);
	__ASSERT_DEBUG(iPreallocatedSpace==NULL, User::Panic(KSpecAssert_ElemMeshMachNodC, 5));
    iPreallocatedSpace = User::AllocZL(aSize + sizeof(TUint) + maxPreallocatedActivities * sizeof(TAny*));
    *reinterpret_cast<TUint*>(iPreallocatedSpace) = maxPreallocatedActivities;
	}

TUint AMMNodeBase::MaxPreallocatedActivityCount() const
	{
	// Maximum number of activities that can be preallocated is stored at the beginning of the buffer.
	if(iPreallocatedSpace)
		{
		return *reinterpret_cast<TUint*>(iPreallocatedSpace);
		}
	return 0;
	}

TAny* AMMNodeBase::GetPreallocatedCell(TUint aIndex) const
	{
	// Calculate the offset of the start of the preallocated space after the free list.
	TUint8* bufferStart = reinterpret_cast<TUint8*>(iPreallocatedSpace) + sizeof(TUint*) + MaxPreallocatedActivityCount() * sizeof(TAny*);

	return bufferStart + aIndex * KMaxPreallocatedActivitySize;
	}

TAny* AMMNodeBase::BorrowPreallocatedSpace(TUint aSize)
	{
	MESH_LOG((KMeshMachineSubTag, _L8("AMMNodeBase %08x:\tBorrowPreallocatedSpace (%d)"),this,aSize));

	__ASSERT_ALWAYS(iPreallocatedSpace, User::Panic(KMMNodePanic, EPanicPreallocatedSpaceAlreadyTaken));
	__ASSERT_ALWAYS(aSize <= KMaxPreallocatedActivitySize, User::Panic(KMMNodePanic, EPanicPreallocatedSpaceAlreadyTaken));

	#ifdef SYMBIAN_TRACE_ENABLE
		if(!iPreallocatedSpace)
			{
			MESH_LOG((KMeshMachineSubTag, _L8("ERROR AMMNodeBase %08x:\tBorrowPreallocatedSpace - Preallocated space has not been allocated!"), this));
			}
		if(aSize>KMaxPreallocatedActivitySize)
			{
			MESH_LOG((KMeshMachineSubTag, _L8("ERROR AMMNodeBase %08x:\tBorrowPreallocatedSpace - Size exceeds maximum limit for a single allocation (%d was requested but only %d is available)!"), this, aSize, KMaxPreallocatedActivitySize));
			}
	#endif

	TAny* ptr = NULL;
	TUint maxPreallocatedActivities = MaxPreallocatedActivityCount();
	TAny** freeList = reinterpret_cast<TAny**>(reinterpret_cast<TUint8*>(iPreallocatedSpace) + sizeof(TUint));
	TUint index = 0;

	// Search the free list to see if any space is available.
	for(; index < maxPreallocatedActivities; index ++)
		{
		if(!freeList[index])
			{
			// This cell is now allocated to the calling activity.
			ptr = freeList[index] = GetPreallocatedCell(index);

			// Zero the cell so that any object allocated will have the expected initial zero fill.
			memset(ptr, 0, KMaxPreallocatedActivitySize);

			break;
			}
		}

	// Check to make sure a free cell was found.
	bool freeCellFound = index < maxPreallocatedActivities;
	if(!freeCellFound)
		{
		MESH_LOG((KMeshMachineSubTag, _L8("ERROR AMMNodeBase %08x:\tBorrowPreallocatedSpace - All %d preallocation cells have been allocated!"), this, maxPreallocatedActivities));
		__ASSERT_ALWAYS(freeCellFound, User::Panic(KMMNodePanic, EPanicPreallocatedSpaceAlreadyTaken));
		}

	return ptr;
	}

void AMMNodeBase::ReturnPreallocatedSpace(TAny* aSpace)
	{
	MESH_LOG((KMeshMachineSubTag, _L8("AMMNodeBase %08x:\tReturnPreallocatedSpace"), this));

	TUint maxPreallocatedActivities = MaxPreallocatedActivityCount();
	TAny** freeList = reinterpret_cast<TAny**>(reinterpret_cast<TUint8*>(iPreallocatedSpace) + sizeof(TUint));
	TUint index = 0;

	// Search the free list to return the cell.
	for(; index < maxPreallocatedActivities; index ++)
		{
		if(freeList[index] == aSpace)
			{
			// Mark this cell as free.
			freeList[index] = NULL;

			break;
			}
		}

	bool allocatedCellFound = index < maxPreallocatedActivities;
	if(!allocatedCellFound)
		{
		MESH_LOG((KMeshMachineSubTag, _L8("ERROR AMMNodeBase %08x:\tReturnPreallocatedSpace - the returned pointer 0x%08X is invalid!"), this, aSpace));
		__ASSERT_DEBUG(allocatedCellFound, User::Panic(KMMNodePanic, EPanicPreallocatedSpaceReturnedOther));
		}
	}


//-=========================================================
//
//AMMNodeIdBase
//
//-=========================================================
EXPORT_C const TNodeId& AMMNodeIdBase::NodeId() const
    {
    return Messages::ANodeId::Id();
    }

//-=========================================================
//
//XNodePeerId
//
//-=========================================================
EXPORT_C XNodePeerId::XNodePeerId(const TRuntimeCtxId& aPeerId, RNodeInterface* aPeer, CBase* aInfo)
:	TNodePeerId(aPeerId, aPeer),
	iInfo(aInfo)
	{
	}

EXPORT_C void XNodePeerId::Destroy()
	{
	delete iInfo;
	iInfo = NULL;
	}


