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


#include "cntviewprivate.h"
#include "cviewsubsessions.h"
#include "ccntipccodes.h"
#include "ccntdbmanager.h"
#include "ccntserver.h"
#include <cntviewstore.h>
#include "ccntlogger.h"
#include "ccntstatemachine.h"


extern void DebugLogViewNotification(const TDesC& aMethod, const TContactViewEvent& aEvent);


CViewSubSessionQueue::CViewSubSessionQueue()
	{
	}


CViewSubSessionQueue::~CViewSubSessionQueue()
	{
	iEvents.Close();
	}


void CViewSubSessionQueue::QueueEvent(const TContactViewEvent& aEvent)
	{
	const TInt KInvalidValueForRemoteView = -1;

	TBool haveToAddEventInQueue = ETrue;

 	if (iRequestPending)
 		{
		if(aEvent.iEventType == TContactViewEvent::EItemAdded)
			{
			// is this first event sent? If yes and if the event is an 
			// EItemAdded event send first a fake event. 
			// This is because all EItemAdded should be sorted before send them

			TContactViewEvent event;
			event.iEventType = TContactViewEvent::EItemAdded;
			event.iContactId = KInvalidValueForRemoteView;
			event.iInt = KInvalidValueForRemoteView;
			SendEventL(event);
			}
		else
			{
			SendEventL(aEvent);
			haveToAddEventInQueue = EFalse;
			}
 		}

	if (haveToAddEventInQueue  && !iQueueError)
		{
		// There are two requirements for this queue of events:
		// 1. The iInt values for events in the Q at any given point in time
		//    should match the index of the underlying localview array. i.e If the addition/removal
		//    of a contact has caused the position of other contacts items to change in the underlying localview
		//    then previous events sent by them in this Q will have old iInt values and these need to be corrected.
		
		// 2. When the client gets these events in order, it should have a valid view after every event,
		//    ie. if we have 2 additions, at positions 0 and 1, 
		//    we cannot send the event for addition at position 1 before the addition at position 0.
		//	
		// These requirements are fulfilled using the following algorithm.
		// Events are inserted in the queue using following two condition
		// 1. EItemAdded -
		//    1.1 Find if an existing EItemAdded events iInt is greater or equal with incoming events iInt.
		//        if found,insert event before the found one and increment the iInt of the rest event by 1
		//    1.2 if no match is found with same iInt, modify its iInt value with incoming events iInt, 
		//        then append to the event queue.
		//	  1.3 If no EItemAdded events are in queue, append this one to Queue.
		// 2. EItemRemoved -
		//    2.1 Find if an existing EItemAdded events iInt matches with incoming EItemRemoved events iInt.
		//        if found, then remove that EItemAdded event from the Queue and decrement rest of the 
		//        events iInt by 1.
		//    2.2 if no match is found with same iInt, then insert the event before next greater iInt event
		//        and then decerement the iInt value of rest of the event by 1.
		//	  2.3 if no events greater than this  then append this one to Queue.
		
		TContactViewEvent event;
		event.iEventType = aEvent.iEventType;
		event.iContactId = aEvent.iContactId;
		event.iInt = aEvent.iInt;		
		TInt eventsCount = iEvents.Count();		
		TUint pos=0;		
		if( event.iEventType == TContactViewEvent::EItemAdded )
			{	
			TInt lastItemRemovedPosition = KErrNotFound;
			//first check if this add event is not generated by a contact item change
			for( pos=0; pos<eventsCount; ++pos ) 
				{
				if( iEvents[pos].iContactId == aEvent.iContactId && 
							iEvents[pos].iEventType == TContactViewEvent::EItemRemoved )
					{
					lastItemRemovedPosition = pos;
					}
				}
			if( lastItemRemovedPosition != KErrNotFound ) 
				{
				++lastItemRemovedPosition;
				if(lastItemRemovedPosition < eventsCount)
					{
					for( pos = lastItemRemovedPosition-1; pos < eventsCount;++pos ) 
						{
						if(iEvents[pos].iInt >= aEvent.iInt && 
								iEvents[pos].iContactId != aEvent.iContactId)
							{
							iEvents[pos].iInt++;
							}
						}
					iQueueError = iEvents.Insert(aEvent, lastItemRemovedPosition);		
					}
				else
					{
					iQueueError = iEvents.Append(aEvent);	
					}
				}
			else
				{				
				TBool haveToAppendEvent = ETrue; 			
				for( pos=0; pos<eventsCount; ++pos ) 
					{
					if(iEvents[pos].iEventType == TContactViewEvent::EItemAdded)
						{
						if(iEvents[pos].iInt >= event.iInt)
							{
							iQueueError = iEvents.Insert(event, pos);
							eventsCount=iEvents.Count();
							for(TUint loop=pos+1; loop<eventsCount; ++loop)
								{
								iEvents[loop].iInt++;
								}						
							haveToAppendEvent = EFalse;										
							break;
							}				
						}
					}
				if( haveToAppendEvent )
					{
					iQueueError = iEvents.Append(event);
				 	}
				}
			}
		else 
			{
			if(event.iEventType == TContactViewEvent::EItemRemoved)
				{
				TBool anyEventWithSameInt = EFalse;												
				for( pos=0; pos<eventsCount; ++pos ) 
					{
					if(iEvents[pos].iEventType == TContactViewEvent::EItemAdded)
						{								
						if(iEvents[pos].iInt == event.iInt)
							{
							iEvents.Remove(pos);
							eventsCount=iEvents.Count();
							for(TUint loop=pos; loop<eventsCount; ++loop)
								{
								iEvents[loop].iInt--;
								}						
							anyEventWithSameInt=ETrue;	
							break;									
							}					
						}
					}						
				pos=0;	
				if(!anyEventWithSameInt) 
					{
					while(pos < eventsCount && iEvents[pos].iInt <= event.iInt)
						{
						pos++;
						}
					if(pos!=eventsCount)
						{	
						iQueueError = iEvents.Insert(event, pos);
						eventsCount=iEvents.Count();
						for(TUint loop=pos+1; loop<eventsCount; ++loop)
							{
							iEvents[loop].iInt--;
							}	
						}
					else
						{
						iQueueError = iEvents.Append(event);
						}
					}
				}
			else
				{
				iQueueError = iEvents.Append(event);	
				}
			}
		DEBUG_PRINTVN2(__VERBOSE_DEBUG__,_L("[CNTMODEL] CViewSubSessionQueue::QueueEvent(): ->Q:"), event);
		}
	}


