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


/**
 @file 
 @internalComponent
 @released
*/

#include "sisexpression.h"
#include "utility.h"
#include <hal_data.h>

#define LANGUAGE_VARIABLE	L"LANGUAGE"


const SKeyword KVariables [] =
	{
		{L"MANUFACTURER",				HALData::EManufacturer},
		{L"MANUFACTURERHARDWAREREV",	HALData::EManufacturerHardwareRev},
		{L"MANUFACTURERSOFTWAREREV",	HALData::EManufacturerSoftwareRev},
		{L"MANUFACTURERSOFTWAREBUILD",	HALData::EManufacturerSoftwareBuild},
		{L"MODEL",						HALData::EModel},
		{L"MACHINEUID",					HALData::EMachineUid},
		{L"DEVICEFAMILY",				HALData::EDeviceFamily},
		{L"DEVICEFAMILYREV",			HALData::EDeviceFamilyRev},
		{L"CPU",						HALData::ECPU},
		{L"CPUARCH",					HALData::ECPUArch},
		{L"CPUABI",						HALData::ECPUABI},
		{L"CPUSPEED",					HALData::ECPUSpeed},
		//{L"SystemStartupReason",		HALData::ESystemStartupReason},
		//{L"SystemException",			HALData::ESystemException},
		{L"SYSTEMTICKPERIOD",			HALData::ESystemTickPeriod},
		{L"MEMORYRAM",					HALData::EMemoryRAM},
		{L"MEMORYRAMFREE",				HALData::EMemoryRAMFree},
		{L"MEMORYROM",					HALData::EMemoryROM},
		{L"MEMORYPAGESIZE",				HALData::EMemoryPageSize},
		//{L"PowerGood",				HALData::EPowerGood},
		//{L"PowerBatteryGood",			HALData::EPowerBatteryGood},
		{L"POWERBACKUP",				HALData::EPowerBackup},
		//{L"PowerBackupGood",			HALData::EPowerBackupGood},
		//{L"PowerExternal",				HALData::EPowerExternal},
		{L"KEYBOARD",					HALData::EKeyboard},
		{L"KEYBOARDDEVICEKEYS",			HALData::EKeyboardDeviceKeys},
		{L"KEYBOARDAPPKEYS",			HALData::EKeyboardAppKeys},
		{L"KEYBOARDCLICK",				HALData::EKeyboardClick},
		//{L"KeyboardClickState",		HALData::EKeyboardClickState},
		//{L"KeyboardClickVolume",		HALData::EKeyboardClickVolume},
		{L"KEYBOARDCLICKVOLUMEMAX",		HALData::EKeyboardClickVolumeMax},
		{L"DISPLAYXPIXELS",				HALData::EDisplayXPixels},
		{L"DISPLAYYPIXELS",				HALData::EDisplayYPixels},
		{L"DISPLAYXTWIPS",				HALData::EDisplayXTwips},
		{L"DISPLAYYTWIPS",				HALData::EDisplayYTwips},
		{L"DISPLAYCOLORS",				HALData::EDisplayColors},
		//{L"DisplayState",				HALData::EDisplayState},
		//{L"DisplayContrast",			HALData::EDisplayContrast},
		{L"DISPLAYCONTRASTMAX",			HALData::EDisplayContrastMax},
		{L"BACKLIGHT",					HALData::EBacklight},
		//{L"BacklightState",			HALData::EBacklightState},
		{L"PEN",						HALData::EPen},
		{L"PENX",						HALData::EPenX},
		{L"PENY",						HALData::EPenY},
		{L"PENDISPLAYON",				HALData::EPenDisplayOn},
		{L"PENCLICK",					HALData::EPenClick},
		//{L"PenClickState",			HALData::EPenClickState},
		//{L"PenClickVolume",			HALData::EPenClickVolume},
		{L"PENCLICKVOLUMEMAX",			HALData::EPenClickVolumeMax},
		{L"MOUSE",						HALData::EMouse},
		{L"MOUSEX",						HALData::EMouseX},
		{L"MOUSEY",						HALData::EMouseY},
		//{L"MouseState",				HALData::EMouseState},
		//{L"MouseSpeed",				HALData::EMouseSpeed},
		//{L"MouseAcceleration",		HALData::EMouseAcceleration},
		{L"MOUSEBUTTONS",				HALData::EMouseButtons},
		//{L"MouseButtonState",			HALData::EMouseButtonState},
		//{L"CaseState",					HALData::ECaseState},
		{L"CASESWITCH",					HALData::ECaseSwitch},
		//{L"CaseSwitchDisplayOn",		HALData::ECaseSwitchDisplayOn},
		//{L"CaseSwitchDisplayOff",		HALData::ECaseSwitchDisplayOff},
		{L"LEDS",						HALData::ELEDs},
		//{L"LEDmask",					HALData::ELEDmask},
		{L"INTEGRATEDPHONE",			HALData::EIntegratedPhone},
		{L"DISPLAYBRIGHTNESS",			HALData::EDisplayBrightness},
		{L"DISPLAYBRIGHTNESSMAX",		HALData::EDisplayBrightnessMax},
		{L"KEYBOARDBACKLIGHTSTATE",		HALData::EKeyboardBacklightState},
		{L"ACCESSORYPOWER",				HALData::EAccessoryPower},
		{L"SYSTEMDRIVE",				HALData::ESystemDrive},
		
		{L"FPHARDWARE",					HALData::EHardwareFloatingPoint},		
		{L"NUMHALATTRIBUTES",			HALData::ENumHalAttributes},

		{LANGUAGE_VARIABLE,				EVarLanguage},
		{L"REMOTEINSTALL",				EVarRemoteInstall},

		{NULL,							0}
	};


