#include "config.h"

// Implementation
#include "Text.hpp"

// STDC++
#include <cstdio>
#include <iostream>

// C++ libraries
#include <ucommon/secure.h>

// Local
#include "Time.hpp"
#include "Misc.hpp"

namespace UI {
namespace Util {

std::string strVec2Str(StrVec const & strVec, std::string const & sep)
{
	return join(sep, strVec);
}

std::string strerror(int const & errNo)
{
	std::string result;

#ifndef UI_UTILCPP_DISABLE_STRERROR
#ifdef WIN32
	result = ::strerror(errNo);
#else
	int const bufSize(80);
	char buf[bufSize];
#ifdef STRERROR_R_CHAR_P
	// Non-POSIX variant (like GNU libc). See AC_FUNC_STRERROR_R autotool test, and its comments.
	// This "may or may not use the supplied buffer" [debian sarge dev. does not ].
	char * ret(::strerror_r(errNo, buf, bufSize-1));
	result = ret ? ret : "";
#else
	// POSIX variant
	::strerror_r(errNo, buf, bufSize-1);
	result = buf;
#endif
#endif
#endif

	return result == "" ? "No errno error string available" : result;
}

std::string getlineCRLF(std::istream & s)
{
	std::string line;
	if (std::getline(s, line))
	{
		if (line.size() > 0 && *(--line.end()) == '\r')
		{
			line.erase(--line.end());
		}
	}

	return line;
}

std::string asciiCAPS(std::string const & in, bool const upper)
{
	std::string result;
	result.resize(in.size());
	for (unsigned i(0); i < in.size(); ++i)
	{
		result[i] = upper ? static_cast<char>(std::toupper(in[i])) :
			static_cast<char>(std::tolower(in[i]));
	}
	return result;
}

std::string & str2Ascii(std::string & s)
{
	for (std::string::iterator i(s.begin()); i != s.end(); ++i)
	{
#ifdef WIN32
		if (!isascii(*i)) { *i = '?'; }
#else
		if (!::isascii(*i)) { *i = '?'; }
#endif
	}
	return s;
}

AlphaNumericKey::AlphaNumericKey()
	:keyChars_("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
	,engine_(getTimeValUSec())
	,distribution_(0, keyChars_.size()-1)
	,generator_(engine_, distribution_)
{}

std::string AlphaNumericKey::get(int len)
{
	std::string key;
	key.resize(len);
	for (int j(0); j < len; j++)
	{
		key[j] = keyChars_[generator_()];
	}
	return key;
}

std::string genAlphaNumericKey(int len)
{
	return GlobalAlphaNumericKey_.get(len);
}

char * strdup(char const * s)
{
	assert(s);

	size_t const n(std::strlen(s) + 1);
	char * const p((char *) std::malloc(n));
	if (p)
	{
		std::memcpy(p, s, n);
	}
	return p;
}

CStrArray & CStrArray::add(std::string const & str)
{
	assert(!arr_.empty());
	// Push back, but before Null-Terminator
	arr_.insert(arr_.end()-1, strdup(str.c_str()));
	return *this;
}

CStrArray::CStrArray()
{
	// Add Null-Terminator
	arr_.push_back(0);
}

CStrArray::~CStrArray()
{
	UI::Util::freeAnySeqContainer<char const>(arr_);
}

char const ** CStrArray::get() const
{
	assert(!arr_.empty());
	return (char const **) &arr_[0];
}


StrVec strtok(std::string const & s, std::string const & delim, std::string const & prefix, std::string const & postfix)
{
	return strtok<StrVec >(s, delim, prefix, postfix);
}

StrVec strtoks(std::string const & s, std::string const & delims, std::string const & prefix, std::string const & postfix)
{
	return strtoks<StrVec >(s, delims, prefix, postfix);
}

bool isToken(std::string const & token, StrVec const & tokensVec, int const match)
{
	std::string const ltoken(match == 0 ? token : asciiCAPS(token, match > 0));

	for (StrVec::const_iterator i(tokensVec.begin()); i != tokensVec.end(); ++i)
	{
		if (*i == ltoken) { return true; };
	}
	return false;
}

bool isToken(std::string const & token, std::string const & tokens, std::string const & delim, int const match)
{
	StrVec const tokensVec(strtok(tokens, delim));
	return isToken(token, tokensVec, match);
}

std::string & strrpl(std::string & source, std::string const & token, std::string const & reptoken)
{
	assert(!token.empty());
	std::string::size_type i(0);
	while ((i = source.find(token, i)) != std::string::npos)
	{
		source.replace(i, token.size(), reptoken);
		i += reptoken.size();
	}
	return source;
}

std::string strrpl(std::string const & source, std::string const & token, std::string const & reptoken)
{
	std::string result(source);
	strrpl(result, token, reptoken);
	return result;
}

std::string istream2String(std::istream & f, int const blockSize)
{
	std::string result;
	std::vector<char> buffer(blockSize);

	while (!f.eof())
	{
		f.read(&buffer[0], blockSize);
		result.append(&buffer[0], f.gcount());
	}
	return result;
}

/* lookup table for base64 characters */
const unsigned char XConversion::alphabet[] = {'A','B','C','D','E','F','G','H','I','J',
                                               'K','L','M','N','O','P','Q','R','S','T',
                                               'U','V','W','X','Y','Z','a','b','c','d',
                                               'e','f','g','h','i','j','k','l','m','n',
                                               'o','p','q','r','s','t','u','v','w','x',
                                               'y','z','0','1','2','3','4','5','6','7',
                                               '8','9','+','/','=' };

unsigned char XConversion::codes[256] = "";
void XConversion::codesFill() {

	int i = 0;

	for (i=0; i<256; i++)
	{
		codes[i] = 0;
	}
	for (i = 'A'; i <= 'Z'; i++)
	{
		codes[i] = static_cast<unsigned char>( i - 'A');
	}
	for (i = 'a'; i <= 'z'; i++)
	{
		codes[i] = static_cast<unsigned char>(26 + i - 'a');
	}
	for (i = '0'; i <= '9'; i++)
	{
		codes[i] = static_cast<unsigned char>(52 + i - '0');
	}

	codes[(int) '+'] = 62;
	codes[(int) '/'] = 63;
}

std::string XConversion::dec2Basis( int number, const int basis ) {

	std::string out("");
	char digits[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
	int remainder = 0;

	if ( basis >= bMin && basis <=bMax ) {
		do {
			remainder = number % basis;
			out = digits[remainder] + out;
			number = number / basis;
		} while (number>0);
	}
	return out;
}

int XConversion::getInt( unsigned char hex ) {
	int ret=0;
	if ( hex >= 0x41 && hex <= 0x46 ) {      // A-F
		ret = (int)(hex-0x37);
	}
	if ( hex >= 0x61 && hex <= 0x66 ) {     // a-f
		ret = (int)(hex-0x57);
	}
	if ( hex >= 0x30 && hex <= 0x39 ) {     // 1-9
		ret = (int)(hex-0x30);
	}
	return ret;
}

int XConversion::basis2Dec( std::string number, int basis ) {

	int ret = getInt( (unsigned char) number[0] );
	for ( unsigned int i=1 ; i<number.length() ; i++ ) {
		ret = ret * basis + getInt( (unsigned char) number[i] );
	}
	return ret;
}

UI::Util::auto_ptr<unsigned char> XConversion::xorEncrypt(std::string const& message, std::string const& key, int* length)
{
	unsigned char* result=0;
	int keyLen = key.size();
	int messageLen = message.size();
	int i=0;
	int j=0;

	result=new unsigned char[messageLen];
	for ( i=0 ; i<messageLen ; i++ ) {
		result[i] = (unsigned char)(message[i]^key[j]);
		j++;
		if (j>=keyLen) j=0;
	}
	*length=i;
	return UI::Util::auto_ptr<unsigned char>(result);
}

std::string XConversion::xorDecrypt(const unsigned char* message, const int length, std::string const& key) {

	std::stringstream result;
	int keyLen = key.size();
	int messageLen = length;
	int i=0;
	int j=0;

	if ( message ) {
		for ( i=0 ; i<messageLen ; i++ ) {
			result<< (char) (message[i]^key[j]);
			j++;
			if (j>=keyLen) j=0;
		}
	}
	return std::string(result.str());
}

std::string XConversion::base64Encode(const unsigned char* message, const int length) 
{
	int i = 0;
	int index = 0;
	int quad = 0;
	int trip = 0;
	int val = 0;
	char r0, r1, r2, r3 = 0;
	std::stringstream result;

	for (i=0, index=0; i<length; i+=3, index+=4) {
		quad = 0;
		trip = 0;
		val = (0xFF & (int) message[i]);
		val <<= 8;
		if ((i+1) < length) {
			val |= (0xFF & (int) message[i+1]);
			trip = 1;
		}
		val <<= 8;
		if ((i+2) < length) {
			val |= (0xFF & (int) message[i+2]);
			quad = 1;
		}
		r3=alphabet[(quad? (val & 0x3F): 64)];
		val >>= 6;
		r2=alphabet[(trip? (val & 0x3F): 64)];
		val >>= 6;
		r1=alphabet[val & 0x3F];
		val >>= 6;
		r0=alphabet[val & 0x3F];
		result<<r0<<r1<<r2<<r3;
	}
	return std::string(result.str());
}

UI::Util::auto_ptr<unsigned char> XConversion::base64Decode(std::string const& message, int* length) {

	int ix = 0;

	int shift = 0;                                /* # of excess bits stored in accum */
	int accum = 0;                                /* excess bits */
	int index = 0;
	int value = 0;
	int inLen = message.size();
	unsigned char* result= NULL;
                                                /* 4 chars decode to 3 bytes and input length is always an even */
	codesFill();                                  /* multiple of 4 characters. */
                                                /* -> output length is ((intput length / 4) *3) - Pads ! */
	(*length) = ((inLen) / 4) * 3;

	if (inLen>0 && message[inLen-1] == '=') (*length)--;  /* ignore the Pads */
	if (inLen>0 && message[inLen-2] == '=') (*length)--;

	result = new unsigned char[(*length)];

	for (ix=0; ix<inLen; ix++)
	{
		value = codes[ message[ix] & 0xFF ];     /* ignore high byte of char */
		if ( value >= 0 ) {                      /* skip over non-code */
			accum <<= 6;                        /* bits shift up by 6 each time thru */
			shift += 6;                         /* loop, with new bits being put in */
			accum |= value;                     /* at the bottom. */
			if ( shift >= 8 ) {                 /* whenever there are 8 or more shifted in, */
				shift -= 8;                      /* write them out (from the top, leaving any */
				result[index++] =                /* excess at the bottom for next iteration. */
					((accum >> shift) & 0xff);
			}
		}
	}
	return UI::Util::auto_ptr<unsigned char>(result);
}

std::string md5sum(std::string const & data)
{
	return *(ucommon::Digest::md5(data.c_str()));
}

}}

std::ostream & operator <<(std::ostream & os, UI::Util::StrVec const & sv)
{
	os << UI::Util::join(",", sv);
	return os;
}

std::ostream & operator <<(std::ostream & os, UI::Util::StrList const & sl)
{
	os << UI::Util::join(",", sl);
	return os;
}