void CViewSubSessionQueue::RequestEvent(const RMessage2& aMessage)
	{
	__ASSERT_DEBUG(!iRequestPending,User::Leave(KErrAlreadyExists)); // can only leave in debug mode
	if (!iRequestPending)
		{
		iMessage=aMessage;
		iRequestPending=ETrue;
		if (iQueueError)
			{
			TContactViewEvent errorEvent(TContactViewEvent::EServerError,iQueueError);
			iQueueError=KErrNone;
			SendEventL(errorEvent);
			}
		else if (iEvents.Count()>0)
			{
			SendEventL(iEvents[0]);
			iEvents.Remove(0);
			}
		}
	}


void CViewSubSessionQueue::CancelEventRequest()
	{
	if (iRequestPending)
		{
		iMessage.Complete(KErrCancel);
		iRequestPending=EFalse;
		}
	}

/**
Send contact view event.

@leave KErrNotFound In debug mode only, if there is a queue error.
@leave KErrNotReady In debug mode only, if there is already an existing request still pending.
@leave KErrBadDescriptor If there is a error writing aEvent to the RMessage2 to be sent.
*/
void CViewSubSessionQueue::SendEventL(const TContactViewEvent& aEvent)
	{
	__ASSERT_DEBUG(!iQueueError,User::Leave(KErrNotFound));

	if (!iRequestPending)
		{
		return;
		}

   DEBUG_PRINTVN2(__VERBOSE_DEBUG__,_L("[CNTMODEL] CViewSubSessionQueue::SendEventL(): Q->:"), aEvent);

	TRAPD(err,iMessage.WriteL(KSlot0,TPckgC<TContactViewEvent>(aEvent)));
	if (err)
		{
		iRequestPending=EFalse;
		User::Leave(KErrBadDescriptor);//iMessage is completed in CCntSession::ServiceError()
		}
	else
		{
		iMessage.Complete(KErrNone);
		}

	iRequestPending=EFalse;
	}


/**
Called if derived class ServiceL()'s do not consume the opcode.
*/
TInt CViewSubSessionBase::ServiceL(const RMessage2& aMessage)
	{
	TInt reply(KErrNone);
	switch (aMessage.Function())
		{
		case ECntViewCount:
			CountL(aMessage);
			break;
		case ECntViewAt:
			reply=AtL(aMessage);
			break;
		case ECntViewContactAtLength:
			reply=ContactAtLengthL(aMessage);
			break;
		case ECntViewContactAt:
			ContactAtL(aMessage);
			break;
		case ECntViewFind:
			FindL(aMessage);
			break;
		case ECntAllFieldsLength:
			reply=GetAllFieldsLengthL(aMessage);
			break;
		case ECntAllFieldsText:
			GetAllFieldsTextL(aMessage);
			break;
		case ECntContactMatchingCriteriaExternalizedSize:
			ContactMatchingCriteriaExternalizedSizeL(aMessage);
			break;
		case ECntGetContactMatchingCriteria:
			GetContactMatchingCriteriaL(aMessage);
			break;
		case ECntGetIncludedTypes:
			GetIncludedTypesL(aMessage);
			break;
		case ECntRequestViewEvent:
			RequestViewEvent(aMessage);
			reply=KErrNoComplete;
			break;
		case ECntCancelRequestViewEvent:
			CancelRequestViewEvent();
			break;
		case ECntGetContactIds:
			GetContactIdsL(aMessage);
			break;
		case ECntSendPluginUidToServer:
			SendPluginUidToServer(aMessage);
			break;
		case ECntGetContactsMatchingFilter:
			GetContactsMatchingFilterL(aMessage);
			break;
		case ECntGetSortPluginUidFromServer:
			GetSortPluginUidFromServerL(aMessage);
			break;
		default:
			User::Leave(KErrNotFound);
			break;
		}
	return reply;
	}


