#pragma once

#ifdef ENABLE_APL_STRING 

#include <string.h>
#include <wchar.h>

// строки из Windows
typedef wchar_t* LPWSTR;
typedef const wchar_t* LPCWSTR;

// Строка Unicode
class APL_AGGR_API CaplStringW
{
protected:
    wchar_t* m_buffer;
	size_t m_length;	// количество символов в строке (именно в символах)
	size_t m_size;		// размер памяти под буфер в символах (это обычно кол-во симовлов + 1 завершающий символ '\0')

	// выделить память под буфер
	size_t SetSize(size_t new_size);

	void SetLength(size_t new_length);

public:

	enum
	{
		APLSTRERR_INCORRECT_LENGTH = 0		// некорректная длина строки
		, APLSTRERR_OUT_OF_MEMORY			// выход за пределы выделенной памяти
	};

	CaplStringW();
	CaplStringW(const CaplStringW& other);
	CaplStringW(const char* c_str);
	CaplStringW(const wchar_t* w_str);
    CaplStringW(wchar_t ch, size_t nLength = 1);
/*
	CaplStringW(int iValue);
	CaplStringW(long lValue);
*/
	virtual ~CaplStringW();

	// оператор преобразования в C-строку
    operator LPCWSTR() const throw()
	{
		return m_buffer;
	}

	// очистить строку
	void Empty();

	// пустая ли строка
	inline bool IsEmpty() const { return 0 == m_length; }

	inline int GetLength() const { return (int)m_length; }

    inline LPCWSTR GetCString() const { return m_buffer; }

	// вернуть буфер (неконстантный - можно в него писать)
	//	- если nMinBufferLength>m_size, то буфер увеличивается
    LPWSTR GetBuffer(int nMinBufferLength = -1);
    LPWSTR GetBufferSetLength(int nNewLength);
	void ReleaseBuffer(int nNewLength = -1);

	CaplStringW& operator=(const CaplStringW& other);
#ifdef QSTRING_H
	inline CaplStringW& operator=(const QString& other)
	{
#ifdef __linux__
		*this=(const char*)other.toUtf8();
#else
		*this=(const wchar_t*)other.utf16();
#endif
		return *this;
	}
#endif 
	CaplStringW& operator=(const char* c_str);
	CaplStringW& operator=(char* c_str) { return operator=(const_cast<const char*>(c_str)); }
	CaplStringW& operator=(const wchar_t* w_str);
	CaplStringW& operator=(wchar_t* w_str) { return operator=(const_cast<const wchar_t*>(w_str)); }
    CaplStringW& operator=(wchar_t ch);

	// добавление строки в конец текущей
    CaplStringW& Append(LPCWSTR other, int nCount = -1);		// nCount - число добавляемых символов
	CaplStringW& operator+=(const CaplStringW& other);
#ifdef QSTRING_H
	CaplStringW& operator+=(const QString& other)
	{
		CaplStringW tmp;
		tmp = other;
		*this+=tmp;
		return *this;
	}
#endif
    CaplStringW& operator+=(LPCWSTR other);
    CaplStringW& operator+=(wchar_t ch);

	// форматированный вывод на основе sprintf
    int Format(LPCWSTR _format, ...);

	CaplStringW Tokenize(LPCWSTR pszTokens, int& iStart);

	// операторы сравнения
    inline bool operator<(const CaplStringW& other) const;
    inline bool operator<(LPCWSTR pcOther) const;

    inline bool operator<=(const CaplStringW& other) const;
    inline bool operator<=(LPCWSTR pcOther) const;

    inline bool operator>(const CaplStringW& other) const;
    inline bool operator>(LPCWSTR pcOther) const;

    inline bool operator>=(const CaplStringW& other) const;
    inline bool operator>=(LPCWSTR pcOther) const;

	inline friend bool operator==( const CaplStringW& str1, const CaplStringW& str2 ){return 0 == str1.Compare(str2);}
	inline friend bool operator==(	const CaplStringW& str1, LPCWSTR psz2 ) {return 0 == str1.Compare(psz2);}
	inline friend bool operator==(	LPCWSTR psz1, const CaplStringW& str2 ) {return 0 == str2.Compare( psz1 );}
	inline friend bool operator==(LPWSTR psz1, const CaplStringW& str2 )	{return 0== str2.Compare( psz1 );}
	inline friend bool operator==( wchar_t ch1, const CaplStringW& str2 )	{return ( (str2.GetLength() == 1) && (str2[0] == ch1) );}
	inline friend bool operator==( const CaplStringW& str1, wchar_t ch2 )	{return ( (str1.GetLength() == 1) && (str1[0] == ch2) );}

	inline friend bool operator!=( const CaplStringW& str1, const CaplStringW& str2 ){return 0 != str1.Compare(str2);}
	inline friend bool operator!=(	const CaplStringW& str1, LPCWSTR psz2 ) {return 0 != str1.Compare(psz2);}
	inline friend bool operator!=(	LPCWSTR psz1, const CaplStringW& str2 ) {return 0 != str2.Compare( psz1 );}
	inline friend bool operator!=(LPWSTR psz1, const CaplStringW& str2 )	{return 0 != str2.Compare( psz1 );}
	inline friend bool operator!=( wchar_t ch1, const CaplStringW& str2 )	{return !( (str2.GetLength() == 1) && (str2[0] == ch1) );}
	inline friend bool operator!=( const CaplStringW& str1, wchar_t ch2 )	{return !( (str1.GetLength() == 1) && (str1[0] == ch2) );}
	
	
	// функции сравнения
	// возвращаемое значение:
	//	<0	- *this < other
	//	>0	- *this > other
	//	0	- *this == other
	int Compare(LPCWSTR other, bool bCaseSensitive = true) const;
	int CompareNoCase(LPCWSTR other) const { return Compare(other, false); }

