/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää and Jaakko Viertola
 * 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.22 $
 *********************************************************************/

#ifndef _UTIL_H
#define _UTIL_H

namespace util { class Util; }

#include "List.h"
#include <string>
#include <iostream>
#include "io/IO.h"
#include <typeinfo>
#include "Map.h"
#include "xml/XMLException.h"

/**
 * Get the absolute value of a.
 **/
#define absolute(a)   ((a)>=0 ? (a) : -(a))
/**
 * Get the smaller value of a and b.
 **/
#define minimum(a,b)  ((a)<=(b) ? (a) : (b))
/**
 * Get the larger value of a and b.
 **/
#define maximum(a,b)  ((a)>=(b) ? (a) : (b))

namespace util
{
	/**
	 * A class that contains miscellaneous utility methods that do not
	 * fit anywhere else.
	 **/
	class Util
	{
	public:
		/**
		 * Get a newly allocated character array that contains a copy of
		 * the contents of <i>str</i> with a null at the end.
		 * @deprecated Use String::getData instead
		 * @see String::getData(const string&)
		 **/
		static char* getData(const std::string& str);
		/**
		 * Use the provided map of variable name - value pairs to replace
		 * all shell-syntax variables in <i>str</i>.
		 * @param str the string that has variables inserted
		 * @param variables the variables that are to be used
		 * @deprecated Use String::substitute instead
		 * @see String::substitute(const string&, Map<string,string>&)
		 **/
		static std::string substitute(const std::string& str, Map<std::string,std::string>& variables);
		/**
		 * Get an integer out of a string.
		 * @deprecated Use String::parseInt instead
		 * @see String::parseInt(const string&)
		 **/
		static int parseInt(const std::string& str);
		/**
		 * Get a double out of a string.
		 * @deprecated Use String::parseDouble instead
		 * @see String::parseDouble(const string&)
		 **/
		static double parseDouble(const std::string& str);

		/**
		 * Parse a class name as returned by type_info::name() to a readable
		 * format. A typical return from type_info::name() is of the form
		 * "Q24test7MyClass", which converts to a more readable
		 * "test::MyClass".
		 * @param a class name as returned by type_info::name()
		 * @return a formatted class name
		 **/
		static std::string parseName(const char* name);

		/**
		 * A handy template for getting the class name for an object.
		 * @param obj the object whose name is to be fetched. The class of
		 *        the object or at least one of its superclasses must contain
		 *        a virtual method. Otherwise there will be no type information
		 *        and the result is ambiguous.
		 * @return the class name formatted with parseName(const char*)
		 * @see #parseName(const char*)
		 **/
		template <class T> static std::string getClassName(const T& obj);

		/**
		 * Strip template paremeters from a class name. If the formatted
		 * name of a class is, say, mynamespace::Test<int>, the returned
		 * string says "mynamespace::Test".
		 **/
		static std::string stripTemplateParameters(const std::string& className);

		/**
		 * Write any type to a stream. An object (or elementary type)
		 * written to a stream using this method should be possible to
		 * read unambiguously using the readItem method. The
		 * write/readItem pair is needed because some types (like strings)
		 * cannot be read from a stream once they have been written.
		 * @param sout the output stream
		 * @param item the object to be written
		 **/
		template <class T> static void writeItem(std::ostream& sout, const T& item) throw (io::IOException&);
		/**
		 * Read an object written by writeItem.
		 **/
		template <class T> static void readItem(std::istream& sin, T& item) throw (io::IOException&);

		/**
		 * Write any type to a stream as XML. An object (or elementary
		 * type) written to a stream using this method should be possible
		 * to read unambiguously using the readXMLItem method. The
		 * write/readItem pair is needed because some types (like strings)
		 * cannot be read from a stream once they have been written.
		 * @param sout the output stream @param item the object to be
		 * written
		 **/
		template <class T> static void writeXMLItem(std::ostream& sout, const T& item) throw (io::IOException&,xml::XMLException&);
		/**
		 * Read an object written by writeXMLItem.
		 **/
		template <class T> static void readXMLItem(std::istream& sin, T& item) throw (io::IOException&,xml::XMLException&);