CViewSubSessionBase::~CViewSubSessionBase()
	{
	delete iQueue;
	delete iSortableText;
	delete iContact;
	DeleteFindContacts();
	}


CViewSubSessionBase::CViewSubSessionBase(CViewManager& aViewManager) : iViewManager(aViewManager),iContact(0)
	{
	}


void CViewSubSessionBase::ConstructL()
	{
	iQueue = new(ELeave) CViewSubSessionQueue();
	}


void CViewSubSessionBase::CountL(const RMessage2& aMessage) const
	{
	TPckgBuf<TInt> pckg(iView->CountL());
	aMessage.WriteL(0,pckg);
	}


TInt CViewSubSessionBase::AtL(const RMessage2& aMessage) const
	{
	TInt reply = KErrNone;
	const TInt index=aMessage.Int0();
	__ASSERT_ALWAYS(index>=0,User::Leave(KErrUnderflow));
	if(!(index<iView->CountL()))
		{
		// Index is out of bounds.
		reply=KErrNotFound;
		return reply;
		}
	TPckgBuf<TContactItemId> pckg(iView->AtL(index));
	aMessage.WriteL(1,pckg);
	return reply;
	}


void CViewSubSessionBase::ContactAtL(const RMessage2& aMessage) const
	{
	const TInt externalizedSize=iContact->ExternalizedSize();
	HBufC8* buf=HBufC8::NewLC(externalizedSize);
	TPtr8 bufPtr(buf->Des());
	RDesWriteStream writeStream(bufPtr);
	CleanupClosePushL(writeStream);
	writeStream << *iContact;
	bufPtr.SetLength(externalizedSize);
	aMessage.WriteL(0,*buf);
	CleanupStack::PopAndDestroy(2); // writeStream, buf.
	}


/** 
Return the size of the externalized contact data.

@param aMessage.Int0() Index.
@param aMessage.Ptr1() Package buffer to return size.
*/
TInt CViewSubSessionBase::ContactAtLengthL(const RMessage2& aMessage) 
	{
	TInt reply=KErrNone;
	TInt index = aMessage.Int0();

	__ASSERT_ALWAYS(index>=0,User::Leave(KErrUnderflow));

	if(!(index<iView->CountL()))
		{
		// Index is out of bounds.
		reply=KErrNotFound;
		return reply;
		}

	const CViewContact& contact=iView->ContactAtL(index);

	delete iContact;
	iContact=NULL;
	iContact = CViewContact::NewL(contact);
	
	const TInt externalizedSize=iContact->ExternalizedSize();
	TPckgBuf<TInt> pckg(externalizedSize);
	aMessage.WriteL(1,pckg);

	return reply;
	}


void CViewSubSessionBase::GetIncludedTypesL(const RMessage2& aMessage)
	{
	TPckgBuf<TContactViewPreferences> pckg(iView->ContactViewPreferences());
	aMessage.WriteL(0,pckg);	
	}


void CViewSubSessionBase::DeleteFindContacts()
	{
	iContacts.ResetAndDestroy();
	}


#ifdef _DEBUG
void CViewSubSessionBase::HandleContactViewEvent(const CContactViewBase& aView,const TContactViewEvent& aEvent)
#else
void CViewSubSessionBase::HandleContactViewEvent(const CContactViewBase& /*aView*/,const TContactViewEvent& aEvent)
#endif
	{
	ASSERT(&aView==iView);
	iQueue->QueueEvent(aEvent);
	}


