// Copyright (c) 1997-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 <e32svr.h>
#include <f32file.h>
#include "ET_SSTD.H"

const TUint KColonChar=':';
_LIT(KDoubleColon, "::");
const TInt KDoubleColonLength = 2;

//
// CPhoneManager class
//

CPhoneManager* CPhoneManager::NewL()
//
// Create new instance of phone manager
//
	{
	CPhoneManager* phoneManager=new(ELeave) CPhoneManager();
	CleanupStack::PushL(phoneManager);
	phoneManager->ConstructL();
	CleanupStack::Pop();
	return phoneManager;
	}

CPhoneManager::CPhoneManager()
	{}

void CPhoneManager::ConstructL()
	{
	iContainerIx=CObjectConIx::NewL();
	iObjectCon=iContainerIx->CreateL();
	iTsyModulesCon =iContainerIx->CreateL();
	iDuplicatePhones = new (ELeave) CArrayPtrFlat<CDuplicatePhoneInfo>(3);
	}

CPhoneManager::~CPhoneManager()
//
//	Destroy all objects in global storage
//
	{
	if (iContainerIx)
		{
		iContainerIx->Remove(iObjectCon);
		iContainerIx->Remove(iTsyModulesCon);
		delete iContainerIx;
		}
	if(iDuplicatePhones)
		{
		iDuplicatePhones->ResetAndDestroy();
		delete iDuplicatePhones;
		}
	}

void CloseObject(TAny* aObj)
//
// Utility func for cleanup stack
//
	{
	CObject* obj=REINTERPRET_CAST(CObject*,aObj);
	obj->Close();
	}

CPhoneBase* CPhoneManager::OpenPhoneFromFactoryL(CPhoneFactoryBase* aParent,const TDesC& aName)
//
// Search for the name of this TelObject if found open it if not create new instances of the object
//
	{
	CPhoneBase* newObject=NULL;
	TInt handle=0;
	TFullName dummy;
	TInt res=iObjectCon->FindByFullName(handle,aName,dummy);
	switch (res)
		{
	case KErrNotFound:
			{
			TInt len=aName.LocateReverse(KColonChar);
			if (len==KErrNotFound)
				User::Leave(KErrBadName);
			TName name=aName;
			name=aName.Right(aName.Length()-len-1);
			TName originalName(name);
			ConvertPhoneNameToOriginal(originalName);	// this converts phone name to original if it has been previously converted

			newObject=aParent->NewPhoneL(originalName);

			TCleanupItem newObjectClose(CloseObject,newObject);
			CleanupStack::PushL(newObjectClose);
			newObject->SetNameL(&name); // MUST not be a full name. It must be the name recognised by clients
					// not necessarily what the TSY calls it, if another TSY has already been loaded
					// with an identical phone name
			newObject->SetOwner(aParent);
			newObject->SetPhoneOwner(newObject);
			iObjectCon->AddL(newObject);
			newObject->Init(); // Call Tsy Init
			CleanupStack::Pop();
			}
		break;
	case KErrNone:
		newObject=REINTERPRET_CAST(CPhoneBase*,iObjectCon->At(handle));
		newObject->Open();
		break;
	default:
		User::Leave(res);
		}
	return newObject;
	}

CTelObject* CPhoneManager::OpenSubSessionObjectL(CTelObject* aParent, TDes& aNewName)
//
//	Create a new instance of this TelObject, expecting to be given a name back
//
	{
	CTelObject* newObject=NULL;
	newObject=aParent->OpenNewObjectL(aNewName);
	
	__ASSERT_ALWAYS(newObject!=NULL,Fault(EEtelFaultBadPhonePointer));
	
	TCleanupItem newObjectClose(CloseObject,newObject);
	CleanupStack::PushL(newObjectClose);
	newObject->SetOwner(aParent);
	newObject->SetNameL(&aNewName); // MUST not be a full name	
	newObject->SetPhoneOwner(aParent->PhoneOwner());
	if(iObjectCon->CheckUniqueFullName(newObject)!=KErrAlreadyExists)		// If the TSY is allocating objects from a pool, this one may already be registered.
		iObjectCon->AddL(newObject);
	newObject->Init();
	CleanupStack::Pop();
	return newObject;
	}
	
