// APFREC.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <e32uid.h>
#include <f32file.h>
#include <apadef.h>
#include <apffndr.h>
#include <apfrec.h>
#include "apfstd.h" // Panics etc.

_LIT(KRecognizerDllExtension,".RDL");


///////////////////////////////
// CApaRecognizerDll
///////////////////////////////

class CApaRecognizerDll : public CBase
	{
public:
	~CApaRecognizerDll(); // closes lib and deletes next
public:
	enum TPresence { ENotPresent, EIsPresent, ESuperseded };
public:
	RLibrary iLibrary;
	CApaRecognizerDll* iNext;
	CApaScanningFileRecognizer::TRecognizer iId; // cached so that this data can be accessed from other threads (RLibrary cant be used in this case)
	CApaFileRecognizerType* iRec; // not ownership
	TPresence iPresent;
	};


CApaRecognizerDll::~CApaRecognizerDll()
	{
	iLibrary.Close();
	delete iNext;
	}


///////////////////////////////
// CApaScanningFileRecognizer
///////////////////////////////


EXPORT_C CApaScanningFileRecognizer* CApaScanningFileRecognizer::NewL(RFs& aFs,MApaAppStarter* aAppStarter)
	{
	CApaScanningFileRecognizer* self=new(ELeave) CApaScanningFileRecognizer(aFs,aAppStarter);
	CleanupStack::PushL(self);
	self->ConstructL();
	self->ScanForRecognizersL();
	CleanupStack::Pop(); // self
	return self;
	}


EXPORT_C CApaScanningFileRecognizer::CApaScanningFileRecognizer(RFs& aFs,MApaAppStarter* aAppStarter)
:CApaFileRecognizer(aFs),
iAppStarter(aAppStarter)
	{}


EXPORT_C void CApaScanningFileRecognizer::ConstructL()
	{
	SetAppLocator( CApaAppLocatorProxy::NewL(iFs) ); // takes ownership
	}


EXPORT_C CApaScanningFileRecognizer::~CApaScanningFileRecognizer()
	{
	DestroyRecognizerList();
	delete iRecognizerLib;
	}


EXPORT_C void CApaScanningFileRecognizer::ScanForRecognizersL()
	{
	// set all recognizers to not present - pending rediscoivery
	CApaRecognizerDll* rec=iRecognizerLib;
	while (rec)
		{
		rec->iPresent = CApaRecognizerDll::ENotPresent;
		rec = rec->iNext;
		}
	//
	// get a list of available drives
	TDriveList driveList;
	TDriveInfo driveInfo;
	User::LeaveIfError(iFs.DriveList(driveList));
	TInt ret;
	for (TInt n=0 ; n<KMaxDrives ; n++)
		{
		// check each drive in the list in turn
		if (driveList[n]!=0)
			{
			ret = iFs.Drive(driveInfo,n);
			if (ret==KErrNone && driveInfo.iType!=EMediaNotPresent && driveInfo.iType!=EMediaRemote)
				ScanDriveL(n);
			}
		}
	//
	// remove any recognizers that are no longer present
	rec=iRecognizerLib;
	while (rec)
		{
		CApaRecognizerDll* next=rec->iNext;
		if (rec->iPresent==CApaRecognizerDll::ENotPresent)
			RemoveRecognizer(rec);
		rec = next;
		}
	}


TInt CApaScanningFileRecognizer::RemoveRecognizer(CApaRecognizerDll* aDll)
	{
	// try to remove the recognizer from the list
	TInt ret=RemoveFileRecognizerType(aDll->iRec);
	if (ret!=KErrNone && ret!=KErrNotFound)
		return ret;
	//
	// get a handle to the previous entry in the list
	CApaRecognizerDll* prev=NULL;
	CApaRecognizerDll* dll=iRecognizerLib;
	while (dll && dll!=aDll)
		{
		prev = dll;
		dll = dll->iNext;
		}
	if (!dll)
		Panic(EPanicLibraryNotInList);
	//
	// remove the dll
	if (prev)
		prev->iNext = dll->iNext;
	else
		iRecognizerLib = dll->iNext;
	dll->iNext = NULL;
	delete dll;
	iUpdateCounter++;
	return KErrNone;
	}


