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

#include <E32SVR.H>
#include <F32FILE.H>
#include "ET_SSTD.H"

const TUint KColonChar=':';

//
// 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
//
	{
	iContainerIx->Remove(iObjectCon);
	iContainerIx->Remove(iTsyModulesCon);
	delete iContainerIx;
	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);
	TCleanupItem newObjectClose(CloseObject,newObject);
	CleanupStack::PushL(newObjectClose);
	newObject->SetNameL(&aNewName); // MUST not be a full name	
	newObject->SetOwner(aParent);
	newObject->SetPhoneOwner(aParent->PhoneOwner());
	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:
			{
			TInt len=aName.LocateReverse(':');
			if (len==KErrNotFound)
				User::Leave(KErrBadName);

			TName name=aName;
			name=aName.Right(aName.Length()-len-1);
			if (name.Length())
				newObject=aParent->OpenNewObjectByNameL(name);
			else
				newObject=aParent->OpenNewObjectL(name);	// TSY will supply a name
			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(_L8("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
	LOGTEXT2(_L8("Loading %S"),&buf);
	TInt r=lib.Load(aFileName);
	if (r!=KErrNone)
		User::Leave(r);
	LOGTEXT2(_L8("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(_L8("About to get CPhoneFactoryBase ptr"));
	CPhoneFactoryBase* s=NULL;
	s=(*libEntry)();	// libEntry may leave.
	LOGTEXT(_L8("Got CPhoneFactoryBase ptr"));
	s->ConstructL(lib);
	CleanupStack::Pop();

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

	s->SetName(&name);
	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;
	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;
		tsyCount++;
		lastCount=numPhones;
		}
	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;
				User::LeaveIfError(GetTsyName(i,tsyName));
				_LIT(KDash,"-");
				TName newName(tsyName);
				newName.Append(KDash);
				newName.Append(infoToMatch.iName);	// Create unique name
				LOGTEXT2(_L8("new name = %S"),&newName);
				LOGTEXT2(_L8("tsy name = %S"),&tsyName);
				LOGTEXT2(_L8("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()
	{
	User::Free(iTsyName);
	User::Free(iNewName);
	User::Free(iOriginalName);
	}

void CDuplicatePhoneInfo::ConstructL(const TDesC* aTsyName,const TDesC* aOriginalName,const TDesC* aNewName)
	{
	User::Free(iTsyName);
	iTsyName=NULL;
	if (aTsyName!=NULL)
		iTsyName=aTsyName->AllocL();
	User::Free(iNewName);
	iNewName=NULL;
	if (aNewName!=NULL)
		iNewName=aNewName->AllocL();
	User::Free(iOriginalName);
	iOriginalName=NULL;
	if (aOriginalName!=NULL)
		iOriginalName=aOriginalName->AllocL();
	}