CTelObject* CPhoneManager::OpenSubSessionObjectByNameL(CTelObject* aParent, const TDesC& aName/*,TTelObjectOpenSource aSource*/)
//
// Search for the name of this TelObject if found open it if not create new instances of the object
//
	{
	CTelObject* newObject=NULL;
	TInt handle=0;
	TFullName dummy;
	TInt res=iObjectCon->FindByFullName(handle,aName,dummy);
	switch (res)
		{
	case KErrNotFound:
			{
			// All parsing of the name string done so far has been from left to right
			// Cannot simply start parsing from right to left to extract the last name element
			// There is an, albiet unlikely, error case in which the string ":::" appears, which would cause a problem
			TInt len = 0;
			TName name = aName;
			// Loop through the name string, removing each element deliminated by two colons, until only the tail of the string is left
			do
				{
				len = name.Find(KDoubleColon);

				if ( (len != KErrNotFound) && (name.Length() > KDoubleColonLength) )
					{
					name.Copy(name.Mid(len + KDoubleColonLength));
					}
				else
					{
					// Length wasn't greater than KDoubleColonLength, so the double colon was at the end of the string
					// This shouldn't ever happen.
					break;
					}
				
				} while (len != KErrNotFound);

			if (name.Length())
				newObject=aParent->OpenNewObjectByNameL(name);
			else
				newObject=aParent->OpenNewObjectL(name);	// TSY will supply a name
				
			__ASSERT_ALWAYS(newObject!=NULL,Fault(EEtelFaultBadPhonePointer));				
			
			TCleanupItem newObjectClose(CloseObject,newObject);
			CleanupStack::PushL(newObjectClose);
			newObject->SetNameL(&name); // MUST not be a full name
			newObject->SetOwner(aParent);
			newObject->SetPhoneOwner(aParent->PhoneOwner());
			iObjectCon->AddL(newObject);
			newObject->Init();
			CleanupStack::Pop();
			break;
			}
	case KErrNone:
		newObject=REINTERPRET_CAST(CTelObject*,iObjectCon->At(handle));
		newObject->Open();
		break;
	default:
		User::Leave(res);
		}
	return newObject;
	}
	
void CloseLibrary(TAny* aLib)
//
// Close a library from the cleanup stack
//
	{
	RLibrary* lib=REINTERPRET_CAST(RLibrary*,aLib);
	lib->Close();
	}

CPhoneFactoryBase* CPhoneManager::OpenPhoneFactoryFromTsyL(const TDesC& aName)
//
// Find a previously opened phone by name
//
	{
	TInt handle=0;
	TName matching;
	User::LeaveIfError(iTsyModulesCon->FindByName(handle,aName,matching));
	return REINTERPRET_CAST(CPhoneFactoryBase*,iTsyModulesCon->At(handle));
	}

//
// Typedef required for returning PhoneFactoryBase instance
//
typedef CPhoneFactoryBase*(*TPhoneFactoryBaseNewL)();

