/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
* @internalComponent 
* @released
*
*/


#include "image_reader.h"

ImageReader::ImageReader(const char* aFile) : iDisplayOptions(0),iImgFileName(aFile)
{
}

ImageReader::~ImageReader()
{
}

void ImageReader::SetDisplayOptions(TUint32 aFlag)
{
	iDisplayOptions |= aFlag;
}

bool ImageReader::DisplayOptions(TUint32 aFlag)
{
	return ((iDisplayOptions & aFlag) != 0);
}

void ImageReader::DumpData(TUint* aData, TUint aLength)
{
	TUint *p=aData;
	TUint i=0;
	char line[256];
	char *cp=(char*)aData;
	TUint j=0;
	memset(line,' ',sizeof(line));
	while (i<aLength)
		{
		TUint ccount=0;
		char* linep=&line[8*5+2];
		*out<< "0x";
		out->width(6);
		out->fill('0');
		*out << i << ":";
		while (i<aLength && ccount<4)
			{
			*out<< " ";
			out->width(8);
			out->fill('0');
			*out << *p++;
			i+=4;
			ccount++;
			for (j=0; j<4; j++)
				{
				char c=*cp++;
				if (c<32)
					{
					c = '.';
					}
				*linep++ = c;
				}
			}
		*linep = '\0';
		*out << line+(ccount*5) << endl;
		}
	}



/** 
Function to extract specified file from a given image. 

@internalComponent
@released
 
@param aOffset - starting offset of the file in the image.
@param aSize - size of the file in the image.
@param aFileName - name of the file.
@param aPath - full path of the file inside image.
@param aFilePath - path where file has to be extracted.
*/
void ImageReader::ExtractFile(TUint aOffset,TInt aSize,const char* aFileName,const char* aPath,char* aFilePath,char* aData)
{
	// concatenate path where specified file needs to be extracted with
	// path where file is located in the image.
	string fullPath( aFilePath );
	string delimiter( "\\" );
	string appStr( "\\\\" );
	fullPath.append( aPath );
	
	// replace all the occurrence of slash with double slash. 
	FindAndInsertString( fullPath, delimiter, delimiter );
	// now terminate the string with double slash.
	fullPath.append( appStr );

	// create specified directory where file needs to be extracted.
	CreateSpecifiedDir( &fullPath[0], appStr.c_str() );

	// concatenate path information with the filename
	fullPath.append( aFileName );

	// create an output stream to extract the specified file.
	ofstream outfile (fullPath.c_str(), ios::out | ios::binary);
	// create an input stream by opening the specified image file.
	ifstream infile(ImageReader::iImgFileName.c_str(),ios::in|ios::binary);

	//declare a buffer to store the data.
	char* buffer = new char[aSize];

	if(aData != NULL)
	{
		memcpy(buffer, aData + aOffset, aSize);
	}
	else if(infile.is_open())
	{
		// place the get pointer for the current input stream to offset bytes away from origin.
		infile.seekg(aOffset,ios::beg);
		//read number of bytes specified by the variable size 
		//from the stream and place it on to buffer.
		infile.read(buffer,aSize);
		//close the input stream after reading.
		infile.close();
	}
	else
	{
		throw ImageReaderException(ImageReader::iImgFileName.c_str(), "Failed to open the image file");
	}

	if(outfile.is_open())
	{
		//writes number of bytes specified by the variable size 
		//from buffer to the current output stream.
		outfile.write(buffer,aSize);
		//close the output stream after writing.
		outfile.close();
	}
	else
	{
		throw ImageReaderException(aFileName, "Failed to extract the file");
	}

	//delete the buffer.
	delete[] buffer;
}

