////////////////////////////////////////////////////////////////////////////////
// BGCPString declaration
// A class that stores a string and knows its codepage
// Copyright Bjoern Ganster
// Developed since 2011
////////////////////////////////////////////////////////////////////////////////

#pragma once

#include "BGBase.h"
#include "BGStringTemplates.h"

#include "wchar.h"

#ifdef USE_QT
#include <QString>
#endif

#include <vector>

#define FILE_NUMBER FILE_NO_BGCPSTRING_H

////////////////////////////////////////////////////////////////////////////////

size_t lengthIfOK(size_t len, size_t maxLen);

// Return number of bytes used by one character - code pages 932 to 950 still need testing
// A return value of 0 indicates an invalid encoding
size_t getCharSize(const void* chars, WORD cp, size_t maxLen);

// Get size of a terminator for a codepage
int getTerminatorSize(WORD cp);

// Test if word is zero-length, i.e. points to a terminator for the given code page
bool isTerminator(const char* word, WORD cp);

// Return number of bytes in string
size_t getCharCount(const char* word, size_t maxBytes, WORD cp);

// Return number of bytes used for the given number of characters
size_t countBytesForChars(const char* word, size_t charCount, WORD cp, int maxLen);

////////////////////////////////////////////////////////////////////////////////

class BGCPString FINAL: public VirtualClass {
public:
	static const BGCPString EOL;
	//static const BGCPString OSPathSepStr;
	
	// Default constructor
	BGCPString();

	// Construct from single ASCII character
	BGCPString(char c);

	// Construct from a string with known length
	// cp alone defines the type for str
	BGCPString(const void* str, size_t byteCount, WORD cp);

	// Construct from a string with unknown length
	// Only ASCII strings are allowed without stating a codepage ...
	BGCPString(const char* str);

	// Construct from a string with unknown length
	BGCPString(const void* str, WORD cp /*= CP_ASCII*/);

	// Copy constructor
	BGCPString(const BGCPString& other);

	// Move constructor
	BGCPString(BGCPString&& other);

	// Construct a string that represents an integer
	static BGCPString fromInt(int64_t num, int base = 10);

	// Destructor
	~BGCPString() OVERRIDE;

	// Check if the string is empty
	bool empty() const;

	// Get string length in terms of bytes
	static size_t getByteCount(const char* str, WORD cp, size_t maxLen);

	// Assign from other BGCPString
	void operator=(const BGCPString& str);

	// Assign move from other BGCPString
	void operator=(BGCPString&& other);

	// Compare to other BGCPString
	bool operator==(const BGCPString& other) const;
	bool operator!=(const BGCPString& other) const;

	// Copy part of a string
	bool assign(const void* str, size_t byteCount, WORD cp);

	void terminateStr(size_t strEnd);

	// Set length of string and add terminator
	// Does not check whether a new character starts at the given offset!
	void setByteCount(size_t newLen);

	// Clear string
	void clear();

	// Add a character to the current string
	// Returns the number of bytes used by the character, 0 implies an error
	// charP: pointer to the character
	// codepage: codepage currently used by charP
	// maxLen: maximum length of charP in bytes
	size_t add(const void* charP, WORD codepage = CP_ASCII, size_t maxLen = 1);

	// Append part of a string
	// todo: quite similar to add in functionality, apart from the parameter order
	bool appendPart(const char* str, size_t byteCount, WORD cp);

	// Make sure buffer is large enough to hold newSize bytes
	void resizeBuffer(size_t newSize);

	// Append another BGCPString
	bool operator+=(const BGCPString& other);

	// Create new BGCPString by string concatenation
	BGCPString operator+(const BGCPString& other) const;

	// Convert to codepage
	bool convertToCP(WORD cp);

	// Convert string to a double
	double toDouble() const;

	// Get codepage currently used by the internal representation
	WORD getCP() const;

	// Get current internal representation of the string
	template <typename C>
	const C* get() const
	{
		return (C*) m_chars;
	}

	// Get a pointer to the internal buffer, with the important difference
	// that this pointer allows to manipulate its contents (non-const)!
	template <typename C>
	C* getPtr(size_t byteOffset = 0)
	{
		if (byteOffset < getByteCount()) {
			return (C*)(m_chars + byteOffset); // do not remove these brackets ...
		} else {
			return nullptr;
		}
	}
	// Get pointer to a character (const)
	template <typename C>
	const C* getPtr(size_t byteOffset = 0) const
	{
		if (byteOffset + sizeof(C) <= getByteCount()) {
			return (C*)(m_chars + byteOffset);
		} else {
			return nullptr;
		}
	}

	// Get number of bytes used by current internal string representation
	// The returned value does not include the terminating zero
	size_t getByteCount() const;

	// Use table to encode all characters in this string
	void transform (const char16_t* table, size_t tableSize);

	// Get number of characters in current string
	size_t getCharCount() const;

	// Cut off string at specified number of characters
	void setCharCount(size_t newCount);

	// Load string from file
	bool loadFromFile(const PathChar* fn);

	// Print this string to the console
	void print() const;

	// Print this string to the console and add a line feed
	void printLn() const;

	bool saveToFile(const PathChar* fn, WORD cp) const;

	// Get a representation of this string that is composed of characters as used for specifying file names on this system
	const PathChar* getAsPath();

	// Append windows error message to string
	#ifdef _WIN32
	static BGCPString getOSError(DWORD winErrCode = GetLastError());
	#else
	static BGCPString getOSError(int code);
	#endif

	// Return the integer represented by the string
	int64_t toInt(int base = 10) const;

	// Create a string that represents a double
	static BGCPString fromDouble(double d, int precision = 3);