struct TExpressionFeatures
	{
	bool			iLeftNeeded;
	bool			iRightNeeded;
	bool			iStringNeeded;
	bool			iNumberNeeded;
	const wchar_t*	iName;
	};

static TExpressionFeatures expressionFeatures[] =
	{
		// Left	Right	String	Number	Name
		{true,	true,	false,	false,	L"="		},	 //EBinOpEqual
		{true,	true,	false,	false,	L"<>"		},	//EBinOpNotEqual
		{true,	true,	false,	false,	L">"		},	//EBinOpGreaterThan
		{true,	true,	false,	false,	L"<"		},	//EBinOpLessThan
		{true,	true,	false,	false,	L">="		},	//EBinOpGreaterThanOrEqual
		{true,	true,	false,	false,	L"<="		},	//EBinOpLessThanOrEqual
		{true,	true,	false,	false,	L"AND"		},	//ELogOpAnd
		{true,	true,	false,	false,	L"OR"		},	//ELogOpOr
		{false,	true,	false,	false,	L"NOT"		},	//EUnaryOpNot
		{false,	false,	true,	false,	L"exists"	},	//EFuncExists
		{true,	true,	false,	false,	L"appprop"	},	//EFuncAppProperties
		{false,	true,	false,	false,	L"package"	},	//EFuncDevProperties
		{false,	false,	true,	false,	L""			},	//EPrimTypeString
		{false,	false,	false,	true,	L"option"	},	//EPrimTypeOption
		{false,	false,	false,	true,	L""			},	//EPrimTypeVariable
		{false,	false,	false,	true,	L""			}	//EPrimTypeNumber
	};

TExpressionFeatures* findExpressionFeature (TUint32 aOperator)
	{
	if(aOperator <= CSISExpression::EOpNone ||aOperator >= CSISExpression::EOpUnknown)
		{
		return NULL;
		}
	return &expressionFeatures[aOperator-1];
	}



void CSISExpression::InsertMembers ()
	{
	InsertMember (iOperator);
	InsertMember (iInteger);
	InsertMember (iString);
	InsertMember (iLeaf);
	}


CSISExpression::CSISExpression (const CSISExpression& aExpression) :
		CStructure <CSISFieldRoot::ESISExpression> (aExpression),
		iOperator (aExpression.iOperator),
		iInteger (aExpression.iInteger),
		iString (aExpression.iString),
		iLeaf (aExpression.iLeaf)					
	{ 
	InsertMembers (); 
	}