CPhoneFactoryBase* CPhoneManager::LoadPhoneModuleL(const TDesC& aFileName)
//
// Load an Etel module (TSY) on user request
//
	{
	// Strip .TSY Off
	TName name(aFileName);
	TInt len=name.Locate('.');
	name.SetLength(len);

	TName foundName;
	TInt findHandle=0;
	TInt found=iTsyModulesCon->FindByName(findHandle,name,foundName);	// Is already loaded?
	if(found==KErrNone)
		{	// The TSY is already loaded, we'll just open, and bump up the reference count.
		LOGTEXT("LoadPhoneModuleL\tTSY already loaded - inc ref count");
		CPhoneFactoryBase* s=REINTERPRET_CAST(CPhoneFactoryBase*,iTsyModulesCon->At(findHandle));
		s->Open();
		return s;
		}

	RLibrary lib;
#ifdef _DEBUG
	TBuf8<128> buf;
	buf.Copy(aFileName);
#endif // _DEBUG
	LOGTEXT2("Loading %S", &buf);
	TInt r=lib.Load(aFileName);
	if (r!=KErrNone)
		User::Leave(r);
	LOGTEXT2("Loaded %S", &buf);
	TCleanupItem libClose(CloseLibrary,&lib);
	CleanupStack::PushL(libClose);

	// Check the Uid2
	if(lib.Type()[1]!=TUid::Uid(KUidUnicodeEtelServerModule))
		User::Leave(KErrBadLibraryEntryPoint);

	TPhoneFactoryBaseNewL libEntry=(TPhoneFactoryBaseNewL)lib.Lookup(1);
	if (libEntry==NULL)
		User::Leave(KErrBadLibraryEntryPoint);
	LOGTEXT("About to get CPhoneFactoryBase ptr");
	CPhoneFactoryBase* s=NULL;
	s=(*libEntry)();	// libEntry may leave.
	if(s==NULL)
		User::Leave(KErrNoMemory);
	LOGTEXT("Got CPhoneFactoryBase ptr");
	TRAPD(error, s->ConstructL(lib));
	if(error)
		{
		s->Close();
		User::Leave(error);
		}
	CleanupStack::Pop();

	if (DuplicatePhoneName(s))
		{
		s->Close();
		User::Leave(KErrEtelDuplicatePhoneName);
		}

	TInt errorName = s->SetName(&name);
	if(errorName!=KErrNone)
		{
		s->Close();
		User::Leave(errorName);
		}

	TRAPD(ret,iTsyModulesCon->AddL(s));
	if(ret!=KErrNone)
		{
		s->Close();
		User::Leave(ret);
		}

	if (iTsyModulesCon->Count()>1)	// only check for phone name clashes if have >1 TSYs loaded
		{
		TInt ret = RenameDuplicatePhoneName();
		if (ret!=KErrNone)
			{
			s->Close();
			User::Leave(ret);
			}
		}
	return s;
	}

TInt CPhoneManager::EnumeratePhones() const
//
// Return the number of phones in all loaded tsy
//
	{
	TInt tsyCount=0;
	TUint numPhones=0;
	while (tsyCount<iTsyModulesCon->Count())
		{
		CPhoneFactoryBase* phoneFac=REINTERPRET_CAST(CPhoneFactoryBase*,iTsyModulesCon->operator[](tsyCount));
		numPhones+=phoneFac->EnumeratePhones();
		tsyCount++;
		}
	return numPhones;
	}

TInt CPhoneManager::GetPhoneInfo(TUint aIndex,RTelServer::TPhoneInfo& aInfo) const
//
// Get info by number on a specified phone.
//
	{
	TInt tsyCount=0;
	TUint numPhones=0;
	TUint lastCount=0;
	while (tsyCount<iTsyModulesCon->Count())
		{
		CPhoneFactoryBase* phoneFac=REINTERPRET_CAST(CPhoneFactoryBase*,iTsyModulesCon->operator[](tsyCount));
		numPhones+=phoneFac->EnumeratePhones();
		if (aIndex<numPhones) // the phone index refers to is in this tsy
			{
			return phoneFac->GetPhoneInfo(aIndex-lastCount,aInfo);
			}
		tsyCount++;
		lastCount=numPhones;
		}
	return KErrNotFound;
	}

TInt CPhoneManager::GetTsyName(const TInt aIndexOfPhone,TDes& aTsyName) const
	{
	CPhoneFactoryBase* phoneFac = PhoneFactoryObjectFromPhoneIndex(aIndexOfPhone);
	if (phoneFac!=NULL)
		{
		aTsyName = phoneFac->Name();
		return KErrNone;
		}
	else
		return KErrNotFound;
	}