void CApaScanningFileRecognizer::ScanDriveL(TInt aDriveNum)
	{
	// get a listing of the Recognizer dir (listing recognisers only)
	TDriveUnit driveUnit(aDriveNum);
	TParse parse;
	TDriveName name=driveUnit.Name();
	parse.Set(_L("*"),&KAppRecognizerSearchPath,&name);
	CDir* fileList=NULL;
	TInt ret=iFs.GetDir(parse.FullName(),TUidType(KNullUid,KUidFileRecognizer,KNullUid),ESortNone,fileList);
	if (ret!=KErrNone)
		{
		delete fileList;
		if (ret==KErrNoMemory)
			User::Leave(ret);
		else
			return; // dont scan the dir
		}
	//
	// check all files in the Recognizer dir
	TInt count=0;
	while (count<fileList->Count())
		{
		const TEntry& entry=(*fileList)[count++];
		// we found one
		TPtrC name=driveUnit.Name();
		ret = parse.Set(entry.iName,&KAppRecognizerSearchPath,&name);
		if (ret==KErrNone)
			TRAP(ret, LoadRecognizerL(parse.FullName(),entry.MostDerivedUid()));
		if (ret==KErrNoMemory)
			User::Leave(ret); // ignore all other errors, just try the next file
		}
	// tidy up
	delete fileList;	
	}


void CApaScanningFileRecognizer::LoadRecognizerL(const TDesC& aFullName,TUid aUid)
	{
	// check we haven't loaded this one already
	CApaRecognizerDll* lib=iRecognizerLib;
	CApaRecognizerDll* last=NULL; // the previous one in the list
	while (lib)
		{
		if (lib->iLibrary.Type()[2]==aUid)
			{// we may have to override one
			if (lib->iPresent!=CApaRecognizerDll::ENotPresent)
				return; // already found
			TParsePtrC ptr(aFullName);
			TDriveUnit drive(ptr.Drive());
			if (lib->iId.iDrive==drive)
				{// we've already got it
				lib->iPresent = CApaRecognizerDll::EIsPresent;
				return;
				}
			else
				{// it's time to override
				lib->iPresent = CApaRecognizerDll::ESuperseded;
				if (RemoveRecognizer(lib)!=KErrNone)
					return; // we can't remove it, so don't override
				break;
				}
			}
		last = lib;
		lib = lib->iNext;
		}
	//
	// load the library
	lib = new(ELeave) CApaRecognizerDll();
	CleanupStack::PushL(lib);
	User::LeaveIfError(lib->iLibrary.Load(aFullName));
	//
	// check its type
#if defined(__WINS__)
	if (lib->iLibrary.Type()[1]!=KUidFileRecognizer || lib->iLibrary.Type()[2]!=aUid)
		User::Leave(KErrNotSupported);
#else
	if (lib->iLibrary.Type()!=TUidType(KDynamicLibraryUid,KUidFileRecognizer,aUid))
		User::Leave(KErrNotSupported);
#endif
	//
	// construct a Recognizer from the library
	TLibraryFunction ctor = lib->iLibrary.Lookup(1);
	lib->iRec = (CApaFileRecognizerType*)((*ctor)());
	if (!lib->iRec)
		{
		CleanupStack::PopAndDestroy(); // lib
		return;
		}
	lib->iPresent = CApaRecognizerDll::EIsPresent;
	SetAppStarter(lib->iRec,iAppStarter);
	AddFileRecognizerType(lib->iRec);
	//
	// set the lib's ID
	TParsePtrC ptr(aFullName);
	TDriveUnit drive(ptr.Drive());
	lib->iId.iUid = aUid;
	lib->iId.iDrive = drive;
	lib->iId.iName = ptr.Name();
	iUpdateCounter++;
	//
	// add lib to the library list
	if (last)
		{
		while (last->iNext)
			last=last->iNext;
		last->iNext = lib;
		}
	else
		iRecognizerLib = lib;
	CleanupStack::Pop(); // lib
	}


EXPORT_C TInt CApaScanningFileRecognizer::RecognizerCount()
	{
	CApaRecognizerDll* lib=iRecognizerLib;
	TInt count=0;
	while (lib)
		{
		count++;
		lib = lib->iNext;
		}
	return count;
	}


