// APGCTL.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include "apgctl.h"
#include "apgicnfl.h" 
#include "apgstd.h" 
#include "apfdef.h"
#include "..\apparc\trace.h"

///////////////////////////////////////////
// CApaSystemControl
///////////////////////////////////////////

CApaSystemControl* CApaSystemControl::NewL(const TDesC& aFullPath,const TUidType aUidType,const TDesC& aDefaultAppInfoFileName)
	{
	CApaSystemControl* self=new(ELeave) CApaSystemControl();
	CleanupStack::PushL(self);
	self->ConstructL(aFullPath,aUidType,aDefaultAppInfoFileName);
	CleanupStack::Pop();
	return self;
	}


CApaSystemControl::CApaSystemControl()
	:iExists(ETrue)
	{}


CApaSystemControl::~CApaSystemControl()
	{
	delete iCaption;
	delete iIcon;
	iDll.Close();
	}


void CApaSystemControl::ConstructL(const TDesC& aFullPath,const TUidType aUidType,const TDesC& aDefaultAppInfoFileName)
	{
	//
	// check the dll's type
	if (aUidType[1]!=KUidSystemControlDll)
		User::Leave(KErrNotSupported);

	iFullPath=aFullPath;
	iUidType=aUidType;
	//
	// get caption & icon from aif
	TBool useDefaultIcon=ETrue;
	CApaAppInfoFileReader* aif=NULL;
	TParse aifName;
	aifName.Set(KAppInfoFileExtension,&aFullPath,NULL);
	TRAPD(ret, aif=CApaAppInfoFileReader::NewL(aifName.FullName(),aUidType[2])); 
	if (ret==KErrNone)
		{
		useDefaultIcon = EFalse;
		CleanupStack::PushL(aif);
		iCaption = aif->CaptionL(User::Language()).AllocL();
		TRAP(ret, iIcon=aif->CreateMaskedBitmapL(48));
		CleanupStack::PopAndDestroy(); // aif
		if (ret!=KErrNone)
			useDefaultIcon = ETrue;
		}
	if (!iCaption)
		{
		TParsePtrC ptr(aFullPath);
		iCaption = ptr.Name().AllocL();
		}
	if (useDefaultIcon)
		{
		CApaAppInfoFileReader* defaultAppInfoFile = CApaAppInfoFileReader::NewLC(aDefaultAppInfoFileName);
		iIcon = defaultAppInfoFile->CreateMaskedBitmapL(48);
		CleanupStack::PopAndDestroy(); // defaultAppInfoFile
		}
	}


typedef TInt (*ControlConstructor)(const TDesC&);


EXPORT_C void CApaSystemControl::CreateL()
	{
	// load the dll
	User::LeaveIfError(iDll.Load(iFullPath));	
	// get a pointer to the first ordinal function and call it, passing in the library's path (for loading rsc's etc)
	ControlConstructor ctor=(ControlConstructor)iDll.Lookup(1);
	if (!ctor)
		User::Leave(KErrBadLibraryEntryPoint);
	else
		(*ctor)(iDll.FileName());
	
	}


EXPORT_C TUid CApaSystemControl::Type()const
	{
	return iUidType[2];
	}


EXPORT_C TFileName CApaSystemControl::FileName()const
	{
	return iFullPath;
	}


EXPORT_C CApaMaskedBitmap* CApaSystemControl::Icon()const
	{
	return iIcon;
	}


EXPORT_C TPtrC CApaSystemControl::Caption()const
	{
	__ASSERT_DEBUG(iCaption,Panic(EDPanicNoCaptionInControl));
	return iCaption->Des();
	}


///////////////////////////////////////////
// CApaSystemControlList
///////////////////////////////////////////

EXPORT_C CApaSystemControlList* CApaSystemControlList::NewL(RFs& aFs,CApaAppFinder& aCtlFinder,const TDesC& aDefaultAppInfoFileName)
	{
	CApaSystemControlList* self=new(ELeave) CApaSystemControlList(aFs,aCtlFinder);
	CleanupStack::PushL(self);
	self->ConstructL(aDefaultAppInfoFileName);
	self->UpdateL();
	CleanupStack::Pop();
	return self;
	}


CApaSystemControlList::CApaSystemControlList(RFs& aFs,CApaAppFinder& aCtlFinder)
	:iFs(aFs),
	iCtlFinder(&aCtlFinder)
	{}


EXPORT_C CApaSystemControlList::~CApaSystemControlList()
	{
	CApaSystemControl* control=iControl;
	CApaSystemControl* next;
	while (control)
		{
		next = control->iNext;
		delete control;
		control = next;
		}
	delete iAif;
	}


