/*
* Copyright (c) 2008-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: 
*
*/


#include "sisutils.h"
#include "sis2iby.h"

/**
Constructor: Sis2Iby class
Initilize the parameters to data members.

@internalComponent
@released

@param aFile	- SIS file name
*/
Sis2Iby::Sis2Iby(const char* aFile) : SisUtils(aFile) {
}

/**
Destructor: Sis2Iby class
Deallocates the memory for data members

@internalComponent
@released
*/
Sis2Iby::~Sis2Iby() {
	PKGFILE_MAP::iterator begin = iPkgFileMap.begin();
	PKGFILE_MAP::iterator end = iPkgFileMap.end();
	while(begin != end) {
		PPKGPARSER ptemp = 0;
		ptemp = (*begin).second;

		if(ptemp)
			delete ptemp;
		++begin;
	}
	iPkgFileMap.clear();
}

/**
ProcessSisFile: Processes the input sis file
  Invoke the DUMPSIS tool to extract the sis file contents
  Creates package parser object for each of the package file

@internalComponent
@released
*/
void Sis2Iby::ProcessSisFile() {
	TUint32 retStatus = STAT_SUCCESS;
	string sisFile = SisFileName();

	if(IsVerboseMode()) {
		cout << "Processing " << sisFile.c_str() << endl;
	}

	if(IsFileExist(sisFile)) {
		retStatus = InvokeExtractTool(sisFile);

		switch(retStatus) {
		case STAT_SUCCESS: 
			UpdatePkgFileMap(iExtractPath, sisFile); 
			break;
		case STAT_FAILURE:
			throw SisUtilsException(sisFile.c_str(), "Failed to extract SIS file");
			break ;
		}
	}
	else
		throw SisUtilsException(sisFile.c_str(), "File not found");
}

/**
GenerateOutput: Generates IBY for each of the package file

@internalComponent
@released
*/
void Sis2Iby::GenerateOutput() {
	PKGFILE_MAP::iterator begin = iPkgFileMap.begin();
	PKGFILE_MAP::iterator end = iPkgFileMap.end();
	while(begin != end) {
		GenerateIby((*begin).first, (*begin).second);
		++begin;
	}
}

/**
GenerateOutput: Generates IBY file for the given package file

@internalComponent
@released

@param aPkgFile - package file name
@param aParser - corresponding package file reader object
*/
void Sis2Iby::GenerateIby(string aPkgFile, PPKGPARSER aParser) {
	string ibyFile = iOutputPath;
	
	AppendFileName(ibyFile, aPkgFile);
	ibyFile.append(".iby");

	if( !MakeDirectory(iOutputPath) )
		throw SisUtilsException(iOutputPath.c_str(), "Failed to create path");

	if(IsVerboseMode())	{
		cout << "Generating IBY file " << ibyFile.c_str() << endl;
	}

	ibyHandle.open(ibyFile.c_str(),ios_base::out);

	if(!ibyHandle.good()) 	{
		throw SisUtilsException(ibyFile.c_str() , "Failed to create IBY file");
	}

	// Generating Header
	MakeFullPath(aPkgFile);
	ibyHandle << "\n// Generated IBY file for the package file: ";
	ibyHandle << aPkgFile;

	// Language Supported
	WriteLanguages(aParser);

	// Package Header
	WritePackageHeader(aParser);

	// Install options list
	WriteInstallOptions(aParser);

	// Package Body
	WritePackageBody(aParser);

	ibyHandle.close();
}

/**
InvokeExtractTool: Invokes the SIS file extraction tool and returns the status

@internalComponent
@released

@param sisFile - SIS file name
*/
TUint32 Sis2Iby::InvokeExtractTool(const string& aSisFile) {
	string cmdLine;

	cmdLine.append(SISEXTRACT_TOOL_NAME SISEXTRACT_TOOL_DEFOPT);

	AppendFileName(iExtractPath, aSisFile);

	cmdLine.append(SISEXTRACT_TOOL_EXTOPT);
	cmdLine.append("\"" + iExtractPath + "\" ");
	cmdLine.append(aSisFile);

	if(IsVerboseMode()) {
		cout << "Executing " << cmdLine.c_str() << endl;
	}

	return RunCommand(cmdLine.c_str());
}

