/*
* 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"
#include <stdio.h>
#include <stdlib.h>
#include <boost/filesystem.hpp>
 
using namespace boost::filesystem;
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,const char* aFilePath,const char* aData) {
	// concatenate path where specified file needs to be extracted with
	// path where file is located in the image.
	string fullPath( aFilePath );
	if(*aPath != SLASH_CHAR1 && *aPath != SLASH_CHAR2){
		char ch = aFilePath[fullPath.length() - 1];
		if(ch != SLASH_CHAR1 && ch != SLASH_CHAR2)
			fullPath += SLASH_CHAR1 ;		
	}
	int startImagePath = (int)fullPath.length();
	fullPath += aPath ;	
	
	// create specified directory where file needs to be extracted. 
	// to lower
	char* data = const_cast<char*>(fullPath.data() + startImagePath);
	for(; *data != 0 ; data++){
		if(*data >= 'A' && *data <= 'Z'){
			*data |= 0x20 ;
		}
	}
	CreateSpecifiedDir(fullPath);
	data -- ;
	if(*data != SLASH_CHAR1)
		fullPath += SLASH_CHAR1 ; 
	
	// concatenate path information with the filename	
	fullPath +=  aFileName ;

	// create an output stream to extract the specified file.  
	ofstream outfile (fullPath.c_str(), ios_base::out | ios_base::binary);
	// create an input stream by opening the specified image file.
	ifstream infile(ImageReader::iImgFileName.c_str(),ios_base::in|ios_base::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_base::beg);
		//read number of bytes specified by the variable size 
		//from the stream and place it on to buffer.
		if (aSize)
			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.
		if (aSize)
			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. 
*/
void ImageReader::CreateSpecifiedDir(const string& aSrcPath) {	 
	 char* currWorkingDir = new char[PATH_MAX];
	int len = aSrcPath.length() ;
	const char* origPath = aSrcPath.c_str();	 
	char* path = new char[len + 2]; 

	memcpy(path,origPath,len);
	if(path[len - 1] != SLASH_CHAR1 && path[len - 1] != SLASH_CHAR2){
		path[len] = SLASH_CHAR1 ;
		len ++ ;
	}
	path[len] = 0; 
	char* start = path;
	char* end  = path + len ;
	char errMsg[400]  ;
	*errMsg = 0;
	char* dirEnd ;
	
	// get the current working directory and store in buffer.
	if( getcwd(currWorkingDir,PATH_MAX) == NULL ) {		 
		// throw an exception if unable to get current working directory information.
		snprintf(errMsg,400,"Failed to get the current working directory") ;
		goto L_EXIT;
	}
#ifdef WIN32
//check dir 
	if(isalpha(start[0]) && start[1] == ':'){
		char ch = start[3] ;
		start[3] = 0;
		if(chdir(start)) {
			snprintf(errMsg, 400 ,"Failed to change to the directory \"%s\".",path);
			goto L_EXIT;
		}
		start[3] = ch ;
		start += 3 ;		 
	}
	else if(*start == SLASH_CHAR1 || *start == SLASH_CHAR2){
		if(chdir("\\")){
			snprintf(errMsg, 400 ,"Failed to change to the directory \"\\\".");
			goto L_EXIT;
		}
		start ++ ;
	}
#else
	if(*start == SLASH_CHAR1 || *start == SLASH_CHAR2){
		if(chdir("/")) {
			snprintf(errMsg, 400 ,"Failed to change to the directory \"/\".");
			goto L_EXIT;
		}
		start ++ ;
	}	
#endif
	dirEnd = start ;

	while( start < end ) {		
		while(*dirEnd != SLASH_CHAR1 && *dirEnd != SLASH_CHAR2)
			dirEnd ++ ;
		*dirEnd =  0 ;
		
		if(!exists(start)) {  
			MKDIR(start);
		}   
		if(chdir(start)){
			snprintf(errMsg, 400 ,"Failed to change to the directory \"%s\".",path);
			goto L_EXIT;
		}
		*dirEnd = SLASH_CHAR1;
		start = dirEnd + 1;
		dirEnd = start ;		 
	} 
L_EXIT:
	chdir(currWorkingDir);
	delete[] currWorkingDir;
	delete [] path;
	if(*errMsg)
	 throw ImageReaderException(ImageReader::iImgFileName.c_str(), errMsg); 
}

 
 


/** 
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(const char* aData) {
	FILEINFOMAP fileInfoMap; 
	TUint extfileCount = 0, noWcardFlag = 0 ;

	//Get the filelist map
	GetFileInfo(fileInfoMap);

	//Check for wildcards
	const char* patternStr = iPattern.c_str();
	TInt dp = iPattern.length() - 1;
	while(dp >= 0){
		if(patternStr[dp] == SLASH_CHAR1 || patternStr[dp] == SLASH_CHAR2)
			break ;
		dp -- ;
	} 
	size_t pos = iPattern.find_first_of("*?",dp + 1);
	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
		string pat(iPattern);
		for(size_t n = 0 ; n < iPattern.length(); n++){
			if(patternStr[n] == SLASH_CHAR2)
				pat[n] = SLASH_CHAR1 ;
		}
	 
		// Insert root directory at the beginning if it is not there
		pos = pat.find_first_not_of(" ", 0);
		if(pos != string::npos) {
			if(pat.at(pos) != SLASH_CHAR1)
				pat.insert(pos, 1,SLASH_CHAR1);
		}

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

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

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

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

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

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

			if(status) {
				// Extract the file

				// Separarate the path and file name
				int slash_pos = fileName.rfind(SLASH_CHAR1);
				string fullPath = fileName.substr(0,slash_pos );
				string file = fileName.substr(slash_pos + 1, fileName.length());
				//FindAndReplaceString(fullPath, dirSep, backSlash);
				char* fpStr = const_cast<char*>(fullPath.c_str());
				for(size_t m = 0 ; m < fullPath.length() ; m++){
					if(fpStr[m] == SLASH_CHAR2)
						fpStr[m] = SLASH_CHAR1 ;
				}

				// 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.c_str(), aData);

					extfileCount++;
				}
			}

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

	// Throw error if the extracted file count is zero
	if(!extfileCount) {
		throw ImageReaderException(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(const string& aPattern, const 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 == SLASH_CHAR1)
				return FileNameMatch(Pattern, InputString, aRecursiveFlag);

			// Exact match so increment both
			Pattern++;
			InputString++;
		} 
		else if ((*InputString == SLASH_CHAR1) && (!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;
}