/** 
Match an array of search strings against the contacts in the view.

The descriptor from the client contains a flag at the start to indicate if a
prefix or substring search has been requested.

@param aMessage.Ptr0() Size of contact data to read (to client).
@param aMessage.Int1() Size of descriptor (from client).
@param aMessage.Ptr2() Descriptor (from client).
*/
void CViewSubSessionBase::ContactMatchingCriteriaExternalizedSizeL(const RMessage2& aMessage)
	{
	TPckgBuf<TInt> size;
	aMessage.ReadL(1,size);
	const TInt bufferSize = size();

	// Restore buffer.
	CBufFlat* buffer = CBufFlat::NewL(bufferSize);
	CleanupStack::PushL(buffer);
	buffer->ExpandL(0,bufferSize);
	TPtr8 des(buffer->Ptr(0));
	aMessage.ReadL(2,des);

	// Internalize the data from the stream.
	RBufReadStream readStream(*buffer);
	CleanupClosePushL(readStream);

	TBool prefixSearch = readStream.ReadUint32L(); 
	const TInt numFindWords = readStream.ReadUint32L();
	CPtrC16Array* findDesArray = new(ELeave) CPtrC16Array(numFindWords);
	CleanupStack::PushL(findDesArray);

	TInt findWordLength=0;
	for (TInt i=0; i<numFindWords; ++i)
		{
		findWordLength = readStream.ReadUint32L();
		HBufC* findword = HBufC::NewLC(readStream,findWordLength);
		findDesArray->AppendL(*findword);
		}

	DeleteFindContacts();

	if (prefixSearch)
		iView->ContactsMatchingPrefixL(*findDesArray,iContacts);
	else
		iView->ContactsMatchingCriteriaL(*findDesArray,iContacts);

	findDesArray->Reset();
	
	CleanupStack::PopAndDestroy(numFindWords);
	CleanupStack::PopAndDestroy(3, buffer);

	// Compute contacts externalized size.
	const TInt contactsCount = iContacts.Count();
	TInt contactsExternalizedSize=0;
	contactsExternalizedSize+=sizeof(TInt32);
	for (TInt jj=0;jj<contactsCount;++jj)
		{
		contactsExternalizedSize+=(iContacts)[jj]->ExternalizedSize();
		}

	TPckgBuf<TInt> pckg(contactsExternalizedSize);
	aMessage.WriteL(0,pckg);
	}


/** 
Write matching contacts back to client.

@param aMessage.Ptr0() Descriptor to write array of matching contacts.
*/
void CViewSubSessionBase::GetContactMatchingCriteriaL(const RMessage2& aMessage)
	{
	// Compute contacts externalized size.
	const TInt contactsCount = iContacts.Count();
	TInt contactsExternalizedSize=0;
	contactsExternalizedSize+=sizeof(TInt32);
	for (TInt jj=0;jj<contactsCount;++jj)
		{
		contactsExternalizedSize+=(iContacts)[jj]->ExternalizedSize();
		}

	HBufC8* buf=HBufC8::NewLC(contactsExternalizedSize);
	TPtr8 bufPtr(buf->Des());
	RDesWriteStream writeStream(bufPtr);
	CleanupClosePushL(writeStream);

	writeStream.WriteUint32L(contactsCount);
	for (TInt ii=0;ii<contactsCount;++ii)
		{
		CViewContact* thisContact = (iContacts)[ii];
		writeStream << *thisContact;
		}

	bufPtr.SetLength(contactsExternalizedSize);
	aMessage.WriteL(0,*buf);

	CleanupStack::PopAndDestroy(2, buf); //writeStream.Close(), buf

	DeleteFindContacts();
	}


void CViewSubSessionBase::FindL(const RMessage2& aMessage) const
	{
	TPckgBuf<TContactItemId> pckg(iView->FindL(aMessage.Int0()));
	aMessage.WriteL(1,pckg);
	}


TInt CViewSubSessionBase::GetAllFieldsLengthL(const RMessage2& aMessage)
	{
	TInt reply = KErrNone;
	TInt index = aMessage.Int0();

	__ASSERT_ALWAYS(index>=0,User::Leave(KErrUnderflow));

	if(!(index<iView->CountL()))
		{
		// Index is out of bounds.
		reply=KErrNotFound;
		return reply;
		}

	TBuf<256> bufPtr;// = buf->Des();
	aMessage.ReadL(1,bufPtr);

	// Create sortable text from all fields of view contact at specified index.
	delete iSortableText;
	iSortableText=NULL;
	HBufC* allfields=iView->AllFieldsLC(index,bufPtr);
	CleanupStack::Pop(); // allfields
	iSortableText=allfields;

	TPckgBuf<TInt> pckg(iSortableText->Length());
	aMessage.WriteL(2,pckg);

	return reply;
	}


void  CViewSubSessionBase::GetAllFieldsTextL(const RMessage2& aMessage)
	{
	TPtrC8 narrowPtr((TUint8*)iSortableText->Ptr(),iSortableText->Size());
	aMessage.WriteL(0,narrowPtr);
	}


void CViewSubSessionBase::RequestViewEvent(const RMessage2& aMessage)
	{
	iQueue->RequestEvent(aMessage);
	}


void CViewSubSessionBase::CancelRequestViewEvent()
	{
	iQueue->CancelEventRequest();
	}