/**
UpdatePkgFileMap: Update the package file map by getting the embedded sis file list from the parser object

@internalComponent
@released

@param aPath - Extract path
@param aFile - SIS file name
*/
void Sis2Iby::UpdatePkgFileMap(const string& aPath, const string& aFile) {
	string pkgFileName;
	list<string> sisList;

	// main pkg file
	pkgFileName = aPath;
	AppendFileName(pkgFileName, aFile);
	pkgFileName.append(".pkg");

	// create an instance for the pkg file parser
	// get the embedded sis file list
	// add each as pkg file into the list
	pkgParser = 0;
	if( IsFileExist(pkgFileName) ) {
		pkgParser = new PkgParser(pkgFileName);

		if(pkgParser) {
			pkgParser->ParsePkgFile();

			iPkgFileMap[pkgFileName] = pkgParser;

			pkgParser->GetEmbeddedSisList(sisList);
			SISFILE_LIST::iterator begin = sisList.begin();
			SISFILE_LIST::iterator end = sisList.end();

			while(begin != end) {
				string currPath = aPath;

				currPath.append(PATHSEPARATOR);
				GetFileName((*begin), currPath);
				UpdatePkgFileMap(currPath, (*begin));

				++begin;
			}
		}
		else
			throw SisUtilsException(pkgFileName.c_str(), "Could not create parser object");
	}
	else
		throw SisUtilsException(pkgFileName.c_str(), "File not found");
}

/**
WriteLanguages: Writes language section in the IBY file

@internalComponent
@released

@param aParser - Package file parser object
*/
void Sis2Iby::WriteLanguages(PPKGPARSER aParser) {
	LANGUAGE_LIST lanMap;
	PLANG_LIST iLangCode;

	aParser->GetLanguageList(lanMap);
	ibyHandle << "\n// Languages: ";

	LANGUAGE_LIST::iterator begin = lanMap.begin();
	LANGUAGE_LIST::iterator end = lanMap.end();

	while(begin != end) {
		iLangCode = (*begin);

		ibyHandle << " " << iLangCode->iLangName;
		ibyHandle << "(" << iLangCode->iLangCode;

		if(iLangCode->iDialectCode) {
			ibyHandle << "-" << iLangCode->iDialectCode;
		}
		ibyHandle << ")";

		++begin;
	}
}

/**
WritePackageHeader: Writes package header section in the IBY file

@internalComponent
@released

@param aParser - Package file parser object
*/
void Sis2Iby::WritePackageHeader(PPKGPARSER aParser) {
	PKG_HEADER pkgHeader;
	list<string> pkgList;
	ostringstream str;

	aParser->GetHeader(pkgHeader);

	ibyHandle << "\n// Header: ";

	pkgList = pkgHeader.iPkgNames;
	while(pkgList.size())
	{
		ibyHandle << "\"" << pkgList.front() << "\" ";
		pkgList.pop_front();
	}

	str << "(0x" << setbase(16) << pkgHeader.iPkgUID << ")";

	ibyHandle << str.str();
}

/**
WriteInstallOptions: Writes install option section in the IBY file

@internalComponent
@released

@param aParser - Package file parser object
*/
void Sis2Iby::WriteInstallOptions(PPKGPARSER aParser) {
	list<string> optList;
	string ibyName;

	aParser->GetInstallOptions(optList);
	SISFILE_LIST::iterator begin = optList.begin();
	SISFILE_LIST::iterator end = optList.end();

	if(begin != end) {
		ibyHandle << "\n// Install Options: ";
	}

	while(begin != end) {
		ibyHandle << " \"" << (*begin) << "\"";
		++begin;
	}
}

/**
InsertTabs: Inserts spaces for indentation in the output IBY file

@internalComponent
@released

@param num - num of spaces to be inserted
*/
void Sis2Iby::InsertTabs(TInt num) {
	ibyHandle << "\n";
	while(num--) {
		ibyHandle << "  ";
	}
}

/**
WritePackageBody: Writes package body details in the IBY file

@internalComponent
@released

@param aParser - Package file parser object
*/
void Sis2Iby::WritePackageBody(PPKGPARSER aParser) {
	CMDBLOCK_LIST cmdList;
	PCMD_BLOCK cmd;
	TInt pad = 0;

	ibyHandle << "\n\n";
	aParser->GetCommandList(cmdList);

	CMDBLOCK_LIST::iterator begin = cmdList.begin();
	CMDBLOCK_LIST::iterator end = cmdList.end();

	while(begin != end) {
		cmd = (*begin);

		switch(cmd->iCmdType)
		{
		case IF:			 
			InsertTabs(pad);
			ibyHandle << "#if " << cmd->iCmdExpr;
			pad++;
			 
			break;
		case ELSEIF:			
			InsertTabs(pad-1);
			ibyHandle << "#elif " << cmd->iCmdExpr;			
			break;
		case ELSE:		
			InsertTabs(pad-1);
			ibyHandle << "#else";			
			break;
		case ENDIF:			
			--pad;
			InsertTabs(pad);
			ibyHandle << "#endif";			
			break;
		case INSTALLFILE:			
			WriteInstallFileList(cmd->iInstallFileList, aParser, pad);			
			break;
		case PACKAGE:			
			InsertTabs(pad);
			ibyHandle << "#include " << "\"" << cmd->iCmdExpr << "\"";			
			break;
		}

		++begin;
	}
}

