/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.16 $
 *********************************************************************/

#include "Util.h"

#include "String.h"
#include "StreamTokenizer.h"
#include <ctype.h>
#include <strings.h>
#include "xml/XMLParser.h"
#include "SmartPtr.h"

using namespace std;
using namespace util::io;

namespace util
{
	char* Util::getData(const string& str) { return String::getData(str); }
	string Util::substitute(const string& str, Map<string,string>& variables) { return String::substitute(str,variables); }
	int Util::parseInt(const string& str) { return String::parseInt(str); }
	double Util::parseDouble(const string& str) { return String::parseDouble(str); }

	template <> bool* Util::copyArray(const bool* from, bool* to, int len) { return (bool*)memcpy(to,from,len*sizeof(bool)); }

	template <> char* Util::copyArray(const char* from, char* to, int len) { return (char*)memcpy(to,from,len); }
	template <> short* Util::copyArray(const short* from, short* to, int len) { return (short*)memcpy(to,from,len*sizeof(short)); }
	template <> int* Util::copyArray(const int* from, int* to, int len) { return (int*)memcpy(to,from,len*sizeof(int)); }
	template <> long* Util::copyArray(const long* from, long* to, int len) { return (long*)memcpy(to,from,len*sizeof(long)); }

	template <> unsigned char* Util::copyArray(const unsigned char* from, unsigned char* to, int len)
	{ return (unsigned char*)memcpy(to,from,len); }
	template <> unsigned short* Util::copyArray(const unsigned short* from, unsigned short* to, int len)
	{ return (unsigned short*)memcpy(to,from,len*sizeof(unsigned short)); }
	template <> unsigned int* Util::copyArray(const unsigned int* from, unsigned int* to, int len)
	{ return (unsigned int*)memcpy(to,from,len*sizeof(unsigned int)); }
	template <> unsigned long* Util::copyArray(const unsigned long* from, unsigned long* to, int len)
	{ return (unsigned long*)memcpy(to,from,len*sizeof(unsigned long)); }

	template <> float* Util::copyArray(const float* from, float* to, int len) { return (float*)memcpy(to,from,len*sizeof(float)); }
	template <> double* Util::copyArray(const double* from, double* to, int len) { return (double*)memcpy(to,from,len*sizeof(double)); }

	string Util::stripTemplateParameters(const string& className)
	{
		string result;
		int openCount = 0;
		for (unsigned int i=0;i<className.size();i++)
			{
				char chr = className[i];
				if (chr == '<')
					openCount++;
				else if (chr == '>')
					openCount--;
				else if (!openCount)
					result += chr;
			}
		return result;
	}
	
	string Util::parseName(const char* name)
	{
		int len;
		return parseName(name,len);
	}
	
	//PENDING: this method does not work with nestings deeper than 9 levels!
	string Util::parseName(const char* name, int& len)
	{
		const char* start = name;
		len = 0;
		string result;
		if (name == NULL)
			return result;

		const char shortNames[] = { 'v','c','s','i','l','x','f','d','r','b' };
		string wholeNames[] = {
			"void",
			"char",
			"short",
			"int",
			"long",
			"long long",
			"float",
			"double",
			"long double",
			"bool"
		};

		string ending;
		int partCount = 1;
		while (true)
			{
				long multiplier = 1L;
				string strArraySize;
				switch (*name)
					{
					case 'P':
						ending = "*" + ending;
						break;
					case 'A':
						name++;
						if (*name == 'm')
							{
								multiplier = -1L;
								name++;
							}
						strArraySize = String::readInteger(name);
						name += strArraySize.size();
						ending += "[" + String::toString(multiplier*String::parseLong(strArraySize)+1L) + "]";
						break;
					case 'C':
						result += "const ";
						break;
					case 'V':
						result += "volatile ";
						break;
					case 'U':
						result += "unsigned ";
						break;
					case 'S':
						result += "signed ";
						break;
					case 'Q':
						//Starts a multi-part definition.
						name++;
						len = int(name - start);
						if (!name) return result;
						partCount = *name - '0';
						name++;
						goto out;
					case 0:
						return result;
					default:
						//Only one part which starts with the length.
						if (isdigit(*name))
							goto out;

						//Elementary data types
						int i;
						for (i=10;i--;)
							{
								if (*name == shortNames[i])
									{
										result += wholeNames[i];
										result += ending;
										len = int(name - start)+1;
										return result;
									}
							}
						if (i<0)
							{
								//cerr << *name << "? Unknown type." << endl;
								return string(start);
							}
					}
				name++;
			}
	out:;
		
		for (int i=0;i<partCount;i++)
			{
				bool isTemplate = false;
				if (*name == 't')
					{
						isTemplate = true;
						name++;
					}
				string strMarkCount = String::readInteger(name);
				name += strMarkCount.size();
				int markCount = String::parseInt(strMarkCount);
				len = int(name - start);
				if (!markCount) return result;
				if (i) result += "::";
				int j;
				for (j=0;j<markCount && *name;j++,name++)
					result += *name;
				if (j<markCount)
					return result;

				//Read template parameters
				if (isTemplate)
					{
						string strParamCount = String::readInteger(name);
						name += strParamCount.size();
						int paramCount = String::parseInt(strParamCount);

						len = int(name - start);
						if (!paramCount) return result;
						result += '<';
						for (int i=0;i<paramCount && *name;i++)
							{
								if (i) result += ',';
								bool readValue = (*name != 'Z');
								if (!readValue)
									name++;
								int length;
								string strName = parseName(name,length);
								//cerr << "Parsed template parameter: " << strName << ", length: " << length << endl;
								name += length;
								if (!readValue)
									result += strName;
								else
									{
										long multiplier = 1L;
										if (*name == 'm')
											{
												multiplier = -1L;
												name++;
											}
										string strValue = String::readInteger(name);
										//cerr << "Read value: " << strValue << ", length: " << strValue.size() << endl;
										name += strValue.size();
										long value = multiplier * String::parseLong(strValue);
										if (!strName.compare("bool"))
											result += value? "true" : "false";
										else
											result += String::toString(value);
									}
							}
						result += ">";
					}
			}
		len = int(name - start);
		result += ending;
		return result;
	}

