//
// This file is part of the aMule Project.
//
// Copyright (c) 2005-2011 aMule Team ( admin@amule.org / http://www.amule.org )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//

#include "Format.h"

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

#if defined HAVE_STDINT_H
#	include <stdint.h>
#elif defined HAVE_INTTYPES_H
#	include <inttypes.h>
#endif

#include <wx/intl.h>			// Needed for _()

#include <errno.h>			// Needed for errno and EINVAL
#include "strerror_r.h"			// Needed for mule_strerror_r()

/** Returns true if the char is a format-type. */
inline bool isTypeChar(wxChar c)
{
	switch (c) {
		case wxT('s'):		// String of characters
		case wxT('u'):		// Unsigned decimal integer
		case wxT('i'):		// Signed decimal integer
		case wxT('d'):		// Signed decimal integer
		case wxT('c'):		// Character
		case wxT('f'):		// Decimal floating point
		case wxT('F'):		// Decimal floating point
		case wxT('x'):		// Unsigned hexadecimal integer
		case wxT('X'):		// Unsigned hexadecimal integer (capital letters)
		case wxT('o'):		// Unsigned octal
		case wxT('e'):		// Scientific notation (mantise/exponent) using e character
		case wxT('E'):		// Scientific notation (mantise/exponent) using E character
		case wxT('g'):		// Use shorter %e or %f
		case wxT('G'):		// Use shorter %E or %F
		case wxT('p'):		// Pointer
		case wxT('n'):		// Not supported, still needs to be caught though
		case wxT('a'):		// (C99; not in SUSv2) Double in hexadecimal notation.
		case wxT('A'):		// (C99; not in SUSv2) Double in hexadecimal notation (capital letters).
		case wxT('C'):		// (Not in C99, but in SUSv2.)  Synonym for lc.  Don't use. Not supported.
		case wxT('S'):		// (Not in C99, but in SUSv2.)  Synonym for ls.  Don't use. Not supported.
		case wxT('m'):		// (Glibc extension.)  Print output of strerror(errno).  No argument is required.
//		case wxT('%'):		// A `%' is written.  No argument is converted.  The complete conversion specification is `%%'.
			return true;
	}

	return false;
}

/** Returns true if the char is a valid flag. */
inline bool isFlagChar(wxChar c)
{
	switch (c) {
		// C standard flags
		case wxT('+'):		// Include sign for integers
		case wxT('-'):		// Left-align output
		case wxT('#'):		// Alternate form, varies
		case wxT(' '):		// Pad with spaces
		case wxT('0'):		// Pad with zeros
		// SUSv2
		case wxT('\''):		// For decimal conversion (i, d, u, f, F, g, G) the output is to be grouped with thousands' grouping characters if the locale information indicates any.
		// glibc 2.2
		case wxT('I'):		// For decimal integer conversion (i, d, u) the output uses the locale's alternative output digits, if any.
			return true;
	}

	return false;
}

/** Returns true if the char is a valid length modifier. */
inline bool isLengthChar(wxChar c)
{
	switch (c) {
		case wxT('h'):		// Short ('hh') and char ('h')
		case wxT('l'):		// Long ('l') and long long ('ll')
		case wxT('L'):		// Double long
		case wxT('z'):		// size_t
		case wxT('j'):		// intmax_t
		case wxT('t'):		// ptrdiff_t
		case wxT('q'):		// quad. Synonym for 'll'. Don't use.
			return true;
	}

	return false;
}

/** Returns true if the argument is a valid length modifier */
inline bool isValidLength(const wxString& str)
{
	return ((str == wxT("hh")) || (str == wxT("h")) ||
		(str == wxT("l")) || (str == wxT("ll")) ||
		(str == wxT("L")) || (str == wxT("z")) ||
		(str == wxT("j")) || (str == wxT("t")) ||
		(str == wxT("q")));
}


enum eStringParserStates {
	esNonFormat = 0,	// Not in a format string
	esFormatStart,		// Start of a format string
	esFormat,		// Inside a format string
	esFormatEnd,		// Finished reading a format string
	esInvalidFormat		// Invalid (incomplete) format specifier
};

/**
 * State machine to extract format specifiers from the string
 *
 * All format strings will be extracted, regardless whether they are valid or
 * not. Also '%%' is considered to be special format string which requires no
 * arguments, thus it will be extracted too (that is because it has to be
 * converted to '%').
 */
static eStringParserStates stringParser[][3] = {
			/* %-sign,		type-char,	other */
/* esNonFormat */	{ esFormatStart,	esNonFormat,	esNonFormat	},
/* esFormatStart */	{ esFormatEnd,		esFormatEnd,	esFormat	},
/* esFormat */		{ esInvalidFormat,	esFormatEnd,	esFormat	},
/* esFormatEnd */	{ esFormatStart,	esNonFormat,	esNonFormat	},
/* esInvalidFormat */	{ esFormatEnd,		esFormatEnd,	esFormat	}
};