void CSISExpression::Verify (const TUint32 aLanguages) const
	{
	CStructure <CSISFieldRoot::ESISExpression>::Verify (aLanguages);
	assert (iLeaf.size () < 3);
	CSISException::ThrowIf (	static_cast <TOperator> (iOperator.Value ()) > EOpUnknown,
								CSISException::EVerification,
								"illegal or unknown operator");
	}


CSISExpression& CSISExpression::operator = (const CSISExpression& aExpression)
	{
	iOperator = aExpression.iOperator;
	iInteger = aExpression.iInteger;
	iString = aExpression.iString;
	iLeaf.clear ();
	iLeaf.assign (aExpression.iLeaf.begin (), aExpression.iLeaf.end ());
	return *this;
	}


void CSISExpression::SetOperator (const TOperator aOperator, const CSISExpression& aLHS)	
	{
	switch (aOperator)
		{
	case EBinOpEqual :
	case EBinOpNotEqual :
	case EBinOpGreaterThan :
	case EBinOpLessThan :
	case EBinOpGreaterThanOrEqual :
	case EBinOpLessThanOrEqual :
	case ELogOpAnd :
	case ELogOpOr :
	case EFuncAppProperties :
		{
		AddLeaf (aLHS);
		AddLeaf ();
		break;
		}

	case EUnaryOpNot :
	case EFuncDevProperties :
		{
		AddLeaf (aLHS);
		break;
		}

	case EPrimTypeString :
	case EFuncExists :
		{
		iString.SetRequired(true);
		break;
		}

	case EPrimTypeVariable :
	case EPrimTypeOption :
	case EPrimTypeNumber :
		{
		break;
		}

	default :
		{
		assert (false);	
		}
		}
	iOperator = aOperator; 
	assert (iLeaf.size () < 3);
	}


void CSISExpression::SetLanguageComparision (const TInt32 aValue)
	{
	AddLeaf ();
	AddLeaf ();
	LHS ().SetOperator (EPrimTypeVariable);
	LHS ().SetLanguage ();
	RHS ().SetOperator (EPrimTypeNumber);
	RHS ().SetNumeric (aValue);
	iOperator =	EBinOpEqual;
	}


void CSISExpression::SetLanguage ()
	{
	iOperator =	EPrimTypeVariable;
	iInteger = EVarLanguage;
	}


void CSISExpression::SetVariable (const std::wstring& aIdentifier)
	{
	SetOperator (CSISExpression::EPrimTypeVariable);
	SetValue (static_cast <TUint32> (IdentifyUCKeyword (KVariables, aIdentifier, L"unknown variable")));
	}

void CSISExpression::AddPackageEntry(std::wostream& aStream, bool aVerbose) const
	{
	TExpressionFeatures* features = findExpressionFeature (iOperator);
	if(NULL == features)
		{
		aStream << L"UNKNOWN";
		return;
		}
	
	if (features->iLeftNeeded)
		{
		aStream << L"(";
		if(iOperator.Value() == EFuncExists)
			{
			aStream << L"\"";
			}

		LHS().AddPackageEntry(aStream, aVerbose);
		if(iOperator.Value() == EFuncExists)
			{
			aStream << L"\"";
			}
		aStream << L")";
		}
	
	std::wstring versionStr = iString.GetString().substr(0,KFuncVersionPrefix.length());
	std::wstring supportedLanguageStr = iString.GetString().substr(0,KFuncSupportedLanguagePrefix.length());
	if((iOperator.Value() == EFuncExists) && (versionStr == KFuncVersionPrefix))
		{
		WriteVersionCondition (aStream, aVerbose);
		}
	else if ((iOperator.Value() == EFuncExists) && (supportedLanguageStr == KFuncSupportedLanguagePrefix))
		{
		WriteSupportedLanguageCondition (aStream, aVerbose);
		}
	else
		{	
		aStream << features->iName;
		}
	
	if (features->iRightNeeded)
		{
		aStream << L"(";
		if(iOperator.Value() == EFuncExists)
			{
			aStream << L"\"";
			}

		RHS().AddPackageEntry(aStream, aVerbose);
		if(iOperator.Value() == EFuncExists)
			{
			aStream << L"\"";
			}
		aStream << L")";
		}

	if (features->iStringNeeded)
		{
		if(((iOperator.Value() == EFuncExists) && (versionStr != KFuncVersionPrefix)) && ((iOperator.Value() == EFuncExists) && (supportedLanguageStr != KFuncSupportedLanguagePrefix)))
			{
			aStream << L"(";

			if(iOperator.Value() == EFuncExists)
				{
				aStream << L"\"";
				}
			
			iString.AddPackageEntry(aStream, aVerbose);
			if(iOperator.Value() == EFuncExists)
				{
				aStream << L"\"";
				}
			aStream << L")";
			}
		}
	
	if (features->iNumberNeeded)
		{
		if(iOperator.Value() == EPrimTypeVariable)
			{
			if(iInteger.Value() == EVarLanguage)
				{
				aStream << L"LANGUAGE";
				}
			else
				{
				for (int i=0; KVariables[i].iName != NULL; ++i)
					{
					if (KVariables[i].iId == iInteger)
						{
						aStream << KVariables[i].iName;
						}
					}
				}
			}
		else
			{
			aStream << iInteger;
			}
		}
	}