	// конкатенация двух строк
	friend APL_AGGR_API CaplStringW operator+(const CaplStringW& str1, const CaplStringW& str2);
#ifdef QSTRING_H
	friend APL_AGGR_API CaplStringW operator+(const CaplStringW& str1, const QString& str2)
	{
		CaplStringW sResult = str1;
		sResult += str2;
		return sResult;
	}
#endif
    friend APL_AGGR_API CaplStringW operator+(const CaplStringW& str, wchar_t ch);
    friend APL_AGGR_API CaplStringW operator+(wchar_t ch, const CaplStringW& str);

	wchar_t operator[](int index);

	wchar_t GetAt( int iChar ) const;
	void SetAt(int iChar, wchar_t ch );

	// Удаление nCount символов начиная с iIndex
	//	- возвращает длину измененной строки
	int Delete(size_t iIndex, size_t nCount = 1);

	// Поиск символа/подстроки
	//	- возвращает индекс первого найденного вхождения, либо -1 если вхождение не найдено
    int Find(LPCWSTR psSub, size_t iStart = 0) const;
    int Find(wchar_t ch, size_t iStart = 0) const;

	// Поиск символа начиная с конца строки
    int ReverseFind(wchar_t ch) const;

	// Извлечь nCount самых левых символов
	//	- возвращает копию извлеченной подстроки
	CaplStringW Left(int nCount) const;

	// Извлечь nCount самых правых символов
	//	- возвращает копию извлеченной подстроки
	CaplStringW Right(int nCount) const;

	// Преобразует все символы в нижний регистр (требует установленной локали)
	CaplStringW& MakeLower();

	// Преобразует в обратную строку (меняет символы местами)
	CaplStringW& MakeReverse();

	// Преобразует все символы в верхний регистр (требует установленной локали)
	CaplStringW& MakeUpper();

	// Извлекает подстроку символов начиная с позиции iFirst и до конца строки
	CaplStringW Mid(int iFirst) const;

	// Извлекает подстроку длины nCount символов начиная с позиции iFirst
	CaplStringW Mid(int iFirst, int nCount) const;

	// Удаляет все вхождения указанного символа в строке (возвращает число удаленных символов)
    int Remove(wchar_t chRemove);

	// Вставка подстроки/символа
	//	- iIndex - номер символа перед которым осуществляется вставка
	//	- возвращает новую длину строки
    int Insert(int iIndex, LPCWSTR pcInsert);
    int Insert(int iIndex, wchar_t chInsert);

	// Замена всех вхождений подстроки/символа на другую подстроку/символ
	// - возвращает количество замен
    int Replace(LPCWSTR pcOld, LPCWSTR pcNew);
    int Replace(wchar_t chOld, wchar_t chNew);

	// Функции обрезки
	CaplStringW& Trim(wchar_t chTarget);
	CaplStringW& Trim(LPCWSTR pszTargets);
	CaplStringW& Trim();

	CaplStringW& TrimLeft(wchar_t chTarget);
	CaplStringW& TrimLeft(LPCWSTR pszTargets);
	CaplStringW& TrimLeft();

	CaplStringW& TrimRight(wchar_t chTarget);
	CaplStringW& TrimRight(LPCWSTR pszTargets);
	CaplStringW& TrimRight();

	BOOL GetEnvironmentVariable(LPCWSTR pszVar );

#ifdef _MFC_VER
	// Загрузка строки из ресурсов
	bool LoadString(void* hInstance, unsigned int nId);
#endif
};

inline bool CaplStringW::operator<(const CaplStringW& other) const
{
    return wcscmp(m_buffer, other.GetCString()) < 0;
}

inline bool CaplStringW::operator<(LPCWSTR pcOther) const
{
    return wcscmp(m_buffer, pcOther) < 0;
}

inline bool CaplStringW::operator<=(const CaplStringW& other) const
{
    return wcscmp(m_buffer, other.GetCString()) <= 0;
}

inline bool CaplStringW::operator<=(LPCWSTR pcOther) const
{
    return wcscmp(m_buffer, pcOther) <= 0;
}

inline bool CaplStringW::operator>(const CaplStringW& other) const
{
    return wcscmp(m_buffer, other.GetCString()) > 0;
}

inline bool CaplStringW::operator>(LPCWSTR pcOther) const
{
    return wcscmp(m_buffer, pcOther) > 0;
}

inline bool CaplStringW::operator>=(const CaplStringW& other) const
{
    return wcscmp(m_buffer, other.GetCString()) >= 0;
}

inline bool CaplStringW::operator>=(LPCWSTR pcOther) const
{
    return wcscmp(m_buffer, pcOther) >= 0;
}

#endif
