/**
 * @file
 * @brief Text, descriptors and sockets.
 */
#ifndef UI_UTIL_TEXT_HPP
#define UI_UTIL_TEXT_HPP

// STDC++
#include <string>
#include <vector>
#include <map>
#include <list>
#include <sstream>
#include <memory>
#include <cassert>
#include <iomanip>
#include <cerrno>

// C++ libraries
#include <ui-utilcpp/auto_ptr_compat.hpp>
#include <ui-utilcpp/Exception.hpp>
#include <boost/random.hpp>

#ifdef WIN32
	#if !defined( PATH_MAX )
		#include <Windows.h>
		#define PATH_MAX MAX_PATH
	#endif
#endif

namespace UI {
namespace Util {

/** @name Shortcuts for often used types and convenience stream operators.
 * @{ */
typedef std::vector<std::string> StrVec;
typedef std::list<std::string> StrList;
typedef std::map<std::string, std::string> StrMap;
/** @} */
}}

namespace UI {
namespace Util {

std::string strVec2Str(StrVec const & strVec, std::string const & sep=" ");

/** @brief You may use on as return value for an existing, but empty, string. */
static std::string const EmptyString_("");

/** @brief Save wrapper for ::strerror. Always use this instead of ::strerror directly. */
std::string strerror(int const & errNo=errno);

/** @brief Like std::getline, but also rips of trailing "CR" when line break was CRLF. */
std::string getlineCRLF(std::istream & s);

/** @brief ASCII CAPS converter for strings (using std::tolower|upper). */
std::string asciiCAPS(std::string const & in, bool const upper=true);

/** @brief Replace all non-ASCII characters to '?' in string. */
std::string & str2Ascii(std::string & s);

/** @brief Defaults values for some types via template specialization. */
template<typename T>
inline T Default() { return T(); }
/** @brief Default-Specialising. */
template<> inline int Default<int>() { return 0; }
/** @brief Default-Specialising. */
template<> inline float Default<float>() { return 0.0; }
/** @brief Default-Specialising. */
template<> inline double Default<double>() { return 0.0; }
/** @brief Default-Specialising. */
template<> inline long int Default<long int>() { return 0; }
/** @brief Default-Specialising. */
template<> inline bool Default<bool>() { return false; }
/** @brief Default-Specialising. */
template<> inline std::string Default<std::string>() { return ""; }

/** @brief Stream modifier function (is there a std:: type??). */
typedef std::ios_base & (* StreamModifier) (std::ios_base &);

/** @brief String-to-anything converter with error handling.
 *
 * @param t String to convert.
 * @param m Optional stream modifier like std::hex.
 * @returns Pair with conversion result and success; if false, result's value is undefinded.
 */
template <typename out_type>
std::pair<out_type, bool> eato(std::string const & t, StreamModifier const m=0)
{
	// Prepare converter stream
	std::stringstream s(t);
	if (m) { s << m; };

	// Convert with error handling
	std::pair<out_type, bool> result;
	s >> result.first;
	result.second = !s.fail();

	return result;
}

/** @brief String-to-anything converter with default values on error. @see eato. */
template <typename out_type>
out_type ato(std::string const & t, StreamModifier const m=0)
{
	std::pair<out_type, bool> const result(eato<out_type>(t, m));
	return result.second ? result.first : Default<out_type>();
}

/** @brief Anything-to-string converter with error handling.
 *
 * @param t Value to convert.
 * @param m Optional stream modifier like std::hex.
 * @param w Argument for std::setw() modifier.
 * @param f Argument for std::setfill() modifier.
 * @returns Pair with conversion result and success; if false, result's value is undefinded.
 */
template <typename in_type>
std::pair<std::string, bool> etos(in_type const & t, StreamModifier const m=0, int w=0, char f=' ')
{
	// Prepare converter stream
	std::stringstream s;
	if (m) { s << m; }
	if (w) { s << std::setw(w); }
	if (f != ' ') { s << std::setfill(f); }

	// Convert with error handling
	std::pair<std::string, bool> result;
	s << t;
	result.first = s.str();
	result.second = !s.fail();

	return result;
}

/** @name Anything-to-string converter with empty string on error. @see etos.
 * @{ */
template <typename in_type>
std::string tos(in_type const & t, StreamModifier const m=0, int w=0, char f=' ')
{
	std::pair<std::string, bool> const result(etos<in_type>(t, m, w, f));
	return result.second ? result.first : std::string("");
}

template <typename in_type>
std::string tos(in_type const & t, int w, char f=' ')
{
	std::pair<std::string, bool> const result(etos<in_type>(t, 0, w, f));
	return result.second ? result.first : std::string("");
}
/** @} */

/** @name Generate arbitrary length pseudo random keys with alphanumeric ASCII characters.
 * @{ */
/** @brief Class using boost::mt19937 as random engine. */
class AlphaNumericKey
{
public:
	/** @brief Construct; seed will be automatically set (once) via timestamp (for linux/unix: microseconds). */
	AlphaNumericKey();
	std::string get(int len=8);
private:
	std::string const keyChars_;
	boost::mt19937 engine_;
	boost::uniform_int<> distribution_;
	boost::variate_generator<boost::mt19937&, boost::uniform_int<> > generator_;
};

/** @brief You may just use this global generator for convenience.
 * @note A singleton would be nicer, but imho not worth the effort, considering multi threading protection. */
static AlphaNumericKey GlobalAlphaNumericKey_;

/** @brief For compatibility only. */
std::string genAlphaNumericKey(int len=8);
/** @} */

/** @brief BSD style "strdup" implementation (is not C standard). See strdup(3). */
char * strdup(char const * s);

/** @brief Helper to construct/use C-String arrays ("char **"), occasionally needed by some c functions. */
class CStrArray
{
public:
	/** @brief Add string to array. */
	CStrArray & add(std::string const & str);
	CStrArray();
	~CStrArray();
	/** @brief Get C string array. */
	char const ** get() const;
private:
	std::vector<char const *> arr_;
};


/** @brief Generate any STL container of strings from a token string.
 *
 * @note There is a <tt>StrVec</tt> shortcut for
 * this template. Doxygen seems to show only its syntax. @see
 * Text.hpp source header for the template syntax.
 */
template <typename C>
C strtok(std::string const & s, std::string const & delim=",", std::string const & prefix="", std::string const & postfix="")
{
	C tokens;
	std::string::size_type pos(0);
	while (pos != std::string::npos)
	{
		std::string::size_type delimPos(s.find(delim, pos));
		std::string::size_type tokLen(delimPos == std::string::npos ? std::string::npos : delimPos-pos);
		std::string token(s.substr(pos, tokLen));
		if (!token.empty())
		{
			tokens.push_back(prefix + token + postfix);
		}
		pos = delimPos == std::string::npos ? std::string::npos : delimPos+delim.size();
	}
	return tokens;
}

StrVec strtok(std::string const & s, std::string const & delim=",", std::string const & prefix="", std::string const & postfix="");

/** @brief Like strtok, but you can use a list of one-char delimiters
 * (delims in strtoks) instead of a fixed delimiter string (delim in strtok). */
template <typename C>
C strtoks(std::string const & s, std::string const & delims=",", std::string const & prefix="", std::string const & postfix="")
{
	C tokens;
	std::string::size_type p0(0);
	while (p0 != std::string::npos)
	{
		std::string::size_type const p1(s.find_first_of(delims, p0));
		std::string const token(s.substr(p0, p1-p0));
		if (!token.empty())
		{
			tokens.push_back(prefix + token + postfix);
		}
		p0 = s.find_first_not_of(delims, p1);
	}
	return tokens;
}

StrVec strtoks(std::string const & s, std::string const & delims=" \t\n\r", std::string const & prefix="", std::string const & postfix="");

/**
 * @brief join a list,vector,set, ... with the connector string
 *
 * e.g. the string list 'a', 'b', 'c', 'd' joined with ',' will return 'a,b,c,d'
 *
 * As C needs to supply a  C::const_iterator C.begin() C.end() and ++opertator
 * e.g. std::vector<std::string>
 */
template <typename C>
std::string join( std::string const & expr, C const & list )
{
	std::string res;
	typename C::const_iterator it( list.begin() );
	if ( it != list.end() )
	{
		res = *it;
		++it;
		for ( ; it != list.end();  ++it )
		{
			res += expr + *it;
		}
	}
	return res;
}

/** @name Check whether token is in tokens. */
/** @{ */
bool isToken(std::string const & token, StrVec const & tokensVec, int const match=0);
bool isToken(std::string const & token, std::string const & tokens, std::string const & delim=",", int const match=0);
/** @} */

/** @name Mass string replace.
 * @{ */
std::string & strrpl(std::string & source, std::string const & token, std::string const & reptoken);
std::string strrpl(std::string const & source, std::string const & token, std::string const & reptoken);
/** @} */

/** @brief Read any stream into any byte vector.
 *
 * @note std::istream::read() is not const, so the stream reference can't be const.
 */
template <typename Byte>
std::vector<Byte> istream2Vector(std::istream & f, int const blockSize=1024)
{
	// We rely on byte size
	assert(sizeof(Byte) == 1);

	// Pre: Set vector to size zero, no bytes read
	std::vector<Byte> result(0);
	long int bytesRead(0);

	while (!f.eof())
	{
		// Prepare to read blockSize bytes from stream into vector
		result.resize(bytesRead + blockSize);
		// Read a block directly to vector memory (we cast here, but this should really work for any byte-sized type)
		f.read((char *) &result[bytesRead], blockSize);
		bytesRead += f.gcount();
	}

	// Resize down to readBytes size
	result.resize(bytesRead);
	return result;
}

/** @brief Istream-to-string converter.
 *
 * @note Works for "binary"-files as well, but istream2Vector might be a better choice.
 */
std::string istream2String(std::istream & f, int const blockSize=1024);

/** @brief a class to convert between different systems.
 *
 * @deprecated All different conversions here should rather get a
 * XXXConverter class in Recoder.?pp (and an appropriate switch in the
 * Recoder class if needed).
 */
class XConversion {

private:
	/* lookup table for base64 characters */
	static const unsigned char alphabet[];