	// Add path separator to the end of the string
	void addPathSep();

	// Add str with a path separator
	void addPathSep(const BGCPString& str);
	void addPathSep(const BGCPString& str1, const BGCPString& str2);

	static BGCPString sizeStr(int64_t size);

	// Check if string begins with given prefix
	bool startsWith(const BGCPString& other) const;

	template <typename C>
	C get(size_t pos) const
	{
		if (pos+sizeof(char) <= m_byteCount) {
			return *((const C*)(m_chars+pos));
		} else {
			return 0;
		}
	}

	// Scan this string for searchStr
	// Returns either the byte offset of the first match for c 
	// past startPos or endPos if nothing is found.
	// The size of a character is often not fixed, therefore even a single
	// character should be treated as a string, so it does not make sense
	// to offer a function that looks for a single character ...
	// Keep in mind that codepage conversions can invalidate the indexes ...
	size_t scan(const BGCPString& searchStr, size_t startPos, size_t endPos) const;

	// Find other in this string
	// Duplicate of scan, above?
	//bool find (const BGCPString& other, size_t& pos); // const;

	// Check if this string ends with another string
	bool endsWith(const BGCPString& other) const;

	// Get number of bytes used by character at given byte offset
	int getCharSize(size_t byteOffset) const;

	// Count number of occurrences of c in this string 
	// todo: allow c to be a template type, allow multibyte c
	size_t count(char c) const;
	
	// Check if the character at offset (interpreted as a byte offset) matches c
	bool charAtEquals(size_t offset, char c) const;

	// Return the offset of the first occurrence of c if the string contains c, or -1 otherwise
	// similar to scan, above?
	template <typename C>
	int findChar(C c) const
	{
		SET_SCOPE;
		int offset = 0;
		while (offset < (int)m_byteCount) {
			if (charAtEquals(offset, c)) {
				RETURN offset;
			}
			offset += getCharSize(offset);
		}
		
		RETURN -1;
	}

	// Return the offset of the last occurrence of c if the string contains c, or -1 otherwise
	int findLastChar(char c) const;

	// Return part of this string as a copy. Start and count are byte offsets into the source string.
	BGCPString copyPart(size_t start, size_t count) const;

	// Append other to this string if this string is not empty
	// Useful to generate lists
	void appendIfNotEmpty(const BGCPString& other);

	// Convert string to lower case ASCIIupdateMercsContracts
	BGCPString lowerStringASCII() const;

	// Advance from pos by count characters and return the result
	size_t forward(size_t pos, size_t count) const;

	// Remove count characters from end of string
	bool removeCharsFromEnd(size_t count);

	#ifdef USE_QT
	QString toQString() const;
	#endif

	// Split this string into several strings, one for each line
	std::vector<BGCPString> split(const char* splitChars, bool addEmpties) const;

	// Return a string that equals this string minux all preceeding or trailing whitespace
	BGCPString removeWhitespace() const;

	// Advance the position given by pos by count characters and return the new byte offset
	size_t advance(size_t pos, size_t count) const;

	// Test if the ASCII chars in other are equal to this string, disregarding case
	bool equalsNoCase(const BGCPString& other) const;

	// Return a new string where every occurrence of searhcStr was replaced with replaceStr
	BGCPString replace(const BGCPString& searchStr, const BGCPString& replaceStr);

private:
	static const size_t internalBufSize = 32;
	char* m_chars;
	char m_internalBuf[internalBufSize];
	WORD m_codepage;

	// Amount used of the buffer in bytes, not including the terminator
	size_t m_byteCount;

	// Size of the buffer in bytes, including the terminator
	size_t m_reserved;

	size_t m_terminatorSize;
};

#ifdef _WIN32
#define PATH_SEP_STR BGCPString("\\", CP_SYSTEM)
#else
#define PATH_SEP_STR BGCPString("/", CP_SYSTEM)
#endif

class BGException {
public:
	BGException(const BGCPString& str) 
	: mMsg(str)
	{
	}

	void printLn()
	{
		mMsg.printLn();
	}
private:
	BGCPString mMsg;
};

#define CONDITIONAL_THROW_WITH_BACKTRACE(condition, msg) \
if (condition) { \
	throw BGException(msg); \
}

// Get translated string
// Implemented in BGBAse.cpp ...
BGCPString tr (size_t strNum);

// Get directory part of path
BGCPString getDirectory(const BGCPString& directory);

// Get file name (last part of path)
BGCPString getFilename(const PathChar* path);

////////////////////////////////////////////////////////////////////////////////
// Logging: for ArchiveAccess, use aaSetError instead
// todo: maybe change this later? Requires construction of an appropriate 
// MessagListener in aaInit. Does a method aaInit exist?

void log(const BGCPString& str);
void logNoLF(const BGCPString& str);

template <typename C>
void log(const C* str)
{ log(BGCPString(str)); }

template <typename T1, typename T2>
void log (const T1& v1, const T2& v2)
{ log(BGCPString(v1) + BGCPString(v2)); }

/*template<typename T1, typename T2, typename... Args>
void log(const T1& v1, const T2& v2, Args... args) {
	log(BGCPString(v1) + BGCPString(v2), args...);
}*/

void setLogListener(MessageListener* newListener);

////////////////////////////////////////////////////////////////////////////////
// Get/set execution directory (singleton)

// Set exec dir from string
// The only relative path currently supported is .
void setDataDir (const BGCPString& newDir, bool parse);

// Set exec dir from current directory
void setDataDir();

const BGCPString& getDataDir();
//void getDosBoxCDir (BGFilePath& str);
bool isRelativePath (const PathChar* path);

#undef FILE_NUMBER