/**
Provides conversion between view indexes and contact IDs.

@param aMessage.Int0() Buffer size (from client).
@param aMessage.Ptr1() Descriptor containing indices (from client).
@param aMessage.Ptr2() Descriptor containing contact IDs (to client).
*/
void CViewSubSessionBase::GetContactIdsL(const RMessage2& aMessage)
	{
	TPckgBuf<TInt> size;
	aMessage.ReadL(0,size);
	const TInt bufferSize = size();
	
	CBufFlat* buffer = CBufFlat::NewL(bufferSize);
	CleanupStack::PushL(buffer);
	buffer->ExpandL(0,bufferSize);
	TPtr8 des(buffer->Ptr(0));
	aMessage.ReadL(1,des);

	RBufReadStream readStream(*buffer);
	CleanupClosePushL(readStream);
	const TInt count = readStream.ReadUint32L(); 

	CArrayFixFlat<TInt>* indexes = new(ELeave) CArrayFixFlat<TInt>(8);	
	CleanupStack::PushL(indexes);

	for (TInt i=0; i<count; ++i)
		{
		TInt index = readStream.ReadUint32L();
		indexes->AppendL(index);
		}

	CContactIdArray* array = CContactIdArray::NewLC();
	iView->GetContactIdsL(*indexes, *array);

	HBufC8* buf=HBufC8::NewLC(bufferSize);
	TPtr8 bufPtr(buf->Des());
	RDesWriteStream writeStream(bufPtr);
	CleanupClosePushL(writeStream);
	writeStream << *array;
	bufPtr.SetLength(bufferSize);
	aMessage.WriteL(2,*buf);

	CleanupStack::PopAndDestroy(6, buffer); // &writeStream, buf, array, indexes, &readStream, buffer
	}


void CViewSubSessionBase::SendPluginUidToServer(const RMessage2& aMessage)
	{
	TUid uid;
	uid.iUid = aMessage.Int0();
	iView->SetViewFindConfigPlugin(uid);
	}


/** 
Filter server-side view based on filter supplied by client.  The IDs of matching
contact items are externalized to the client-side.

@param aMessage.Int0() Filter (from client).
@param aMessage.Ptr1() Descriptor containing matching contact IDs (to client).
*/
void CViewSubSessionBase::GetContactsMatchingFilterL(const RMessage2& aMessage)
    {
    const TInt filter(aMessage.Int0());
    
    RArray<TContactIdWithMapping> array;
    CleanupClosePushL(array);
    TContactIdWithMapping idMap;

    const TInt viewCount(iView->CountL());
    if (filter & CContactDatabase::ECustomFilter3)
        {
        // CContactDatabase::ECustomFilter3 was used to filter the contacts 
        // which can be used in speed dial fetch dialog. 
        MLplCollection& collection = iViewManager.FactoryL().GetCollectorL();
        collection.Reset();
        CContactIdArray* speedDialIDArray = collection.FindSpeedDialContactsL();
        CleanupStack::PushL(speedDialIDArray);
       // Filter view contacts.
        for (TInt i=0;i<viewCount;++i)
	        {
	        const CViewContact& contact = iView->ContactAtL(i);
	        if(KErrNotFound !=speedDialIDArray->Find(contact.Id())) // Check if the contacts support speed dial.
	            {
	            idMap.iId=contact.Id();
	            idMap.iMapping=i;
	            User::LeaveIfError(array.Append(idMap));
	            }
	        }
        CleanupStack::PopAndDestroy(speedDialIDArray);
        }
        else
        {
        // Filter view contacts.
        for (TInt i=0;i<viewCount;++i)
            {
            const CViewContact& contact = iView->ContactAtL(i);
            if(contact.ContactMatchesFilter(filter))
                {
                idMap.iId=contact.Id();
                idMap.iMapping=i;
                User::LeaveIfError(array.Append(idMap));
                }
            }
        }

    // Externalize array to client.
    const TInt count(array.Count());
    const TInt maxBufSize = (1+(array.Count()*2))*sizeof(TInt);
    HBufC8* buf=HBufC8::NewLC(maxBufSize);
    TPtr8 bufPtr(buf->Des());
    RDesWriteStream writeStream(bufPtr);
    CleanupClosePushL(writeStream);
    writeStream.WriteUint32L(count);
    for (TInt j=0; j<count; ++j)
        {
        writeStream.WriteInt32L(array[j].iId);
        writeStream.WriteInt32L(array[j].iMapping);
        }
    bufPtr.SetLength(maxBufSize);
    aMessage.WriteL(1,*buf);
    CleanupStack::PopAndDestroy(3,&array);
    }


void CViewSubSessionBase::GetSortPluginUidFromServerL(const RMessage2& aMessage)
	{
	TUid uid = iView->GetViewSortPluginImplUid();
	TPckgBuf<TInt> pckg(uid.iUid);
	aMessage.WriteL(0,pckg);
	}


CViewSubSession* CViewSubSession::NewL(CViewManager& aViewManager,const RMessage2& aMessage)
	{
	CViewSubSession* self=new(ELeave) CViewSubSession(aViewManager);
	CleanupClosePushL (*self);
	self->ConstructL(aMessage);
	CleanupStack::Pop(); // self.
	return self;
	}


/**
Attempt to consume opcode.  If the opcode is not consumed then the base class
ServiceL() is called.
*/
TInt CViewSubSession::ServiceL(const RMessage2& aMessage)
	{
	switch (aMessage.Function())
		{
		case ECntViewSortOrderExternalizedSize:
			ExternalizedSortOrderSizeL(aMessage);
			break;
		case ECntGetViewSortOrder:
			GetSortOrderL(aMessage);
			break;
		default:
			return CViewSubSessionBase::ServiceL(aMessage);
		}
	return 0;
	}