	/* lookup table for values 0..63 */
	static unsigned char codes[];
	static void codesFill();

	static const int bMin = 2;
	static const int bMax = 16;

	static int getInt( unsigned char hex );
	static std::string out;

public:
	/**
	 * Convert a dezimal (10) represented number in a number<br>
	 * represented by any basis between 2 (Bin) and 16 (Hex)
	 *
	 * @param number the number to convert
	 * @param basis the basis to use
	 *
	 * @returns a number represented by the wanted basis<br>
	 *         or an empty string
	 */
	static std::string dec2Basis( int number, int basis );

	/**
	 * Convert a number represented by any basis between<br>
	 * 2 (bin) and 16 (hex) to a dezimal number (10)
	 *
	 * @param number the numbet to convert
	 * @param basis the basis to use
	 *
	 * @returns a number represented in decimal (basis 10)
	 */
	static int basis2Dec( std::string number, int basis );

	/**
	 * encrypt a message using xor and the given key
	 *
	 * @param message the mesaage to encrypt
	 * @param key the key used to encrypt the message
	 * @param length number of encrypted chars (return)
	 *
	 * @returns the encrypted message (binary) 
	 */
	static UI::Util::auto_ptr<unsigned char> xorEncrypt(std::string const& message, std::string const& key, int* length);

	/**
	 * decrypt a message using xor and the given key
	 *
	 * @param message the message to decrypt
	 * @param length the length of the message
	 * @param key the key used to decrypt the message
	 *
	 * @returns the decrypted message (ASCII)
	 */
	static std::string xorDecrypt( const unsigned char* message, const int length, std::string const& key );

	/**
	 * returns an array of base64-encoded characters to represent the
	 * passed data array.
	 *
	 * @param message the array of bytes to encode
	 * @param length length of the byte array
	 *
	 * @returns result base64 encoded message (ASCII)
	 */
	static std::string base64Encode( const unsigned char* message, const int length );

	/**
	 * returns an array of bytes which were encoded in the passed
	 * character array.
	 *
	 * @param message a base64 encoded message
	 * @param length used to store the result (Binary)
	 *
	 * @returns number decoded chars
	 */
	static UI::Util::auto_ptr<unsigned char> base64Decode(std::string const& message, int* length);
};

std::string md5sum(std::string const & data);

}}

/** @name Some convenience stream operators.
 * @{ */
std::ostream & operator <<(std::ostream & os, UI::Util::StrVec const & sv);
std::ostream & operator <<(std::ostream & os, UI::Util::StrList const & sl);
/** @} */

#endif