enum eFormatParserStates {
	efStart = 0,	// Format string start
	efArgIndex,	// Argument index
	efArgIndexEnd,	// End of the argument index ('$' sign)
	efFlagChar,	// Flag character
	efWidth,	// Width field
	efPrecStart,	// Precision field start ('.' character)
	efPrecision,	// Precision field
	efLength,	// Length field
	// The following two are terminal states, they terminate processing
	efType,		// Type character
	efError		// Invalid format specifier
};

/**
 * State machine to parse format specifiers
 *
 * Format specifiers are expected to follow the following structure:
 *	%[argIndex$][Flags][Width][.Precision][Length]<Type>
 */
static eFormatParserStates formatParser[][7] = {
			/* [1-9],	'0',		flagChar,	'.',		lengthChar,	typeChar,	'$' */
/* efStart */		{ efArgIndex,	efFlagChar,	efFlagChar,	efPrecStart,	efLength,	efType,		efError, },
/* efArgIndex */	{ efArgIndex,	efArgIndex,	efError,	efPrecStart,	efLength,	efType,		efArgIndexEnd, },
/* efArgIndexEnd */	{ efWidth,	efFlagChar,	efFlagChar,	efPrecStart,	efLength,	efType,		efError, },
/* efFlagChar */	{ efWidth,	efError,	efError,	efPrecStart,	efLength,	efType,		efError, },
/* efWidth */		{ efWidth,	efWidth,	efError,	efPrecStart,	efLength,	efType,		efError, },
/* efPrecStart */	{ efPrecision,	efPrecision,	efError,	efError,	efLength,	efType,		efError, },
/* efPrecision */	{ efPrecision,	efPrecision,	efError,	efError,	efLength,	efType,		efError, },
/* efLength */		{ efError,	efError,	efError,	efError,	efLength,	efType,		efError, }
};

// Forward-declare the specialization for const wxString&, needed by the parser
template<> void CFormat::ProcessArgument(FormatList::iterator, const wxString&);

void CFormat::Init(const wxString& str)
{
	m_formatString = str;
	m_argIndex = 0;

	// Extract format-string-like substrings from the input
	{
		size_t formatStart = 0;
		eStringParserStates state = esNonFormat;
		for (size_t pos = 0; pos < str.length(); ++pos) {
			if (str[pos] == wxT('%')) {
				state = stringParser[state][0];
			} else if (isTypeChar(str[pos])) {
				state = stringParser[state][1];
			} else {
				state = stringParser[state][2];
			}
			switch (state) {
				case esInvalidFormat:
					wxFAIL_MSG(wxT("Invalid format specifier: ") + str.Mid(formatStart, pos - formatStart + 1));
				case esFormatStart:
					formatStart = pos;
					break;
				case esFormatEnd:
					{
						FormatSpecifier fs;
						fs.startPos = formatStart;
						fs.endPos = pos;
						fs.result = str.Mid(formatStart, pos - formatStart + 1);
						m_formats.push_back(fs);
					}
				default:
					break;
			}
		}
		wxASSERT_MSG((state == esFormatEnd) || (state == esNonFormat), wxT("Incomplete format specifier: ") + str.Mid(formatStart));
	}

	// Parse the extracted format specifiers, removing invalid ones
	unsigned formatCount = 0;
	for (FormatList::iterator it = m_formats.begin(); it != m_formats.end();) {
		if (it->result == wxT("%%")) {
			it->argIndex = 0;
			it->result = wxT("%");
			++it;
		} else {
			it->argIndex = ++formatCount;
			it->flag = '\0';
			it->width = 0;
			it->precision = -1;
			it->type = '\0';
			unsigned num = 0;
			wxString lengthModifier;
			bool isPrecision = false;
			eFormatParserStates state = efStart;
			for (size_t pos = 1; pos < it->result.length(); ++pos) {
				wxChar c = it->result[pos];
				if ((c >= wxT('1')) && (c <= wxT('9'))) {
					state = formatParser[state][0];
				} else if (c == wxT('0')) {
					state = formatParser[state][1];
				} else if (isFlagChar(c)) {
					state = formatParser[state][2];
				} else if (c == wxT('.')) {
					state = formatParser[state][3];
				} else if (isLengthChar(c)) {
					state = formatParser[state][4];
				} else if (isTypeChar(c)) {
					state = formatParser[state][5];
				} else if (c == wxT('$')) {
					state = formatParser[state][6];
				} else {
					state = efError;
				}
				if ((c >= wxT('0')) && (c <= wxT('9'))) {
					num *= 10;
					num += (c - wxT('0'));
				}
				switch (state) {
					case efArgIndexEnd:
						it->argIndex = num;
						num = 0;
						break;
					case efFlagChar:
						it->flag = c;
						break;
					case efPrecStart:
						it->width = num;
						num = 0;
						isPrecision = true;
						break;
					case efLength:
						if (isPrecision) {
							it->precision = num;
						} else if (num > 0) {
							it->width = num;
						}
						num = 0;
						lengthModifier += c;
						if (!isValidLength(lengthModifier)) {
							state = efError;
						}
						break;
					case efType:
						if (isPrecision) {
							it->precision = num;
						} else if (num > 0) {
							it->width = num;
						}
						if (c == wxT('m')) {
							it->argIndex = 0;
							it->type = wxT('s');
							int errnum = errno;
#if defined(HAVE_STRERROR) || defined(HAVE_STRERROR_R)
							unsigned buflen = 256;
							bool done = false;
							do {
								errno = 0;
								char* buf = new char[buflen];
								*buf = '\0';
								int result = mule_strerror_r(errnum, buf, buflen);
								if ((result == 0) || (buflen > 1024)) {
									ProcessArgument<const wxString&>(it, wxString(buf, wxConvLocal));
								} else if (errno == EINVAL) {
									if (*buf == '\0') {
										ProcessArgument<const wxString&>(it, wxString::Format(_("Unknown error %d"), errnum));
									} else {
										ProcessArgument<const wxString&>(it, wxString(buf, wxConvLocal));
									}
								} else if (errno != ERANGE) {
									ProcessArgument<const wxString&>(it, wxString::Format(_("Unable to get error description for error %d"), errnum));
								} else {
									buflen <<= 1;
								}
								delete [] buf;
								done = ((result == 0) || (errno != ERANGE));
							} while (!done);
#else
							wxFAIL_MSG(wxString::Format(wxT("Unable to get error description for error %d."), errnum));
							ProcessArgument<const wxString&>(it, wxString::Format(_("Unable to get error description for error %d"), errnum));
#endif
						} else {
							it->type = c;
						}
					default:
						break;
				}
				wxCHECK2_MSG(state != efError, break, wxT("Invalid format specifier: ") + it->result);
				if (state == efType) {
					// Needed by the '%m' conversion, which takes place immediately,
					// overwriting it->result
					break;
				}
			}
			if (state == efError) {
				it = m_formats.erase(it);
				--formatCount;
			} else {
				++it;
			}
		}
	}
}