void CApaSystemControlList::ConstructL(const TDesC& aDefaultAppInfoFileName)
	{
	iAif = aDefaultAppInfoFileName.AllocL();
	}


EXPORT_C TInt CApaSystemControlList::Count()const
// returns the number of controls in the linked list
//
	{
	TInt count=0;
	CApaSystemControl* control=iControl;
	while (control)
		{
		count++;
		control = control->iNext;
		}
	return count;
	}


EXPORT_C TInt CApaSystemControlList::Index(TUid aType)const
// returns the index of the control if there's a match, KErrNotFound otherwise
//
	{
	TInt count=0;
	CApaSystemControl* control=iControl;
	while (control && control->Type()!=aType)
		{
		count++;
		control = control->iNext;
		}
	if (!control)
		count = KErrNotFound;
	return count;
	}


EXPORT_C CApaSystemControl* CApaSystemControlList::Control(TInt aIndex)const
// returns the control at index aIndex in the list
// panics if the index is out of range
//
	{
	TInt count=0;
	CApaSystemControl* control=iControl;
	while (control && count!=aIndex)
		{
		count++;
		control = control->iNext;
		}
	//
	__ASSERT_ALWAYS(control,Panic(EPanicIndexOutOfRange));
	return control;
	}


EXPORT_C CApaSystemControl* CApaSystemControlList::Control(TUid aType)const
// returns the control who's type matches aType
// returns null if there is no match
//
	{
	CApaSystemControl* control=iControl;
	while (control && control->Type()!=aType)
		control = control->iNext;
	return control;
	}


CApaSystemControl* CApaSystemControlList::PreviousControl(CApaSystemControl* aControl)const
	{
	CApaSystemControl* control=iControl;
	CApaSystemControl* previous=NULL;
	while (control && control!=aControl)
		{
		previous = control;
		control = control->iNext;
		}
	if (!control)
		previous = NULL;
	return previous;
	}


EXPORT_C void CApaSystemControlList::UpdateL()
// updates the cached list of controls by scanning the file system
// adds any new ones, and removes any that no longer exist
// increments iUpdateCount if list has changed
// if an error occurs, the list will be incomplete but functional
//
	{
	__SHOW_TRACE(_L("Starting CApaSystemControlList::UpdateL()"));
	// set all the current controls to "dont exist", so we can find them again
	CApaSystemControl* control=iControl;
	while (control)
		{
		control->iExists = EFalse;
		control = control->iNext;
		}
	// 
	// scan for new controls
	TBool listChanged=EFalse;
	iCtlFinder->FindAllAppsL();
	//
	TApaAppEntry entry;
	while (iCtlFinder->NextL(entry))
		{
		control = Control(entry.iUidType[2]);
		if (control==NULL)
			{// not in list, so add it at the start
			__SHOW_TRACE(_L("...New control located"));
			listChanged=ETrue;
			TRAPD(err,control=CApaSystemControl::NewL(entry.iFullName,entry.iUidType,*iAif));
			if (err==KErrNone)
				{
				__SHOW_TRACE(_L("...control added"));
				control->iNext = iControl;
				iControl = control;
				}
			}
		else if (!control->iExists)
			{// not already found - we made need to override this one
			if (entry.iFullName.CompareF(control->FileName())!=0)
				{
				__SHOW_TRACE(_L("...new instance of control - delete old one"));
				// delete the old one before creating the new one so that the correct library is loaded
				CApaSystemControl* prev=PreviousControl(control);
				if (prev)
					prev->iNext = control->iNext;
				else
					iControl = control->iNext;
				delete control;
				control = NULL;
				listChanged=ETrue;
				// create the new one. Add it to the list if this is successful
				__SHOW_TRACE(_L("...create new one"));
				TRAPD(err,control=CApaSystemControl::NewL(entry.iFullName,entry.iUidType,*iAif));
				__SHOW_TRACE(_L("...new one created"));
				if (err==KErrNone)
					{
					control->iNext = iControl;
					iControl = control;
					}
				}
			else
				control->iExists = ETrue;
			}
		}
	//
	// remove any controls that no longer exist
	CApaSystemControl* previousControl=NULL;
	control = iControl;
	while (control)
		{
		if (!control->iExists)
			{
			listChanged=ETrue;
			if (!previousControl)
				{// this must be the first control in the list, ie iControl
				iControl = control->iNext;
				delete control;
				control = iControl;
				}
			else
				{
				previousControl->iNext = control->iNext;
				delete control;
				previousControl = previousControl->iNext;
				control = previousControl->iNext;
				}
			}
		else
			control = control->iNext;
		}
	//
	// increment the counter if the list has changed
	if (listChanged)
		iUpdateCount++;
	}