CViewSubSession::~CViewSubSession()
	{
	iViewManager.CloseView(View(),*this);
	}


CViewSubSession::CViewSubSession(CViewManager& aViewManager) : CViewSubSessionBase(aViewManager)
	{
	}


void CViewSubSession::ConstructL(const RMessage2& aMessage) 
	{
	CViewSubSessionBase::ConstructL();

	RContactViewSortOrder sortOrder;
	CleanupClosePushL(sortOrder);
	TContactViewPreferences contactsToInclude;

	TUid sortPluginImplUid;
	HBufC8* sortPluginName = UnpackageSortOrderAndPluginDetailsLC(aMessage,sortOrder,contactsToInclude,sortPluginImplUid);

	iView = &iViewManager.OpenViewL(sortOrder,*this,contactsToInclude,sortPluginImplUid,*sortPluginName);

	CleanupStack::PopAndDestroy(2, &sortOrder); // sortPluginName, sortOrder
	}


void CViewSubSession::UnpackageSortOrderL(const RMessage2& aMessage,RContactViewSortOrder& aSortOrder,TContactViewPreferences& aContactTypes) const
	{
	HBufC8* buf=HBufC8::NewLC(aMessage.Int0());

	TPtr8 bufPtr(buf->Des());
	aMessage.ReadL(1,bufPtr);
	RDesReadStream readStream(bufPtr);
	CleanupClosePushL(readStream);

	readStream >> (TInt32&)aContactTypes;
	readStream >> aSortOrder;

	CleanupStack::PopAndDestroy(2); //readstream, buf.
	}


HBufC8* CViewSubSession::UnpackageSortOrderAndPluginDetailsLC(const RMessage2& aMessage,RContactViewSortOrder& aSortOrder,TContactViewPreferences& aContactTypes,TUid& aSortPluginImplUid) const
	{
	HBufC8* buf=HBufC8::NewLC(aMessage.Int0());

	TPtr8 bufPtr(buf->Des());
	TInt32 nameLen;
	aMessage.ReadL(1,bufPtr);
	RDesReadStream readStream(bufPtr);
	CleanupClosePushL(readStream);

	readStream >> (TInt32&)aContactTypes;
	readStream >> aSortOrder;

	// Extract sort plugin UID.
	aSortPluginImplUid.iUid = readStream.ReadInt32L();

	// Extract sort plugin name.
	nameLen = readStream.ReadInt32L();
	HBufC8* pluginNameBuf = HBufC8::NewLC(nameLen);
	TPtr8	pluginNamePtr = pluginNameBuf->Des();
	readStream.ReadL(pluginNamePtr, nameLen);
	CleanupStack::Pop(pluginNameBuf);
	CleanupStack::PopAndDestroy(2); //readstream, buf.

	CleanupStack::PushL(pluginNameBuf);
	return pluginNameBuf;
	}


CContactLocalView& CViewSubSession::View() const
	{
	return STATIC_CAST(CContactLocalView&,*iView);
	}


void CViewSubSession::ExternalizedSortOrderSizeL(const RMessage2& aMessage) const
	{
	TPckgBuf<TInt> pckg(View().SortOrder().ExternalizedSize());
	aMessage.WriteL(0,pckg);
	}


void CViewSubSession::GetSortOrderL(const RMessage2& aMessage) const
	{
	const RContactViewSortOrder& sortOrder=View().SortOrder();
	const TInt externalizedSize=sortOrder.ExternalizedSize();
	HBufC8* buf=HBufC8::NewLC(externalizedSize);
	TPtr8 bufPtr(buf->Des());
	RDesWriteStream writeStream(bufPtr);
	CleanupClosePushL(writeStream);
	writeStream << sortOrder;
	bufPtr.SetLength(externalizedSize);
	aMessage.WriteL(0,*buf);
	CleanupStack::PopAndDestroy(2); // writeStream, buf.
	}


CNamedViewSubSession* CNamedViewSubSession::NewL(CViewManager& aViewManager,const RMessage2& aMessage)
	{
	CNamedViewSubSession* self=new(ELeave) CNamedViewSubSession(aViewManager);
	CleanupClosePushL(*self); // CObject: Close will call the destructor.
	self->ConstructL(aMessage);
	CleanupStack::Pop(); // self.
	return self;
	}


/**
Attempt to consume opcode.  If the opcode is not consumed then the base class
ServiceL() is called.
*/
TInt CNamedViewSubSession::ServiceL(const RMessage2& aMessage)
	{
	switch (aMessage.Function())
		{
		case ECntChangeViewSortOrder:
			ChangeSortOrderL(aMessage);
			break;
		default:
			return CViewSubSession::ServiceL(aMessage);
		}
	return 0;
	}


CNamedViewSubSession::~CNamedViewSubSession()
	{
	iViewManager.CloseNamedView(View(),*this);
	}