		/**
		 * Efficiently copy an array to another. This method uses direct
		 * memory transfers where possible.
		 **/
		template <class T> static inline T* copyArray(const T* from, T* to, int len);

	private:
		static std::string parseName(const char* name, int& len);
	};

	template <> bool* Util::copyArray(const bool* from, bool* to, int len);

	template <> char* Util::copyArray(const char* from, char* to, int len);
	template <> short* Util::copyArray(const short* from, short* to, int len);
	template <> int* Util::copyArray(const int* from, int* to, int len);
	template <> long* Util::copyArray(const long* from, long* to, int len);

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

	template <> float* Util::copyArray(const float* from, float* to, int len);
	template <> double* Util::copyArray(const double* from, double* to, int len);

	template <class T> T* Util::copyArray(const T* from, T* to, int len)
	{
		T* result = to;
		for (int i=len;i--;from++,to++) *to = *from;
		return result;
	}
	
	/**
	 * Write a string to a stream. The string is wrapped into double
	 * quotes and all special characters within it are replaced with C
	 * backslash notations.
	 **/
	template <> void Util::writeItem(std::ostream& sout, const std::string& item) throw (io::IOException&);
	/**
	 * Read a string from a stream. Backslashed characters are converted
	 * and the double quotes stripped.
	 **/
	template <> void Util::readItem(std::istream& sin, std::string& item) throw (io::IOException&);
	/**
	 * Read a decimal number from a stream. A floating-point number in
	 * the form 1.23e45 with optional minus signs in both mantissa and
	 * exponent is expected.
	 **/
	template <> void Util::readItem(std::istream& sin, double& item) throw (io::IOException&);
	/**
	 * Read a decimal number from a stream. A floating-point number in
	 * the form 1.23e45 with optional minus signs in both mantissa and
	 * exponent is expected.
	 **/
	template <> void Util::readItem(std::istream& sin, float& item) throw (io::IOException&);

	/**
	 * Write a string to a stream. The string is wrapped into
	 * &lt;str&gt; &lt;/str&gt; tags, and special characters are
	 * converted to XML entities.
	 **/
	template <> void Util::writeXMLItem(std::ostream& sout, const std::string& item) throw (io::IOException&,xml::XMLException&);
	/**
	 * Read a string from a stream. XML entities are converted to
	 * characters.
	 **/
	template <> void Util::readXMLItem(std::istream& sin, std::string& item) throw (io::IOException&,xml::XMLException&);
	/**
	 * Read a decimal number from a stream. A floating-point number in
	 * the form 1.23e45 with optional minus signs in both mantissa and
	 * exponent is expected.
	 **/
	template <> void Util::readXMLItem(std::istream& sin, double& item) throw (io::IOException&,xml::XMLException&);
	/**
	 * Read a decimal number from a stream. A floating-point number in
	 * the form 1.23e45 with optional minus signs in both mantissa and
	 * exponent is expected.
	 **/
	template <> void Util::readXMLItem(std::istream& sin, float& item) throw (io::IOException&,xml::XMLException&);

	template <class T> std::string Util::getClassName(const T& obj)
	{
		return parseName(typeid(obj).name());
	}
	

	template <class T> void Util::writeItem(std::ostream& sout, const T& item) throw (io::IOException&)
	{
		sout << item;
	}
	
	template <class T> void Util::readItem(std::istream& sin, T& item) throw (io::IOException&)
	{
		sin >> item;
	}
	
	template <class T> void Util::writeXMLItem(std::ostream& sout, const T& item) throw (io::IOException&,xml::XMLException&)
	{
		sout << item;
	}
	
	template <class T> void Util::readXMLItem(std::istream& sin, T& item) throw (io::IOException&,xml::XMLException&)
	{
		sin >> item;
	}	
}

#endif
