/*********************************************************************
 * 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.16 $
 *********************************************************************/

#ifndef _STRINGUTIL_H
#define _STRINGUTIL_H

namespace util { class String; }

#include "List.h"
#include "Computable.h"
#include <string>
#include "Map.h"
#include <sstream>
#include <values.h>

namespace util
{
	/**
	 * Contains static methods for many useful string handling needs.
	 * String instances can also be used interchangeably with
	 * std::string.
	 **/
	class String : virtual public Computable
	{
	private:
		std::string _str;
		
	public:
		/**
		 * Create an empty string.
		 **/
		String() {}
		/**
		 * Copy another String.
		 **/
		String(const String& other) : _str(other._str) {}
		/**
		 * Copy an std::string.
		 **/
		String(const std::string& str) : _str(str) {}
		/**
		 * Typecast to a std::string&. This method allows the use of a
		 * String instance where a std::string is expected.
		 **/
		operator std::string&() { return _str; }
		/**
		 * Copy a String.
		 **/
		String& operator= (const String& other) { _str = other._str; return *this; }
		/**
		 * Copy an std::string.
		 **/
		String& operator= (const std::string& str) { _str = str; return *this; }
		/**
		 * Convert a String to an std::string.
		 **/
		std::string toString() const { return _str; }
		/**
		 * Clone a String.
		 **/
		Object* clone() const throw (NotCloneableException&) { return new String(_str); }

		/**
		 * Perform an addition operation on a string.
		 **/
		SmartPtr<Computable> plus(const Computable& c, bool switched = false) const throw (ComputationException&);
		/**
		 * Perform a multiplication operation on a string.
		 **/
		SmartPtr<Computable> multiply(const Computable& c, bool switched = false) const throw (ComputationException&);
		
		/**
		 * Remove all trailing and leading white spaces from a string.
		 *
		 * @param str the string to be fixed
		 * @return a fixed string
		 **/
		static std::string fix(const std::string& str);
		/**
		 * Replace all white spaces with ' ' and change sequences of
		 * spaces to single (or any other number of) spaces.
		 *
		 * @param str the string to be shrinked
		 * @param count replace sequences of spaces with this many spaces
		 * @return a shrinked string
		 **/
		static std::string shrink(const std::string& str, int count=1);
		/**
		 * Convert newlines, carriage returns and other special characters
		 * to their C correspondents.
		 * @param str the string to be converted
		 * @return a new string with special characters backslashed
		 **/
		static std::string backslashify(const std::string& str);
		/**
		 * Convert all backslashed special characters to bytes.
		 * @param str the string from which backslashes are to be removed
		 * @return a new string with backslashes removed
		 **/
		static std::string unslashify(const std::string& str);
		/**
		 * Convert all occurences of &amp;, &lt;, &gt;, &quot; and &apos;
		 * to their corresponding XML entities (&amp;amp;, &amp;lt;,
		 * &amp;gt;, &amp;quot; and &amp;apos;).
		 *
		 * @param str the string to convert
		 * @return a converted string
		 **/
		static std::string addXMLEntities(const std::string& str);
		/**
		 * Remove all XML 1.0 standard entities by converting them to
		 * characters.
		 *
		 * @param str the string to convert
		 * @return a converted string
		 **/
		static std::string removeXMLEntities(const std::string& str);
		/**
		 * Replace all occurences of <i>from</i> within <i>str</i> with
		 * <i>to</i>.
		 * @param str the string that is searched for matches
		 * @param from what to look for
		 * @param to if a match is found, replace it with this
		 * @param n the maximum number of replacements allowed. If this
		 *        value is smaller than 1, all occurrences will be replaced.
		 **/
		static std::string replace(const std::string& str, const std::string& from, const std::string& to, int n=0);
		/**
		 * Replace all occurences of <i>from</i> within <i>str</i> with
		 * <i>to</i>. This method is identical to replace, but it starts
		 * from the end of a string.
		 **/
		static std::string reverseReplace(const std::string& str, const std::string& from, const std::string& to, int n=0);
		/**
		 * Get a string that contains the characters in <i>str</i> in
		 * reverse order.
		 **/
		static std::string reverse(const std::string& str);
		
		/**
		 * Get a newly allocated character array that contains a copy of
		 * the contents of <i>str</i> with a null at the end.
		 **/
		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
		 **/
		static std::string substitute(const std::string& str, Map<std::string,std::string>& variables);