CNamedViewSubSession::CNamedViewSubSession(CViewManager& aViewManager) : CViewSubSession(aViewManager)
	{
	}


void CNamedViewSubSession::ConstructL(const RMessage2& aMessage)
	{
	CViewSubSessionBase::ConstructL();

	// Read sort order.
	RContactViewSortOrder sortOrder;
	CleanupClosePushL(sortOrder);
	TContactViewPreferences contactsToInclude;
	TUid sortPluginImplUid;
	HBufC8* sortPluginName = UnpackageSortOrderAndPluginDetailsLC(aMessage,sortOrder,contactsToInclude,sortPluginImplUid);

	// Create a descriptor of the correct length.
	HBufC* nameBuf=HBufC::NewLC(aMessage.GetDesLengthL(2));
	TPtr  wideNameBufPtr(nameBuf->Des());
	// Extract the name of the view from the message.
	aMessage.ReadL(2, wideNameBufPtr);

	// Open view using name provided.
	iView = &iViewManager.OpenNamedViewL(wideNameBufPtr,sortOrder,*this,contactsToInclude,sortPluginImplUid,*sortPluginName);

	CleanupStack::PopAndDestroy(3,&sortOrder); // nameBuf, sortPluginName, sortOrder

	sortOrder.Close();
	}


void CNamedViewSubSession::ChangeSortOrderL(const RMessage2& aMessage)
	{
	RContactViewSortOrder newSortOrder;
	TContactViewPreferences contactsToInclude;
	UnpackageSortOrderL(aMessage,newSortOrder,contactsToInclude);
	View().ChangeSortOrderL(newSortOrder);
	newSortOrder.Close();
	}


CContactNamedLocalView& CNamedViewSubSession::View() const
	{
	return STATIC_CAST(CContactNamedLocalView&,*iView);
	}


CViewManager* CViewManager::NewL(MLplPersistenceLayerFactory& aFactory,CCntDbManager& aManager)
	{
	CViewManager* self =new (ELeave) CViewManager(aFactory,aManager);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}


CContactLocalView& CViewManager::OpenViewL(const RContactViewSortOrder& aSortOrder,MContactViewObserver& aObserver,TContactViewPreferences aContactTypeToInclude, const TUid aSortPluginImplUid, const TDesC8& aSortPluginName)
	{
	// Check to see if there is already a view with the required sort order,
	// preferences and sort plugin.
	const TInt numViews=iLocalViews.Count();
	for (TInt ii=0;ii<numViews;++ii)
		{
		CContactLocalView& thisView=*iLocalViews[ii].iLocalView; 
		if ( thisView.SortOrder()==aSortOrder && thisView.ContactViewPreferences() == aContactTypeToInclude &&
				iLocalViews[ii].iSortPluginImplUid == aSortPluginImplUid)
			{
			if(thisView.Error() == KErrNone)
				{
				// Found one with no sorting error, so share it.
				thisView.OpenL(aObserver);
				return thisView;
				}
			}
		}

	// No view found, so create a new one.
	TViewHandle	viewHandle;
	viewHandle.iSortPluginImplUid = aSortPluginImplUid;
	
	if (!iManager.StateMachineL().DatabaseReady())
		{
		User::Leave(KErrNotReady);
		}

	// Dummy CContactDatabase done for BC.  CContactLocalView does not use
	// CContactDatabase.
	CContactDatabase* dummy = NULL;

	// If aSortPluginImplUid is KNullUid indicates no sort plugin.
	if (aSortPluginName.Length() || (aSortPluginImplUid == KNullUid))
		{
		viewHandle.iLocalView=CContactLocalView::NewL(aObserver,*dummy,aSortOrder,aContactTypeToInclude,&iFactory,aSortPluginName);
		}
	else
		{
		viewHandle.iLocalView=CContactLocalView::NewL(aObserver,*dummy,aSortOrder,aContactTypeToInclude,&iFactory,KNullDesC8);
		}

	// Register this view as an observer of database events with the
	// associated CCntDbManager.
	iManager.RegisterDatabaseEventObserverL(*viewHandle.iLocalView);

	TInt error = iLocalViews.Append(viewHandle);
	if (error != KErrNone)
		{
		iManager.UnRegisterDatabaseEventObserver(*viewHandle.iLocalView);
		viewHandle.iLocalView->Close(aObserver);
		User::Leave(error);
		}

	return *viewHandle.iLocalView;
	}

	