/** 
Function to create a given directory. 

@internalComponent
@released

@param aSrcPath - path of the directory that needs to be created.
@param aDelimiter - delimiter.
*/
void ImageReader::CreateSpecifiedDir(char* aSrcPath,const char* aDelimiter)
{
	char* currWorkingDir = new char[_MAX_BUFFER_SIZE_];
	string origPath;

	origPath.assign(aSrcPath);


	// get the current working directory and store in buffer.
	if( GETCWD(currWorkingDir,_MAX_BUFFER_SIZE_) == NULL )
	{
		// throw an exception if unable to get current working directory information.
		throw ImageReaderException((char*)ImageReader::iImgFileName.c_str(), "Failed to get the current working directory");
	}
	else
	{
		char* cPtr = strtok(aSrcPath,aDelimiter);

		// check whether cPtr is a drive or a directory.
		if(IsDrive(cPtr))
		{
			// if yes, then change the directory to cPtr.
			string changeToDrive ;
			changeToDrive.assign(cPtr);
			changeToDrive.append(aDelimiter);
			
			// change the current working directory to the specified directory.
			if( CHDIR(changeToDrive.c_str()) )
			{
				// throw an exception if unable to change the directory specified.
				throw ImageReaderException((char*)ImageReader::iImgFileName.c_str(), "Failed to change to the directory specified");
			}
		}
		else
		{
			// if not,then create a cPtr directory. 
			MKDIR(cPtr);
			// change the current working directory to cPtr.
			int r = CHDIR(cPtr); (void)r;
		}
		// repeat till cPtr is NULL.
		while (cPtr!=NULL)
		{
			if ((cPtr = strtok(NULL,aDelimiter)) != NULL)
			{
				// create the directory.
				MKDIR(cPtr);
				// change current working directory.
				int r = CHDIR(cPtr); (void)r;
			}
		}
		// revert back the working directory.
		int r = CHDIR(currWorkingDir); (void)r;
		// replace the source path with the original path information.
		strcpy(aSrcPath,origPath.c_str());
		delete[] currWorkingDir;
	}
}


/** 
Function to check whether the given string is a drive or a folder.

@internalComponent
@released

@param aStr - string to be checked.
@return - returns True if the given string is a drive else returns false.
*/
TBool ImageReader::IsDrive(char* aStr)
{
	TInt strlength = strlen(aStr);
	//check for the last character in a given string,
	//if the last character is colon then return true else false.
	if(!strcmp(&aStr[strlength - 1],":"))
	{
		return true;
	}
	else
	{
		return false;
	}
}


/** 
Function to insert a given string with a delimiter.

@internalComponent
@released

@param aSrcStr - string to be modified.
@param aDelimiter - string to be checked.
@param aAppStr - string to be inserted with the delimiter.
*/
void ImageReader::FindAndInsertString(string& aSrcStr,string& aDelimiter,string& aAppStr)
{
	string::size_type loc = 0;
	string::size_type pos =0;
	while(( pos = aSrcStr.find( aDelimiter, loc ) ) != ( string::npos ) )
	{
		if( pos != string::npos )
		{
			aSrcStr.insert(pos,aAppStr);
			loc = pos + aAppStr.length() + 1;
		}
	}
}

/** 
Function to replace a delimiter with a given string.

@internalComponent
@released

@param aSrcStr - string to be modified.
@param aDelimiter - string to be checked.
@param aReplStr - string to be replaced with the delimiter.
*/
void ImageReader::FindAndReplaceString( string& aSrcStr, string& aDelimiter, string& aReplStr )
{
	string::size_type loc = 0;
	string::size_type pos =0;
	while(( pos = aSrcStr.find( aDelimiter,loc) ) != ( string::npos ) )
	{
		if( pos != string::npos )
		{
			aSrcStr.replace( pos, aReplStr.length(),aReplStr );
			loc = pos + aReplStr.length() + 1;
		}
	}
}