wxString CFormat::GetString() const
{
	wxString result;
	FormatList::const_iterator it = m_formats.begin();
	if (it == m_formats.end()) {
		result = m_formatString;
	} else {
		unsigned lastEnd = 0;
		for (; it != m_formats.end(); ++it) {
			result += m_formatString.Mid(lastEnd, it->startPos - lastEnd);
			result += it->result;
			lastEnd = it->endPos + 1;
		}
		result += m_formatString.Mid(lastEnd);
	}
	return result;
}

wxString CFormat::GetModifiers(FormatList::const_iterator it) const
{
	wxString result = wxT("%");
	if (it->flag != wxT('\0')) {
		result += it->flag;
	}
	if (it->width > 0) {
		result += wxString::Format(wxT("%u"), it->width);
	}
	if (it->precision >= 0) {
		result += wxString::Format(wxT(".%u"), it->precision);
	}
	return result;
}

// Forward-declare the specialization for unsigned long long
template<> void CFormat::ProcessArgument(FormatList::iterator, unsigned long long);

// Processing a double-precision floating-point argument
template<>
void CFormat::ProcessArgument(FormatList::iterator it, double value)
{
	switch (it->type) {
		case wxT('a'):
		case wxT('A'):
		case wxT('e'):
		case wxT('E'):
		case wxT('f'):
		case wxT('F'):
		case wxT('g'):
		case wxT('G'):
			break;
		case wxT('s'):
			it->type = wxT('g');
			break;
		default:
			wxFAIL_MSG(wxT("Floating-point value passed for non-floating-point format field: ") + it->result);
			return;
	}
	it->result = wxString::Format(GetModifiers(it) + it->type, value);
}

// Processing a wxChar argument
template<>
void CFormat::ProcessArgument(FormatList::iterator it, wxChar value)
{
	switch (it->type) {
		case wxT('c'):
			break;
		case wxT('s'):
			it->type = wxT('c');
			break;
		case wxT('u'):
		case wxT('d'):
		case wxT('i'):
		case wxT('o'):
		case wxT('x'):
		case wxT('X'):
			ProcessArgument(it, (unsigned long long)value);
			return;
		case wxT('a'):
		case wxT('A'):
		case wxT('e'):
		case wxT('E'):
		case wxT('f'):
		case wxT('F'):
		case wxT('g'):
		case wxT('G'):
			ProcessArgument(it, (double)value);
			return;
		default:
			wxFAIL_MSG(wxT("Character value passed to non-character format field: ") + it->result);
			return;
	}
	it->result = wxString::Format(GetModifiers(it) + it->type, value);
}