void CSISExpression::WriteVersionCondition(std::basic_ostream<wchar_t>& aStream, bool aVerbose) const
	{
	std::wstring parseString = iString.GetString().substr(KFuncVersionPrefix.length());
	std::wstring outputArgs;

	try
		{
		// *** Parse Package UID Argument ***
		std::wstring packageUidStr;
		if(!ExtractNextToken(parseString,packageUidStr))
			{
			throw "Failed to Parse Package UID";
			}
		
		// Package UID format checking
		packageUidStr[1] = tolower(packageUidStr[1]);
		if(packageUidStr.find(L"0x") != 0 || packageUidStr.length() != 10)
			{
			throw "Invalid Package UID Format";
			}

		// Check that the UID string can be converted to Hexadecimal
		if(!IsHexadecimal(packageUidStr.substr(2)))
			{
			throw "Package UID contains non hexadecimal characters";
			}

		// Update Condition Output 
		outputArgs.append(packageUidStr);
		outputArgs.append(L",");
		
		
		// *** Parse Relational Operator ***
		std::wstring relationOpStr;
		if(!ExtractNextToken(parseString,relationOpStr))
			{
			throw "Failed to Parse the Relational Operator";
			}

		// Update the condition argument string as necessary depending on the operator code
		if(relationOpStr == L"ET") 
			{
			outputArgs.append(L"=");
			}
		else if(relationOpStr == L"LT")
			{
			outputArgs.append(L"<");
			}
		else if(relationOpStr == L"LE") 
			{
			outputArgs.append(L"<=");
			}
		else if(relationOpStr == L"GT")
			{
			outputArgs.append(L">");
			}
		else if(relationOpStr == L"GE") 
			{
			outputArgs.append(L">=");
			}
		else if(relationOpStr == L"NE")
			{
			outputArgs.append(L"<>");
			}
		else 
			{
			// If the operator is not recognised, throw an exception
			throw "Invalid Relational Operator";
			}

		// Update Condition Output
		outputArgs.append(L",");


		// *** Parse Major Version Component ***
		std::wstring vMajorStr;
		if(!ExtractNextToken(parseString,vMajorStr))
			{
			throw "Failed to parse the MAJOR version component";
			}

		// Check and convert the wstring to an integer
		TInt vMajor;
		if(!IsDecimal(vMajorStr,vMajor))
			{
			throw "MAJOR version component contains non numeric characters (0-9)";
			}

		// Check the Major component value lies within range
		if(vMajor < 0 || vMajor > 127)
			{
			throw "MAJOR version component out of range (0 - 127)";
			}

		// Update Condition Output
		outputArgs.append(vMajorStr);
		outputArgs.append(L",");


		// *** Parse Minor Version Component ***
		std::wstring vMinorStr;
		if(!ExtractNextToken(parseString,vMinorStr))
			{
			throw "Failed to parse the MINOR version component";
			}

		// Check and convert the wstring to an integer
		TInt vMinor;
		if(!IsDecimal(vMinorStr,vMinor))
			{
			throw "MINOR version component contains non numeric characters (0-9)";
			}
		
		// Check the Minor component value lies within range
		if(vMinor < 0 || vMinor > 99)
			{
			throw "MINOR version component out of range (0 - 99)";
			}

		// Update Condition Output
		outputArgs.append(vMinorStr);
		outputArgs.append(L",");


		// *** Parse Build Version Component ***
		std::wstring vBuildStr(parseString); 

		// Check and convert the wstring to an integer
		TInt vBuild;
		if(!IsDecimal(vBuildStr,vBuild))
			{
			throw "BUILD version component contains non numeric characters (0-9)";
			}

		// Check the Build component value lies within range
		if(vBuild < 0 || vBuild > 32767)
			{
			throw "BUILD version component out of range (0 - 32767)";
			}

		// Update Condition Output
		outputArgs.append(vBuildStr);

		// Output the successfully parsed version condition to the stream
		aStream << L"version(";
		aStream << outputArgs;
		aStream << L")";
		}
	catch(const char* aWarnMsg) 
		{
		// Convert Char* into a wstring 
		TInt bufferLength = strlen(aWarnMsg);
		wchar_t* buffer = new wchar_t[bufferLength+1];

		mbstowcs(buffer,aWarnMsg,bufferLength+1);
		std::wstring msgString(buffer);
		delete buffer;

		// Output the condition as an exists statement and comment warnings to the stream
		aStream << L"exists(\"";
		iString.AddPackageEntry(aStream, aVerbose);
		aStream << L"\")" << std::endl;
		aStream << L"; warning: \"VERSION\" condition output as \"EXISTS\"" << std::endl;
		aStream << L"; " << msgString;
		}
	}