CContactNamedLocalView& CViewManager::OpenNamedViewL(const TDesC& aName,const RContactViewSortOrder& aSortOrder,MContactViewObserver& aObserver,TContactViewPreferences aContactTypeToInclude, const TUid aSortPluginImplUid, const TDesC8& aSortPluginName)
	{
	// Check to see if named view already exists.
	const TInt numViews=iNamedLocalViews.Count();
	for (TInt ii=0;ii<numViews;++ii)
		{
		CContactNamedLocalView& thisView=*iNamedLocalViews[ii];
		if (aName.Compare(thisView.Name())==0 )
			{
			if(thisView.Error() == KErrNone)
				{
				// Found a name match, so share it.
				thisView.OpenL(aObserver);
				return thisView;
				}
			}
		}

	// No name match found, so create a new one.
	CContactNamedLocalView* newNamedView = NULL;

	// Dummy CContactDatabase done for BC.  CContactLocalView does not use
	// CContactDatabase.
	CContactDatabase* dummy = NULL;

	// If aSortPluginImplUid is KNullUid indicates no sort plugin.
	if (aSortPluginName.Length() || (aSortPluginImplUid == KNullUid))
		{
		newNamedView=CContactNamedLocalView::NewL(aObserver,aName,*dummy,aSortOrder,aContactTypeToInclude,&iFactory,aSortPluginName);
		}
	else
		{
		newNamedView=CContactNamedLocalView::NewL(aObserver,aName,*dummy,aSortOrder,aContactTypeToInclude,&iFactory,KNullDesC8);
		}

	// Register this view as an observer of database events with the
	// associated CCntDbManager.
	iManager.RegisterDatabaseEventObserverL(*newNamedView);

	TInt error = iNamedLocalViews.Append(newNamedView);
	if (error != KErrNone)
		{
		iManager.UnRegisterDatabaseEventObserver(*newNamedView);
		newNamedView->Close(aObserver);
		User::Leave(error);
		}

	return *newNamedView;
	}


void CViewManager::CloseView(const CContactLocalView& aView,MContactViewObserver& aObserver)
	{
	const TInt count = iLocalViews.Count();

	// Be tolerant to view not being found, since a leave may have occured
	// before the CViewSubSession derived object has had a chance to call
	// OpenViewL().
	for (TInt index = 0; index < count; index++)
		{
		// Found it?
		if (iLocalViews[index].iLocalView == &aView)
			{
			if(iLocalViews[index].iLocalView->Close(aObserver))
				{
				// Removed last reference to the Local View so un-Register this
				// view as an observer of database events with the associated 
				// CCntDbManager.
				iManager.UnRegisterDatabaseEventObserver(*iLocalViews[index].iLocalView);
				iLocalViews.Remove(index);
				}
			break;
			}
		}
	}


void CViewManager::CloseNamedView(const CContactNamedLocalView& aView,MContactViewObserver& aObserver)
	{
	TInt index = iNamedLocalViews.Find(&aView);

	// Be tolerant to view not being found, since a leave may have occured
	// before the CNamedViewSubSession derived object has had a chance to call
	// OpenViewL().
	if (index != KErrNotFound)
		{
		if (iNamedLocalViews[index]->Close(aObserver))
			{
			// Removed last reference to the Local View so un-Register this view
			// as an observer of database events with the associated
			// CCntDbManager.
			iManager.UnRegisterDatabaseEventObserver(*iNamedLocalViews[index]);
			iNamedLocalViews.Remove(index);
			}
		}
	}


CViewManager::CViewManager(MLplPersistenceLayerFactory& aFactory,CCntDbManager& aManager) :
	iFactory(aFactory),
	iManager(aManager)
	{
	}


CViewManager::~CViewManager()
	{
	iLocalViews.Close();
	iNamedLocalViews.Close();
	}


void CViewManager::ConstructL()
	{
	}

MLplPersistenceLayerFactory& CViewManager::FactoryL()
    {
    return iFactory;
    }

#if defined(_DEBUG)
void CViewManager::GetDefinitionsOfExistingViewsL(RPointerArray<CContactDefaultViewDefinition>& aViewDefs)
	{
	__ASSERT_DEBUG(aViewDefs.Count() == 0, User::Leave(KErrArgument));

	TInt i;
	CContactDefaultViewDefinition* viewDef;
	
	// Anonymous views.
	for (i = 0; i < iLocalViews.Count(); i++)
		{
		CContactLocalView* view = iLocalViews[i].iLocalView;
		viewDef = CContactDefaultViewDefinition::NewLC(CContactDefaultViewDefinition::ERemoteView,
														KNullDesC, view->SortOrderL(), 
														view->ContactViewPreferences(), 
														KNullDesC8/*pluginName*/);
		aViewDefs.AppendL(viewDef);
		CleanupStack::Pop(viewDef);
		}
	
	// Named views.
	for (i = 0; i < iNamedLocalViews.Count(); i++)
		{
		CContactNamedLocalView* view = iNamedLocalViews[i];
		viewDef = CContactDefaultViewDefinition::NewLC(CContactDefaultViewDefinition::ENamedRemoteView,
														view->Name(), view->SortOrderL(), 
														view->ContactViewPreferences(), 
														KNullDesC8/*pluginName*/);
		aViewDefs.AppendL(viewDef);
		CleanupStack::Pop(viewDef);		
		}
	}
#else
void CViewManager::GetDefinitionsOfExistingViewsL(RPointerArray<CContactDefaultViewDefinition>& )
	{
	}
#endif // _DEBUG
