/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* 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:
*   This module contains the implementation of Util class member functions. 
*   Util class provides static utility functions for logging and string
*	manipulation.
*/

// INCLUDE FILES
#include "util.h"

// command line parameters
extern map<string, string> g_parameters;

//**********************************************************************************
// Class Util
//
// Provides static utility functions for logging and string handling.
//**********************************************************************************

// The default verbose level for Util output is error
Util::VerboseLevel Util::iVerbose(Util::error);

// The default output is without timestamp
int Util::iTimestamp(0);

#ifdef ENABLE_LOG_SYNC 
	Mutex Util::iCritical;
#endif

/*-------------------------------------------------------------------------------

    Class: Util

    Method: SetVerboseLevel

    Description: Sets new verbose level by string:
	           
				 "none"
				 "result"
				 "error"
				 "info"
				 "debug"
    
    Parameters: const string& aVerbose : in: Verbose level as string

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::SetVerboseLevel(const string& aVerbose)
{
	string verbose(aVerbose);
	ToLower(verbose);
	if (verbose.compare("result") == 0)
	{
		iVerbose = result;
	}
	else if (verbose.compare("error") == 0)
	{
		iVerbose = error;
	}
	else if (verbose.compare("info") == 0)
	{
		iVerbose = info;
	}
	else if (verbose.compare("debug") == 0)
	{
		iVerbose = debug;
	}
	else
	{
	 	iVerbose = none;
	}
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: SetVerboseLevel

    Description: Sets new verbose level by enumeration.
    
    Parameters: const VerboseLevel aVerbose : in: Verbose level

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::SetVerboseLevel(const VerboseLevel aVerbose)
{
	iVerbose = aVerbose;
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: GetVerboseLevel

    Description: Returns current verbose level.
    
    Parameters: None

    Return Values: VerboseLevel : Current verbose level

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
Util::VerboseLevel Util::GetVerboseLevel()
{
	return iVerbose;
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: SetTimestamp

    Description: Enables/disables timestamp from output by string "yes" or "no".
    
    Parameters: const string& aTimestamp : in : "yes" to enable timestamp, "no" to
	                                            disable.

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::SetTimestamp(const string& aTimestamp)
{
	string timestamp(aTimestamp);
	ToLower(timestamp);
	if (timestamp.compare("true") == 0)
	{
		SetTimestamp(1);
	}
	else
	{
		SetTimestamp(0);
	}
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: SetTimestamp

    Description: Enables/disables timestamp from output. 0 is disabled.
    
    Parameters: const int aTimestamp : in : Enable/disable time stamp

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::SetTimestamp(const int aTimestamp)
{
	iTimestamp = aTimestamp;
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: ParseCmdLine

    Description: Parses command line parameters to map. The map which is passed
	             as argument must contain all valid keys (command line swicthes).
				 Function adds values to map to all keys found from command line.
				 The command line switch is form <key>=<value>. Default values for
				 keys can be specified.
    
    Parameters: const int aArgc           : in     : The number of arguments
	            char **aArgs              : in     : Pointer array to arguments
				map<string, string>& aMap : in/out : Map containing possible command
				                                     line switches (keys). Values of
													 switches are returned as values.

    Return Values: None

    Errors/Exceptions: UtilError

-------------------------------------------------------------------------------*/
void Util::ParseCmdLine(const int aArgc,
						char **aArgs,
						map<string, string>& aMap) throw (UtilError)
{
	for (int i = 1; i < aArgc; i++)
	{
		string p(aArgs[i]);
		string v;
		int idx = p.find_first_of("=");
		if (idx != string::npos)
		{
			if ((idx + 1) == p.length())
			{
				string err("Parameter '" + p.substr(0, idx) + "' without value");
				throw UtilError(err, ERR_UTIL_NO_PARAM_VALUE);
			}
			v = &aArgs[i][idx + 1];
			p = p.substr(0, idx);
		}
		else
		{
			v = "";
		}

		// 28.03.06 Disabled this "check" to enable parameter passing to plug-ins also
		//string def;
		//GetValue(p, aMap, def);
		aMap[p] = v;
	}
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: GetValue

    Description: Returns value by key from a map.
    
    Parameters: const string& aKey        : in  : Key to value 
				map<string, string>& aMap : in  : Map containing valid keys
				string& aValue            : out : Value of key

    Return Values: None

    Errors/Exceptions: UtilError

-------------------------------------------------------------------------------*/
void Util::GetValue(const string& aKey,
					const map<string,
					string>& aMap, string& aValue) throw (UtilError)
{
	map<string, string>::const_iterator i = aMap.find(aKey);
	if (i != aMap.end())
	{
		aValue = i->second;
	}
	else
	{
		string err("Unknown parameter '" + aKey + "'");
		throw UtilError(err, ERR_UTIL_UNKNOWN_PARAM);
	}
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: ReadProperties

    Description: Reads properties from a file to a map. Line beginning with
	             hash character ('#') are considered as comments. The form
				 of prooerties in file is <property> = <value>.
    
    Parameters: const string& aFilename   : in  : Filename where properties are read
	            map<string, string>& aMap : out : Map where property values are put

    Return Values: None

    Errors/Exceptions: UtilError

-------------------------------------------------------------------------------*/
void Util::ReadProperties(const string& aFilename,
						  map<string, string>& aMap) throw (UtilError)
{
	ifstream in(aFilename.c_str());
	if (!in)
	{
		string err("Cannot open properties file '");
		err += aFilename + "'.";
		throw UtilError(err, ERR_UTIL_PROPERTIES_NOT_FOUND);
	}
	while (in)
	{
		char tmp[256];
		in.getline(tmp, 256);
		string line(tmp);
		if (line.length() == 0 || line.find_first_of("#") == 0)
		{
			continue;
		}
		int idx = line.find_first_of('=');
		string v, p;
		if (idx != string::npos)
		{
			p = line.substr(0, idx);
			int r = p.find(" ");
			while (r != string::npos)
			{
				p.replace(r, 1, "");
				r = p.find(" ");
			}
			v = line.substr(idx + 1);
			string::iterator i = v.begin();
			int spaces = 0;
			while (i != v.end() && *i == ' ')
			{
				++spaces;
				++i;
			}
			v.erase(0, spaces);
			if (v.length() == 0)
			{
				string err = "Value not specified for parameter '";
				err += line.substr(0, idx) + "'.";
				throw UtilError(err, ERR_UTIL_NO_PROPERTY_VALUE);			
			}
		}
		else 
		{
			p = line;
			v = "";
		}
		aMap[p] = v;
	}
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Error

    Description: Prints message to output in error level.
    
    Parameters: const string& aMsg : in : Error message to output

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Error(const string& aMsg)
{
	Print(cerr, aMsg, error);
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Error

    Description: Prints message to output in error level with error code number.
    
    Parameters: const string& aMsg : in : Error message to output
				const long aCode   : in : Error code to output

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Error(const string& aMsg, const long aCode)
{
	Print(cerr, aMsg, error, aCode);
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Log

    Description: Prints message to output in result level.
    
    Parameters: const string& aMsg : in : Result message to output

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Log(const string& aMsg)
{
	Print(cout, aMsg, result);
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Info

    Description: Prints message to output in info level.
    
    Parameters: const string& aMsg : in : Info message to output

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Info(const string& aMsg)
{
	Print(cout, aMsg, info);
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Debug

    Description: Prints message to output in debug level.
    
    Parameters: const string& aMsg : in : Debug message to output

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Debug(const string& aMsg)
{
	Print(cout, aMsg, debug);
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Print

    Description: Prints output to a stream.
    
    Parameters: ostream& out              : in : Stream where to write
                const string& aMsg        : in : Message to output
                const VerboseLevel aLevel : in : Verbose level of message
				const long aCode          : in : Possible error code if != 0

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Print(ostream& out,
				 const string& aMsg,
				 const VerboseLevel aLevel,
				 const long aCode)
{
#ifdef ENABLE_LOG_SYNC 
	iCritical.Lock();
#endif

	if (aLevel <= iVerbose)
	{
		if (iTimestamp)
		{
			char timestamp[128];
			_strdate(timestamp);
			char tmp[16];
			_strtime(tmp);
			strcat(timestamp, " ");
			strcat(timestamp, tmp);
			struct _timeb tb;
			_ftime(&tb);
			sprintf(tmp, ".%03d", tb.millitm);
			strcat(timestamp, tmp);
			out << "[" << timestamp << "] ";
		}
		//out << "[" << GetCurrentThreadId() << "] ";
		out << aMsg;
		//if (aLevel == debug)
		{
			OutputDebugString(aMsg.c_str());
			OutputDebugString("\r\n");
		}
		if (aCode != 0)
		{
			out.setf(ios_base::hex, ios_base::basefield);
			out << " (error: 0x" << aCode << ")";
		}
		out << endl;
	}
#ifdef ENABLE_LOG_SYNC 
	iCritical.Unlock();
#endif
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: ToLower

    Description: Converts string to lower case.
    
    Parameters: string& aString : in/out : String which is converted to lower case

    Return Values: string& : Reference to converted string.

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
string& Util::ToLower(string& aString)
{
	string::iterator p = aString.begin();
	while (p != aString.end())
	{
		*p = tolower(*p);
		++p;
	}
	return aString;
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: ToUpper

    Description: Converts string to upper case.
    
    Parameters: string& aString : in/out : String which is converted to upper case

    Return Values: string& : Reference to converted string.

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
string& Util::ToUpper(string& aString)
{
	string::iterator p = aString.begin();
	while (p != aString.end())
	{
		*p = toupper(*p);
		++p;
	}
	return aString;
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Hex

    Description: Prints hex dump of char table to output.
    
    Parameters: const char aData[] : in : Data to be output as hex dump

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Hex(const char aData[])
{
	string s(aData);
	Hex(s);
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Hex

    Description: Prints hex dump of string to output.
    
    Parameters: const string& aMsg : in : Data to be output as hex dump

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Hex(const string& aMsg)
{
	Hex((const unsigned char *)aMsg.c_str(), aMsg.length());
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Hex

    Description: Prints hex dump of data to output.
    
    Parameters: const unsigned char* aData : in : Pointer to data to be output
	                                              as hex dump
                const int aLength          : in : The lenght of data

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Hex(const unsigned char* aData, const int aLength)
{
	Hex((void *)aData, aLength);
}

/*-------------------------------------------------------------------------------

    Class: Util

    Method: Hex

    Description: Prints hex dump of data to output.
    
    Parameters: void *aData       : in : Pointer to data to be output as hex dump
                const int aLength : in : The lenght of data

    Return Values: None

    Errors/Exceptions: None

-------------------------------------------------------------------------------*/
void Util::Hex(void *aData, const int aLength)
{
	Hex(aData, aLength, NULL);
}

void Util::Hex(void* aData, const int aLength, string* aHexDump)
{
#ifdef ENABLE_LOG_SYNC 
	iCritical.Lock();
#endif

	char line[16];
	int line_len = 16;
	int bytes_to_copy = 0;
	int printed = 0;
	bool print_out = (aHexDump == NULL ? true : false);
	unsigned char *data = (unsigned char *)aData;

	if (print_out)
	{
		printf("\n");
	}
	else
	{
		aHexDump->append("\r\n");
	}
	while (printed < aLength)
	{
		bytes_to_copy = ((aLength - printed >= line_len ? line_len : (aLength - printed)));
		memset(line, 0, sizeof(line));
		memcpy(line, &data[printed], bytes_to_copy);
		for (int j = 0; j < line_len; j++)
		{
			char hex[4];
			sprintf(hex, "%02X ", (unsigned char)line[j]);
			if (print_out)
			{
				printf("%s", hex);
			}
			else
			{
				string s(hex);
				aHexDump->append(s);
			}
		}
		if (print_out) printf(" | ");
		for (int j = 0; j < line_len; j++)
		{
			char c = line[j];
			if (c >= ' ' && c <= 'z')
			{
				if (print_out)
				{
					printf("%c", c);
				}
				else
				{
					char tmp[2];
					sprintf(tmp, "%c", c);
					string s(tmp);
					aHexDump->append(s);
				}
			} 
			else
			{
				if (print_out)
				{
					printf(".");
				}
				else
				{
					aHexDump->append(".");
				}
			}
		}
		if (print_out)
		{
			printf("\n");
		}
		else
		{
			aHexDump->append("\r\n");
		}
		if ((printed - line_len) < aLength)
		{
			printed += 16;
		}
		else
		{
			printed = aLength - printed;
		}
	}
	if (print_out) printf("\n");

#ifdef ENABLE_LOG_SYNC 
	iCritical.Unlock();
#endif
}

void Util::CheckCommandlineParam( const string& paramname, string& paramvalue )
{
	// Command line parameter overrides ini-file value
	if ( !g_parameters[paramname].empty() )
		paramvalue = g_parameters[paramname];
}

// End of the file