EXPORT_C CApaScanningFileRecognizer::CRecognizerArray* CApaScanningFileRecognizer::RecognizerListLC()const
	{
	CRecognizerArray* list=new(ELeave) CArrayFixFlat<TRecognizer>(1);
	CleanupStack::PushL(list);
	CApaRecognizerDll* dll=iRecognizerLib;
	while (dll)
		{
		list->AppendL(dll->iId);
		dll = dll->iNext;
		}
	return list;
	}


EXPORT_C void CApaScanningFileRecognizer::SetRecognizersFromList(const CRecognizerArray& aList)
	{
	TParse parse;
	TFileName temp;
	for (TInt i=0 ; i<aList.Count() ; i++)
		{
		TDriveUnit drive(aList[i].iDrive);
		TDriveName name=drive.Name();
		parse.Set(KAppRecognizerSearchPath,&aList[i].iName,&name);
		temp = parse.FullName();
		parse.Set(KRecognizerDllExtension,&temp,NULL);
		TRAPD(ret, LoadRecognizerL(parse.FullName(),aList[i].iUid));
		if (ret==KErrNoMemory)
			User::Leave(ret); // ignore all other errors, just try the next file
		}
	}


EXPORT_C void CApaScanningFileRecognizer::SetRecognizerL(const TRecognizer& aRecognizer)
	{
	TDriveUnit drive(aRecognizer.iDrive);
	TParse parse;
	TDriveName name=drive.Name();
	parse.Set(KAppRecognizerSearchPath,&aRecognizer.iName,&name);
	LoadRecognizerL(parse.FullName(),aRecognizer.iUid);
	}


EXPORT_C CApaScanningFileRecognizer::TRecognizer CApaScanningFileRecognizer::operator[](TInt aIndex)const
	{
	__ASSERT_ALWAYS(aIndex>=0,Panic(EPanicNegativeIndex));
	// scan to correct dll
	TInt num=0;
	CApaRecognizerDll* dll=iRecognizerLib;
	while (dll && num++<aIndex)
		dll = dll->iNext;
	__ASSERT_ALWAYS(dll,Panic(EPanicIndexOutOfRange));
	//
	// get info from dll
	return dll->iId;
	}


EXPORT_C TInt CApaScanningFileRecognizer::UpdateCounter()const
	{
	return iUpdateCounter;
	}


///////////////////////////////
// CApaAppLocatorProxy
///////////////////////////////

EXPORT_C CApaAppLocatorProxy* CApaAppLocatorProxy::NewL(RFs& aFs)
	{
	CApaAppLocatorProxy* self = new(ELeave) CApaAppLocatorProxy(aFs);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(); // self
	return self;
	}


CApaAppLocatorProxy::CApaAppLocatorProxy(RFs& aFs)
:iFs(aFs)
	{}


void CApaAppLocatorProxy::ConstructL()
	{
	iAppFinder = CApaScanningAppFinder::NewL(iFs);
	}


EXPORT_C CApaAppLocatorProxy::~CApaAppLocatorProxy()
	{
	delete iAppFinder;
	}


EXPORT_C TInt CApaAppLocatorProxy::GetAppEntryByUid(TApaAppEntry& aAppEntry,TUid aAppUid)
	{
	TRAPD(ret, DoGetAppEntryByUidL(aAppEntry,aAppUid) );
	if (ret!=KErrNone)
		aAppEntry = TApaAppEntry();
	return ret;
	}


void CApaAppLocatorProxy::DoGetAppEntryByUidL(TApaAppEntry& aAppEntry,TUid aAppUid)
	{
	// locate the app by scanning the system
	iAppFinder->FindAllAppsL();
	while (iAppFinder->NextL(aAppEntry))
		{
		if (aAppEntry.iUidType.MostDerived()==aAppUid)
			return;
		}
	User::Leave(KErrNotFound);
	}
	

EXPORT_C TInt CApaAppLocatorProxy::GetAppCapabilityByUid(TDes8& aCapabilityBuf,TUid /*aAppUid*/)
// always zero-fills aCapability buff as apfile.dll doesn't know about app info files
	{
	aCapabilityBuf.FillZ(aCapabilityBuf.MaxLength()); // I don't know about app info files
	return KErrNotSupported;
	}

