/*
* Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
* Contact: http://www.qt-project.org/legal
* 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 "cntfilesearch.h"

const TInt KNameArrayGranularity = 32;
                         
//Define the patchable data for database cache size here but which is actually used
//in CPplContactsFile.cpp. This is a must-be so that the compiler cannot perform any
//"constant folding" with the value   
EXPORT_C extern const TInt KContactsModelSqliteDbCacheSize = 0;
                         
_LIT(KSqLiteFilePrefix, "SQLite__");
const TInt KPrefixNameLength = 8;   // length of above prefix string

CDesCArray* CCntFileScanner::ListFilesL(RFs& aFs, TDriveUnit* aDriveUnit)
	{
	CCntFileScanner* scanner = new (ELeave) CCntFileScanner(aFs);  // gets pushed in first lines of ScanLD
	// coverity [memory_leak]
	return scanner->ScanLD(aDriveUnit);
	}


CCntFileScanner::CCntFileScanner(RFs& aFs)
	:
	iFs(aFs)
	{
	}


CCntFileScanner::~CCntFileScanner()
	{
	delete iDirStack;
	}


CDesCArray* CCntFileScanner::ScanLD(TDriveUnit* aDriveUnit)
	{
	CleanupStack::PushL(this);

	iFs.PrivatePath(iPath);
	iDirStack = new(ELeave) CDesCArrayFlat(KNameArrayGranularity);

	if (aDriveUnit)
		{
		AddDirL(*aDriveUnit);
		}
	else
		{
		GenerateRootDirsL();
		}

	CDesCArray* results = new(ELeave) CDesCArrayFlat(KNameArrayGranularity);
	CleanupStack::PushL(results);
	ScanForFilesL(*results);
	CleanupStack::Pop(results);
	CleanupStack::PopAndDestroy(this);

	return results;
	}


void CCntFileScanner::AddDirL(TDriveUnit& aDriveUnit)
	{
	ASSERT(iDirStack);
	TBuf<3> root;
	root.Copy(aDriveUnit.Name());
	root.Append(TChar(KPathDelimiter));
	iDirStack->AppendL(root);
	}

	
void CCntFileScanner::GenerateRootDirsL()
	{
	ASSERT(iDirStack);
	// Generate list of root dirs.
	TBuf<3> root;
	TDriveList driveList;
	User::LeaveIfError(iFs.DriveList(driveList));
	TInt len = driveList.Length();
	for (TInt index = 0; index < len; ++index)
		{
		if(driveList[index]) // Check drive exists.
			{
			root.Copy(TDriveUnit(index).Name());
			root.Append(TChar(KPathDelimiter));
			iDirStack->AppendL(root);
			}
		}
	}


void CCntFileScanner::ScanForFilesL(CDesCArray& aResults)
	{
	ASSERT(iDirStack);
	// Traverse the directory stack until all directories have been examined.
	
	TFileName filename;
	while(iDirStack->Count())
		{
		// Pop the dir.
		filename.Copy(iDirStack->MdcaPoint(0));
		iDirStack->Delete(0);
		if(iPath.Length()!=0)
			{
			filename.Delete(2,1);
			filename.Append(iPath);
			}
		else if(filename[filename.Length() - 1] != KPathDelimiter)
			{ // Check it has a trailing '\' to identify it as a dir path.
			filename.Append(TChar(KPathDelimiter));
			}
		TRAPD(err,ScanDirectoryL(filename, aResults));
		if(err==KErrNoMemory)
			{ // Continue to search in each drive except for out of memory.
			User::Leave(err);
			}
		}
	}


/**
Get a list of all files and subdirectories in aDirPath (when the path is not
specified).
*/
void CCntFileScanner::ScanDirectoryL(const TDesC& aDirPath, CDesCArray& aResults)
	{
	CDir* files = NULL;
	CDir* subdirs = NULL;

	User::LeaveIfError(iFs.GetDir(aDirPath, KEntryAttAllowUid, ESortByName, files, subdirs));

	TRAPD(err,AppendFileNamesL(*files, aDirPath, aResults));

	delete files;
	delete subdirs;
	User::LeaveIfError(err);
	}


void CCntFileScanner::AppendFileNamesL(const CDir& aFileSet, const TDesC& aDirPath, CDesCArray& aFileNames) const
	{
	TParse parse;
	TInt len = aFileSet.Count();
	
	for(TInt idx = 0; idx < len ; ++idx)
		{
		const TEntry& entry = aFileSet[idx];
		User::LeaveIfError(parse.SetNoWild(entry.iName, &aDirPath, NULL));
			
		TInt found = parse.NameAndExt().FindF(KSqLiteFilePrefix);
				
		if (found == 0 && iPath.Length() > 0)
			{
			// add drive, remove prefix, add to list of files
			TFileName filename(parse.Drive());
			filename.Append(parse.NameAndExt().Right(parse.NameAndExt().Length() - KPrefixNameLength));	
						
			aFileNames.AppendL(filename);
			}
			
		}
	}