/**
WriteFileInclusion: Writes installable file details in the IBY file

@internalComponent
@released

@param aSrcFile - Name of the source file
@param aDestFile - Name of the destination file
@param aPkgName - Name of the package file
*/
void Sis2Iby::WriteFileInclusion(string aSrcFile, string aDestFile, string aPkgName, TInt aPadding) {
	NormaliseSourceFile(aSrcFile, aPkgName);

	InsertTabs(aPadding);
	if(IsValidE32Image(aSrcFile)){
		ibyHandle << "file = ";
	}
	else {
		ibyHandle << "data = ";
	}

	ibyHandle << aSrcFile << " ";
	NormaliseDestFile(aDestFile);
	ibyHandle << aDestFile;
}

/**
WriteInstallFileList: Writes installable file details in the IBY file

@internalComponent
@released

@param aFileList - Installable file list structure
@param aParser - Package file parser object
@param pad - Number of spaces for indentation purpose
*/
void Sis2Iby::WriteInstallFileList(PINSTALLFILE_LIST aFileList, PPKGPARSER aParser, TInt aPadding) {
	WriteFileInclusion(aFileList->iSourceFiles.front(), aFileList->iDestFile, aParser->GetPkgFileName(), aPadding);
}

/**
AppendFileName: Appends file name to the given path

@internalComponent
@released

@param aPath - Source path
@param aFile - File name
*/
void Sis2Iby::AppendFileName(string& aPath, string aFile) {
	TUint pos = 0;

	TrimQuotes(aPath);
	TrimQuotes(aFile);

	pos = aPath.rfind(PATHSEPARATOR);
	if(pos == string::npos) {
		aPath.append(PATHSEPARATOR);
	}

	if(pos < (aPath.length() - 1)) {
		aPath.append(PATHSEPARATOR);
	}

	GetFileName(aFile, aPath);
	return;
}

/**
GetFileName: Returns the base file name

@internalComponent
@released

@param aName - Input file name
@param aFile - Output parameter to hold the return value
*/
void Sis2Iby::GetFileName(const string& aName, string& aFile) {
	TUint spos = 0, epos = 0;

	spos = aName.rfind(PATHSEPARATOR);
	if(spos != string::npos) {
		spos += 1;
	}
	else {
		spos = 0;
	}

	epos = aName.rfind(".");
	if(epos == string::npos) {
		epos = aName.size();
	}

	aFile.append(aName.substr(spos, (epos-spos)));
}

/**
MakeFullPath: Returns the absolute path of the given file

@internalComponent
@released

@param aFile - Input file name
*/
#ifndef _MAX_PATH
#define _MAX_PATH 1024
#endif
void Sis2Iby::MakeFullPath(string& aFile) {
 
	char path[_MAX_PATH];
	if( _fullpath(path, aFile.c_str(), _MAX_PATH) != NULL ) {
		aFile.assign(path);
	}

}

/**
NormaliseSourceFile: Normalise the source file with its absolute path

@internalComponent
@released

@param aFile - Input file name
@param aPkgFile - Package file path
*/
void Sis2Iby::NormaliseSourceFile(string& aFile, const string& aPkgFile) {
	string result;
	TUint pos = 0;

	pos = aPkgFile.rfind(PATHSEPARATOR);
	if(pos != string::npos) {
		result = aPkgFile.substr(0,pos);
	}
	else {
		result = ".";
	}

	result.append(PATHSEPARATOR);
	result.append(aFile);

	MakeFullPath(result);

	aFile = "\"" + result + "\"";
}

/**
NormaliseDestFile: Normalise the destination file

@internalComponent
@released

@param aFile - Input file name
*/
void Sis2Iby::NormaliseDestFile(string& aFile) {
	TUint pos = 0; 
	pos = aFile.find(":");
	if (1 == pos) {
		char chFirst = aFile[0];
		if ('$' == chFirst || '!' == chFirst || (chFirst >='a' && chFirst <='z') || (chFirst >='A' && chFirst <='Z')) {
			aFile.replace(0, 2, "");
		}
	}
 

	aFile = "\"" + aFile + "\"";
}

/**
IsValidE32Image: Checks whether the given file is E32 image

@internalComponent
@released

@param aFile - Input file name
*/
TBool Sis2Iby::IsValidE32Image(string aFile) {
	ifstream file;
	char sig[5];
	TUint32 e32SigOffset = 0x10, fileSize = 0;
	TBool validE32 = EFalse;

	TrimQuotes(aFile);

	file.open(aFile.c_str(), ios_base::in | ios_base::binary);

	if( !file.is_open() ) {
		throw SisUtilsException(aFile.c_str(), "Cannot open file");
	}

	file.seekg(0,ios_base::end);
	fileSize = file.tellg();
	if(fileSize > 20) {
		file.seekg(e32SigOffset,ios_base::beg);
		file.read(sig, 4);
		sig[4] = '\0';

		if(!strcmp(sig, "EPOC")) {
			validE32 = ETrue;
		}
	}

	file.close();
	return validE32;
}