/** 
Function to extract individual or a subset of file.

@internalComponent
@released

@param aData - ROM/ROFS image buffer pointer.
*/
void ImageReader::ExtractFileSet(char* aData)
{
	FILEINFOMAP fileInfoMap;
	string dirSep(DIR_SEPARATOR), backSlash("\\"), Pattern;
	TUint extfileCount = 0, noWcardFlag = 0, pos;

	//Get the filelist map
	GetFileInfo(fileInfoMap);

	//Check for wildcards
	pos = iPattern.rfind("\\");
	if(pos == string::npos)
	{
		pos = iPattern.rfind("/");
		if(pos == string::npos)
			pos = 0;
	}
	pos = iPattern.find_first_of("*?", pos);
	if(pos == string::npos)
	{
		noWcardFlag = 1;
	}

	//Process the map
	if(fileInfoMap.size() > 0)
	{
		FILEINFOMAP::iterator begin = fileInfoMap.begin();
		FILEINFOMAP::iterator end = fileInfoMap.end();

		// Replace all backslashes with forward slashes
		Pattern = iPattern;
		FindAndReplaceString(Pattern, backSlash, dirSep);

		// Insert root directory at the beginning if it is not there
		pos = Pattern.find_first_not_of(" ", 0);
		if(pos != string::npos)
		{
			if(Pattern.at(pos) != *DIR_SEPARATOR)
				Pattern.insert(pos, dirSep);
		}

		// Assign CWD for destination path if it is empty
		if(ImageReader::iZdrivePath.empty())
			ImageReader::iZdrivePath.assign(".");

		while(begin != end)
		{
			int status = 0;
			PFILEINFO pInfo = 0;
			string fileName = (*begin).first;
			pInfo = (*begin).second;

			// First match
			status = FileNameMatch(Pattern, fileName, (iDisplayOptions & RECURSIVE_FLAG));

			// If no match
			if((!status) && noWcardFlag)
			{
				string newPattern(Pattern);

				// Add * at the end
				if(newPattern.at(Pattern.length()-1) != *DIR_SEPARATOR)
				{
					newPattern.append(DIR_SEPARATOR);
				}
				newPattern += "*";
				status = FileNameMatch(newPattern, fileName, (iDisplayOptions & RECURSIVE_FLAG));

				// If it matches update the pattern and reset wildcard flag
				if(status)
				{
					Pattern = newPattern;
					noWcardFlag = 0;
				}
			}

			if(status)
			{
				// Extract the file

				// Separarate the path and file name
				string fullPath = fileName.substr(0, fileName.rfind(DIR_SEPARATOR));
				string file = fileName.substr(fileName.rfind(DIR_SEPARATOR)+1, fileName.length());
				FindAndReplaceString(fullPath, dirSep, backSlash);

				// Extract only those files exists in the image
				if(pInfo->iSize && pInfo->iOffset)
				{
					ExtractFile(pInfo->iOffset, pInfo->iSize, file.c_str(), fullPath.c_str() , 
						&ImageReader::iZdrivePath[0], aData);

					extfileCount++;
				}
			}

			if(pInfo)
				delete pInfo;
			++begin;
		}
		fileInfoMap.clear();
	}

	// Throw error if the extracted file count is zero
	if(!extfileCount)
	{
		throw ImageReaderException((char*)ImageReader::iImgFileName.c_str(), "No matching files found for the given pattern");
	}
}

/** 
To match the given file name aganist the given pattern with wildcards

@internalComponent
@released

@param aPattern - input filename pattern.
@param aFileName - input file name.
@param aRecursiveFlag - recursive search flag.
*/
int ImageReader::FileNameMatch(string aPattern, string aFileName, int aRecursiveFlag)
{
	const char *InputString = aFileName.c_str();
	const char *Pattern = aPattern.c_str();
	const char *CurrPattern = 0, *CurrString = 0;
	
	// If the input is empty then return false
	if((aPattern.empty()) || (!InputString))
		return 0;

	// First candidate match
	// Step 1: Look for the exact matches between the input pattern and the given file-name till 
	//         the first occurrence of wildcard character (*). This should also skip a character 
	//         from matching for the occurrence of wildcard character(?) in the pattern.
	while ((*InputString) && (*Pattern != '*')) 
	{
		if ((toupper(*Pattern) != toupper(*InputString)) && (*Pattern != '?')) 
		{
			return 0;
		}
		Pattern++;
		InputString++;
	}
	
	// Wildcard match
	// Step 2: Now the input string (file-name) should be checked against the wildcard characters (* and ?). 
	//         Skip the input string if the pattern points to wildcard character(*). Do the exact match for 
	//         other characters in the patterns except the wildcard character(?). The path-separator should be 
	//         considered as non-match for non-recursive option.
	while (*InputString) 
	{
		if ((*Pattern == '*')) 
		{
			if (!*++Pattern) 
			{
				// If recursive flag is set then this case matches for any character of the input string
				// from the current position
				if(aRecursiveFlag)
					return 1;
			}

			// Update the current pattern and the current inputstring
			CurrPattern = Pattern;
			CurrString = InputString+1;			
		} 
		else if ((toupper(*Pattern) == toupper(*InputString)) || (*Pattern == '?')) 
		{
			// Exact match for the path separator
			// So recursively call the function to look for the exact path level match
			if(*Pattern == '/')
				return FileNameMatch(Pattern, InputString, aRecursiveFlag);

			// Exact match so increment both
			Pattern++;
			InputString++;
		} 
		else if ((*InputString == *DIR_SEPARATOR) && (!aRecursiveFlag))
		{
			// Inputstring points to path separator and it is not expected here for non-recursive case
			return 0;
		}
		else
		{
			// Default case where it matches for the wildcard character *
			Pattern = CurrPattern;
			InputString = CurrString++;
		}
	}
	
	// Leave any more stars in the pattern
	while (*Pattern == '*') 
	{
		Pattern++;
	}
	
	// Return the status
	return !*Pattern;
}