		/**
		 * Read an integer starting from <i>ptr</i>. If your ptr points
		 * to, say, "-123Abc", the returned string will contain "-123".
		 * You may need this method in conjunction with parseInt or
		 * parseLong.
		 * @param ptr the character string that contains a number
		 **/
		static std::string readInteger(const char* ptr);
		/**
		 * Read a floating point number starting from <i>ptr</i>. If your
		 * ptr points to, say, "123.56e10Abc", the returned string will
		 * contain "123.56e10". You may need this method in conjunction
		 * with parseInt or parseDouble.
		 * @param ptr the character string that contains a number
		 * @param decimalSeparator the character used to separate decimals
		 **/
		static std::string readDouble(const char* ptr, const char decimalSeparator='.');

		/**
		 * Convert any type to a string.
		 **/
		template <class T> static std::string toString(T value);

		/**
		 * Get any type out of a string.
		 **/
		template <class T> static T parse(const std::string& str);

		/**
		 * Get an integer out of a string.
		 **/
		static int parseInt(const std::string& str);
		/**
		 * Get a long integer out of a string.
		 **/
		static long parseLong(const std::string& str);
		/**
		 * Get a double out of a string.
		 **/
		static double parseDouble(const std::string& str);

		/**
		 * Convert a string to a list of elements of any type.
		 *
		 * @param msg the string to be split into parts
		 * @param separators the characters that are to be treated as
		 * token separators, for example: " \t\n\r".
		 * @return the tokens converted to the wanted type.
		 **/
		template <class T> static List<T> parseList(const std::string& str,std::string separatorChars);
		/**
		 * Tokenize a string.
		 *
		 * @param msg the string to be split into parts
		 * @param separators the characters that are to be treated as token separators,
		 *        for example: " \t\n\r".
		 * @param result the list that is to store the tokens
		 * @param maxParts the maximum number of separated parts. maxParts <= 0 means all.
		 **/
		static void tokenize(const std::string& msg, std::string separators, List<std::string>& result, int maxParts=-1);

		/**
		 * Tokenize a string.
		 *
		 * @param msg the string to be split into parts
		 * @param separators the characters that are to be treated as token separators,
		 *        for example: " \t\n\r".
		 * @param maxParts the maximum number of separated parts. maxParts <= 0 means all.
		 *
		 * @return separated tokens in a list
		 **/
		static List<std::string> tokenize(const std::string& msg, std::string separators, int maxParts=-1);

		/**
		 * Join strings together.
		 *
		 * @param parts some strings
		 * @param glue the string to be placed between the parts
		 **/
		static std::string join(const List<std::string>& parts, std::string glue);

		/**
		 * Take a part of a string. If either <i>start</i> or
		 * <i>length</i> is negative, it is treated as a negative offset
		 * from the end of the string.
		 **/
		static std::string substr(const std::string& str, int start, int length=MAXINT);

		/**
		 * Check if <i>str</i> starts with <i>start</i>.
		 **/
		static bool startsWith(const std::string& str, std::string start)
		{ return str.size() >= start.size() && str.substr(0,start.size()) == start; }
		
		/**
		 * Check if <i>str</i> ends with <i>end</i>.
		 **/
		static bool endsWith(const std::string& str, std::string end)
		{ return str.size() >= end.size() && str.substr(str.size()-end.size()) == end; }
	};

	template <> std::string String::toString(int i);
	template <> std::string String::toString(long l);
	template <> std::string String::toString(double d);
	template <> inline std::string String::toString(float d) { return toString((double)d); }
	template <> inline std::string String::toString(std::string s) { return s; }
	template <> inline std::string String::toString(Object& o) { return o.toString(); }

	template <> int String::parse<int>(const std::string& str);
	template <> long String::parse<long>(const std::string& str);
	template <> double String::parse<double>(const std::string& str);
	template <> inline float String::parse<float>(const std::string& str) { return (float)parse<double>(str); }
	template <> inline std::string String::parse<std::string>(const std::string& str) { return str; }

	template <class T> std::string String::toString(T value)
	{
		std::ostringstream stream;
		stream << value;
		return std::string(stream.str());
	}

	template <class T> T String::parse(const std::string& str)
	{
		std::istringstream stream(str.c_str());
		T value;
		stream >> value;
		return value;
	}

	template <class T> List<T> String::parseList(const std::string& msg, std::string separators)
	{
		List<std::string> strParts(tokenize(msg,separators));
		List<T> result(strParts.getLength());
		for (int i=0;i<strParts.getLength();i++)
			result += parse<T>(strParts[i]);
		return result;
	}
}

#endif