	template <> void Util::writeItem(ostream& sout, const string& item) throw (IOException&)
	{
		sout << "\"" << String::backslashify(item) << "\"";
	}

	template <> void Util::readItem(istream& sin, string& item) throw (IOException&)
	{
		StreamTokenizer reader;
		reader.attribute(':') = TOKEN_WORD;
		SmartPtr<StreamTokenizer::Token> token(reader.getNextToken(sin));
		if (token->getType() != TOKEN_SECTION ||
				((StreamTokenizer::SectionToken*)token.get())->getName() != "string")
			throw IOException("Util::readItem(istream&, string&): Expecting a string section (\"...\").");
		item = ((StreamTokenizer::SectionToken*)token.get())->getContents();
	}

	template <> void Util::writeXMLItem(ostream& sout, const string& item) throw (IOException&,xml::XMLException&)
	{
		sout << "<str>" << String::addXMLEntities(item) << "</str>";
	}

	template <> void Util::readXMLItem(istream& sin, string& item) throw (IOException&,xml::XMLException&)
	{
		using namespace util::xml;
		XMLParser parser;
		sin >> ws;
		SmartPtr<Node> str(parser.readFragment(sin));
		if (str.get())
			{
				const Node* text = str->getChildNode("str.text");
				if (text && text->getNodeType() == Node::TEXT_NODE)
					{
						item = ((const Text*)text)->contents;
						return;
					}
				else if (str->getNodeType() == Node::ELEMENT_NODE &&
								 ((Element*)str.get())->tagType != Element::TAG_CLOSING &&
								 ((Element*)str.get())->tagName == "str")
					{
						item = "";
						return;
					}
			}
		throw IOException("readXMLItem(istream&,string&): input stream does not contain a properly formatted XML string.");
	}

	template <> void Util::readXMLItem(istream& sin, float& item) throw (IOException&,xml::XMLException&)
	{
		readItem(sin,item);
	}
	template <> void Util::readXMLItem(istream& sin, double& item) throw (IOException&,xml::XMLException&)
	{
		readItem(sin,item);
	}

	template <> void Util::readItem(istream& sin, double& item) throw (IOException&)
	{
		char chr;
		string number;

		bool separator = false, exponent = false;
		int index = 0;
		
		do
			{
				sin.read(&chr,1);
			} while (isspace(chr));
		
		while (sin)
			{
				if (sin &&
						(isdigit(chr) ||																		//if it is a digit ...
						 (chr == '.' && !separator) ||											//or the first decimal separator ...
						 ((chr == 'e' || chr == 'E') && !exponent) ||				//or the first 'e', starting an exponent ...
						 (chr == '-' && index == 0)))												//or a minus sign in the beginning of mantissa or exponent ...
					number += chr;																				//then accept
				else
					break;
				index++;

				switch (chr)
					{
					case 'e':
					case 'E':
						exponent = true;
						index = 0;
						break;
					case '.':
						separator = true;
						break;
					}

				sin.read(&chr,1);																				//read a character
			}

		//Put the last character back.
		if (sin)
			sin.putback(chr);

		if (!number.size())
			throw IOException("Util::readItem(istream&, double&): Not a valid floating-point number.");
		item = String::parseDouble(number);
	}

	template <> void Util::readItem(istream& sin, float& item) throw (IOException&)
	{
		double tmp;
		readItem(sin,tmp);
		item = (float)tmp;
	}
}