CPhoneFactoryBase* CPhoneManager::PhoneFactoryObjectFromPhoneIndex(TUint aIndex) const
//
// Return a factory handle for a given phone index.
//
	{
	TInt tsyCount=0;
	TUint numPhones=0;
	while (tsyCount<iTsyModulesCon->Count())
		{
		CPhoneFactoryBase* phoneFac=REINTERPRET_CAST(CPhoneFactoryBase*,iTsyModulesCon->operator[](tsyCount));
		numPhones+=phoneFac->EnumeratePhones();
		if (aIndex<numPhones) // the phone index refers to is in this tsy
			return phoneFac;
		tsyCount++;
		}
	return NULL;
	}

CPhoneFactoryBase* CPhoneManager::PhoneFactoryObjectFromPhoneName(const TDesC& aPhoneName) const
//
// Return a handle for phone factory base with given name
// NULL if not found in any tsy module
//
	{
	RTelServer::TPhoneInfo info;
	TUint numPhones=EnumeratePhones();
	TUint count=0;
	CPhoneFactoryBase* fact=NULL;
	
	while (count<numPhones)
		{
		GetPhoneInfo(count,info);
		fact = PhoneFactoryObjectFromPhoneIndex(count);
		ConvertPhoneNameFromOriginal(fact->Name(),info.iName);	// convert original phone name to munged if possible, 
					// because aPhoneName will be in that form
		if (info.iName.Compare(aPhoneName)==KErrNone) // found a match phone name
			return fact;
		count++;
		}
	return NULL;
	}

TBool CPhoneManager::DuplicatePhoneName(CPhoneFactoryBase* aPhoneFac) const
//
// Scan throu' phone names in this TSY and check that no two phones has the same name
//
	{
	RTelServer::TPhoneInfo infoToCompare;
	RTelServer::TPhoneInfo infoToMatch;
	TUint numPhones=aPhoneFac->EnumeratePhones();
	TUint indexToCompare=0;
	if (numPhones<=1)
		return EFalse;
	do
		{
		aPhoneFac->GetPhoneInfo(indexToCompare,infoToCompare);
		for (TUint i=indexToCompare+1;i<numPhones;i++)
			{
			aPhoneFac->GetPhoneInfo(i,infoToMatch);
			if (infoToMatch.iName.Compare(infoToCompare.iName)==KErrNone) // found a match
				return ETrue;
			}
		indexToCompare++;
		}
	while (indexToCompare<numPhones);
	return EFalse;
	}

TInt CPhoneManager::RenameDuplicatePhoneName() const
//
// Scan through phone names and check for identical phone names (because a new TSY has just been
// loaded for instance). 
// Assumes that for all TSYs loaded, if they have more than one phone, that the phone 
// names within that TSY are all different. This assumption should be OK since this is checked at load time.
// Make a note of which phone has an identical name
//
	{
	RTelServer::TPhoneInfo infoToCompare;
	RTelServer::TPhoneInfo infoToMatch;
	TUint numPhones=EnumeratePhones();
	TUint indexToCompare=0;
	if (numPhones<=1)
		return EFalse;
	do
		{
		GetPhoneInfo(indexToCompare,infoToCompare);
		for (TUint i=indexToCompare+1;i<numPhones;i++)
			{
			GetPhoneInfo(i,infoToMatch);
			if (infoToMatch.iName.Compare(infoToCompare.iName)==KErrNone) // found a match
				{
				TName tsyName;
				TInt err = GetTsyName(i,tsyName);
				if(err != KErrNone)
					return err;
				_LIT(KDash,"-");
				TName newName(tsyName);
				newName.Append(KDash);
				newName.Append(infoToMatch.iName);	// Create unique name
				LOGTEXT2("new name = %S", &newName);
				LOGTEXT2("tsy name = %S", &tsyName);
				LOGTEXT2("old name = %S", &infoToMatch.iName);	
				TRAPD(ret,StoreDuplicateNameL(tsyName,infoToMatch.iName,newName));// even if this returns with 
									//KErrAlreadyExists, carry on searching for another match
				if (ret!=KErrNone && ret!=KErrAlreadyExists)
					return ret;
				}
			}
		indexToCompare++;
		}
	while (indexToCompare<numPhones);
	return KErrNone;
	}