// Processing a signed long long argument
template<>
void CFormat::ProcessArgument(FormatList::iterator it, signed long long value)
{
	switch (it->type) {
		case wxT('c'):
			ProcessArgument(it, (wxChar)value);
			return;
		case wxT('i'):
			break;
		case wxT('o'):
		case wxT('x'):
		case wxT('X'):
			ProcessArgument(it, (unsigned long long)value);
			return;
		case wxT('u'):
		case wxT('d'):
		case wxT('s'):
			it->type = wxT('i');
			break;
		case wxT('a'):
		case wxT('A'):
		case wxT('e'):
		case wxT('E'):
		case wxT('f'):
		case wxT('F'):
		case wxT('g'):
		case wxT('G'):
			ProcessArgument(it, (double)value);
			return;
		default:
			wxFAIL_MSG(wxT("Integer value passed for non-integer format field: ") + it->result);
			return;
	}
	it->result = wxString::Format(GetModifiers(it) + WXLONGLONGFMTSPEC + it->type, value);
}

// Processing an unsigned long long argument
template<>
void CFormat::ProcessArgument(FormatList::iterator it, unsigned long long value)
{
	switch (it->type) {
		case wxT('c'):
			ProcessArgument(it, (wxChar)value);
			return;
		case wxT('u'):
		case wxT('o'):
		case wxT('x'):
		case wxT('X'):
			break;
		case wxT('i'):
		case wxT('d'):
		case wxT('s'):
			it->type = wxT('u');
			break;
		case wxT('a'):
		case wxT('A'):
		case wxT('e'):
		case wxT('E'):
		case wxT('f'):
		case wxT('F'):
		case wxT('g'):
		case wxT('G'):
			ProcessArgument(it, (double)value);
			return;
		default:
			wxFAIL_MSG(wxT("Integer value passed for non-integer format field: ") + it->result);
			return;
	}
	it->result = wxString::Format(GetModifiers(it) + WXLONGLONGFMTSPEC + it->type, value);
}

// Processing a wxString argument
template<>
void CFormat::ProcessArgument(FormatList::iterator it, const wxString& value)
{
	if (it->type != wxT('s')) {
		wxFAIL_MSG(wxT("String value passed for non-string format field: ") + it->result);
	} else {
		if (it->precision >= 0) {
			it->result = value.Left(it->precision);
		} else {
			it->result = value;
		}
		if ((it->width > 0) && (it->result.length() < it->width)) {
			if (it->flag == wxT('-')) {
				it->result += wxString(it->width - it->result.length(), wxT(' '));
			} else {
				it->result = wxString(it->width -it->result.length(), wxT(' ')) + it->result;
			}
		}
	}
}

// Processing pointer arguments
template<>
void CFormat::ProcessArgument(FormatList::iterator it, void * value)
{
	if ((it->type == wxT('p')) || (it->type == wxT('s'))) {
		// Modifiers (if any) are ignored for pointer conversions
		// built-in Format for pointer is not consistent:
		// - Windows: uppercase, no leading 0x
		// - Linux:   leading zeros missing
		// -> format it as hex
		if (sizeof(void*) == 8) {
			// 64 bit
			it->result = wxString::Format(wxT("0x%016x"), (uintptr_t)value);
		} else {
			// 32 bit
			it->result = wxString::Format(wxT("0x%08x"), (uintptr_t)value);
		}
	} else {
		wxFAIL_MSG(wxT("Pointer value passed for non-pointer format field: ") + it->result);
	}
}


// Generic argument processor template
template<typename _Tp>
CFormat& CFormat::operator%(_Tp value)
{
	m_argIndex++;
	for (FormatList::iterator it = m_formats.begin(); it != m_formats.end(); ++it) {
		if (it->argIndex == m_argIndex) {
			if ((it->type != wxT('n')) && (it->type != wxT('C')) && (it->type != wxT('S'))) {
				ProcessArgument<_Tp>(it, value);
			} else {
				wxFAIL_MSG(wxT("Not supported conversion type in format field: ") + it->result);
			}
		}
	}
	return *this;
}

// explicit instatiation for the types we handle
template CFormat& CFormat::operator%<double>(double);
template CFormat& CFormat::operator%<wxChar>(wxChar);
template CFormat& CFormat::operator%<signed long long>(signed long long);
template CFormat& CFormat::operator%<unsigned long long>(unsigned long long);
template CFormat& CFormat::operator%<const wxString&>(const wxString&);
template CFormat& CFormat::operator%<void *>(void *);

// File_checked_for_headers
