#pragma once

#ifdef ENABLE_APL_STRING 

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

// строки из Windows
typedef char* LPSTR;
typedef const char* LPCSTR;

// Строка ANSI
class APL_AGGR_API CaplStringA
{
protected:
    char* 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,		// выход за пределы выделенной памяти
		APLSTRERR_NOT_SUPPORT_UTF8,     // функция не доработана для работы с UTF-8
		APLSTRERR_SUPPORT_MSVC_ONLY     // работает только в VisualStudio
	};

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

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

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

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

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

    inline LPCSTR GetCString() const { return m_buffer; }

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

	CaplStringA& operator=(const CaplStringA& other);
	CaplStringA& operator=(const char* c_str);
	CaplStringA& operator=(char* c_str) { return operator=(const_cast<const char*>(c_str)); }
	CaplStringA& operator=(const wchar_t* w_str);
	CaplStringA& operator=(wchar_t* w_str) { return operator=(const_cast<const wchar_t*>(w_str)); }
    CaplStringA& operator=(char ch);

	// добавление строки в конец текущей
    CaplStringA& Append(LPCSTR other, int nCount = -1);		// nCount - число добавляемых символов
	CaplStringA& operator+=(const CaplStringA& other);
    CaplStringA& operator+=(LPCSTR other);
    CaplStringA& operator+=(char ch);

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

	CaplStringA Tokenize(LPCSTR pszTokens, int& iStart);

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

	inline bool operator<=(const CaplStringA& other);
    inline bool operator<=(LPCSTR pcOther);

	inline bool operator>(const CaplStringA& other);
    inline bool operator>(LPCSTR pcOther);

	inline bool operator>=(const CaplStringA& other);
    inline bool operator>=(LPCSTR pcOther);

	inline friend bool operator==( const CaplStringA& str1, const CaplStringA& str2 ){return 0 == str1.Compare(str2);}
	inline friend bool operator==(	const CaplStringA& str1, LPCSTR psz2 ) {return 0 == str1.Compare(psz2);}
	inline friend bool operator==(	LPCSTR psz1, const CaplStringA& str2 ) {return 0 == str2.Compare( psz1 );}
	inline friend bool operator==(LPSTR psz1, const CaplStringA& str2 )	{return 0 == str2.Compare( psz1 );}
	inline friend bool operator==( char ch1, const CaplStringA& str2 )	{return( (str2.GetLength() == 1) && (str2[0] == ch1) );}
	inline friend bool operator==( const CaplStringA& str1, char ch2 )	{return( (str1.GetLength() == 1) && (str1[0] == ch2) );}

	inline friend bool operator!=( const CaplStringA& str1, const CaplStringA& str2 ){return 0 != str1.Compare(str2);}
	inline friend bool operator!=(	const CaplStringA& str1, LPCSTR psz2 ) {return 0 != str1.Compare(psz2);}
	inline friend bool operator!=(	LPCSTR psz1, const CaplStringA& str2 ) {return 0 != str2.Compare( psz1 );}
	inline friend bool operator!=(LPSTR psz1, const CaplStringA& str2 )	{return 0 != str2.Compare( psz1 );}
	inline friend bool operator!=( char ch1, const CaplStringA& str2 )	{return !( (str2.GetLength() == 1) && (str2[0] == ch1) );}
	inline friend bool operator!=( const CaplStringA& str1, char ch2 )	{return !( (str1.GetLength() == 1) && (str1[0] == ch2) );}

	// функции сравнения
	// возвращаемое значение:
	//	<0	- *this < other
	//	>0	- *this > other
	//	0	- *this == other
	int Compare(LPCSTR other, bool bCaseSensitive = true) const;
	int CompareNoCase(LPCSTR other) const { return Compare(other, false); }

	// конкатенация двух строк
	friend APL_AGGR_API CaplStringA operator+(const CaplStringA& str1, const CaplStringA& str2);
    friend APL_AGGR_API CaplStringA operator+(const CaplStringA& str, char ch);
    friend APL_AGGR_API CaplStringA operator+(char ch, const CaplStringA& str);

	char operator[](int index);

	char GetAt(int iChar ) const;

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

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

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

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

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

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

	// Преобразует в обратную строку (меняет символы местами)
#ifdef _MFC_VER   // Лень писать реализацию под UTF-8
	CaplStringA& MakeReverse();
#endif
	// Преобразует все символы в верхний регистр (требует установленной локали)
	CaplStringA& MakeUpper();

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

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

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

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

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

	// Функции обрезки
	CaplStringA& Trim(char chTarget);
	CaplStringA& Trim(LPCSTR pszTargets);
	CaplStringA& Trim();

	CaplStringA& TrimLeft(char chTarget);
	CaplStringA& TrimLeft(LPCSTR pszTargets);
	CaplStringA& TrimLeft();

	CaplStringA& TrimRight(char chTarget);
	CaplStringA& TrimRight(LPCSTR pszTargets);
	CaplStringA& TrimRight();

	BOOL GetEnvironmentVariable(LPCSTR pszVar );

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

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

inline bool CaplStringA::operator<(LPCSTR pcOther)
{
    return strcmp(m_buffer, pcOther) < 0;
}

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

inline bool CaplStringA::operator<=(LPCSTR pcOther)
{
    return strcmp(m_buffer, pcOther) <= 0;
}

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

inline bool CaplStringA::operator>(LPCSTR pcOther)
{
    return strcmp(m_buffer, pcOther) > 0;
}

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

inline bool CaplStringA::operator>=(LPCSTR pcOther)
{
    return strcmp(m_buffer, pcOther) >= 0;
}

#endif