bool CSISExpression::ExtractNextToken(std::wstring& aParseString, std::wstring& aTokenString)
	{
	TInt separatorPosition = aParseString.find(L",");

	// Check that a separator was located within the parse string
	if(separatorPosition == -1 || separatorPosition > aParseString.length()-1)
		{
		return false;
		}

	// Set the extracted token string and remove the token from the parse string
	aTokenString = aParseString.substr(0,separatorPosition);
	aParseString = aParseString.substr(separatorPosition + 1);

	return true;
	}

bool CSISExpression::IsHexadecimal(const std::wstring& aString)
	{
	TUint32 i;
	return IsHexadecimal(aString,i);
	}

bool CSISExpression::IsHexadecimal(const std::wstring& aString, TUint32& aHexValue)
	{
	if(aString.size() == 0)
		{
		return false;
		}

	std::wistringstream wStrStream(aString);

	if((wStrStream >> std::hex >> aHexValue) && wStrStream.eof())
		{
		return true;
		}

	return false;
	} 

bool CSISExpression::IsDecimal(const std::wstring& aString, TInt& aDecimalValue)
	{
	if(aString.size() == 0)
		{
		return false;
		}

	std::wistringstream wStrStream(aString);

	if((wStrStream >> std::dec >> aDecimalValue) && wStrStream.eof())
		{
		return true;
		}

	return false;
	}

void CSISExpression::WriteSupportedLanguageCondition(std::basic_ostream<wchar_t>& aStream, bool aVerbose) const
	{
	std::wstring parseString = iString.GetString().substr(KFuncSupportedLanguagePrefix.length());
	std::wstring outputArgs;
	try
		{
		// Check and convert the wstring to an integer
		TInt vLanguageId;
		if(!IsDecimal(parseString,vLanguageId))
			{
			throw "Supported_Language option contains non-numeric value";
			}
		// Output the successfully parsed version condition to the stream
		aStream << L"Supported_Language = ";
		aStream << vLanguageId;

		}
	catch(const char* aWarnMsg) 
		{
		// Convert Char* into a wstring 
		TInt bufferLength = strlen(aWarnMsg);
		wchar_t* buffer = new wchar_t[bufferLength+1];

		mbstowcs(buffer,aWarnMsg,bufferLength+1);
		std::wstring msgString(buffer);
		delete buffer;

		// Output the condition as an exists statement and comment warnings to the stream
		aStream << L"exists(\"";
		iString.AddPackageEntry(aStream, aVerbose);
		aStream << L"\")" << std::endl;
		aStream << L"; warning: \"Supported_Language\" condition output as \"EXISTS\"" << std::endl;
		aStream << L"; " << msgString;
		}
	}