void CPhoneManager::StoreDuplicateNameL(const TDesC& aTsyName, const TDesC& aOriginalName, const TDesC& aNewName) const
	{
	for (TInt i=0; i<iDuplicatePhones->Count(); i++)
		{
		if (iDuplicatePhones->At(i)->iNewName->CompareF(aNewName)==KErrNone)
			User::Leave(KErrAlreadyExists);	// Leave if the munged name has already been chosen.
		}
	CDuplicatePhoneInfo* phoneInfo = CDuplicatePhoneInfo::NewLC(&aTsyName,&aOriginalName,&aNewName);
	iDuplicatePhones->AppendL(phoneInfo);
	CleanupStack::Pop();
	}

TInt CPhoneManager::ConvertPhoneNameToOriginal(TDes& aName) const
	{
	CDuplicatePhoneInfo* eachInfo;
	for (TInt i=0; i<iDuplicatePhones->Count();i++)
		{
		eachInfo = iDuplicatePhones->At(i);
		if (eachInfo->iNewName->Des()==aName)
			{
			aName=eachInfo->iOriginalName->Des();
			return KErrNone;
			}
		}
	return KErrNotFound;
	}

TInt CPhoneManager::ConvertPhoneNameFromOriginal(const TDesC& aTsyName, TDes& aPhoneName) const
	{
	CDuplicatePhoneInfo* eachInfo;
	for (TInt i=0; i<iDuplicatePhones->Count();i++)
		{
		eachInfo = iDuplicatePhones->At(i);
		if (eachInfo->iOriginalName->Des()==aPhoneName && eachInfo->iTsyName->Des()==aTsyName)
			{
			aPhoneName=eachInfo->iNewName->Des();
			return KErrNone;
			}
		}
	return KErrNotFound;
	}
				
void CPhoneManager::RemoveDuplicatePhoneInfo(const TDesC& aTsyName)
//
//	If a session Closes a Phone Module, and it was the last session to have a handle open on the
//	Module, the Duplicate Phone Information needs to be removed.
//	So search currently loaded TSYs to see if the session's removed TSY has in fact been removed
//	altogether, and if so remove the duplicate phone info.
//
	{	
	TRAPD(ret,OpenPhoneFactoryFromTsyL(aTsyName));
	if (ret==KErrNone)	// a session still has a handle on it
		return;
	CDuplicatePhoneInfo* eachInfo;
	TInt max = iDuplicatePhones->Count()-1;
	for (TInt i=max; i>=0;--i)	// otherwise we need to remove it
		{
		eachInfo = iDuplicatePhones->At(i);
		if (eachInfo->iTsyName->Des()==aTsyName)
			{
			delete eachInfo;
			iDuplicatePhones->Delete(i);
			}
		}
	}
//
// CDuplicatePhoneInfo
//	

CDuplicatePhoneInfo* CDuplicatePhoneInfo::NewLC(const TDesC* aTsyName,const TDesC* aOriginalName, const TDesC* aNewName)
	{
	CDuplicatePhoneInfo* p = new (ELeave) CDuplicatePhoneInfo();
	CleanupStack::PushL(p);
	p->ConstructL(aTsyName,aOriginalName,aNewName);
	return p;
	}

CDuplicatePhoneInfo::CDuplicatePhoneInfo()
	{}

CDuplicatePhoneInfo::~CDuplicatePhoneInfo()
	{
	delete iTsyName;
	delete iNewName;
	delete iOriginalName;
	}

void CDuplicatePhoneInfo::ConstructL(const TDesC* aTsyName,const TDesC* aOriginalName,const TDesC* aNewName)
	{
	delete(iTsyName);
	iTsyName=NULL;

	if (aTsyName!=NULL)
		{
		iTsyName=aTsyName->AllocL();
		}

	delete(iNewName);
	iNewName=NULL;

	if (aNewName!=NULL)
		{
		iNewName=aNewName->AllocL();
		}

	delete(iOriginalName);
	iOriginalName=NULL;

	if (aOriginalName!=NULL)
		iOriginalName=aOriginalName->AllocL();
	}
