#include "stdafx.h"
#include "aplAggr.h"

#ifdef ENABLE_APL_STRING

#include <stdarg.h>  // Для Format

#ifdef _MFC_VER
	#ifdef _DEBUG
		#define new DEBUG_NEW
	#endif
#endif


//Возвращает кол-во символов в строке UTF8
int aplUtf8StrLen(const char *str)
{
    if(0==str) return 0;
    if('\0'==str[0]) return 0;

    int iCount=0;
    size_t i = 0;
    while(true)
    {
        unsigned char c=str[i];
        if(0==c) break;
        if(0==(c&0x80)) i++; // англ. буква 1 байт
        else if(0xc0==(c&0xe0)) i+=2; // тут 2 байта
        else if(0xe0==(c&0xf0)) i+=3; // тут 3 байта
        else if(0xf0==(c&0xf8)) i+=4; // тут 4 байта
        else break; // это кривой символ
        iCount++;
    }
    return iCount;
}

//Возвращает указатель на n-ный символ в строке UTF8
char *aplUtf8GetPosNSymbol(const char *str, const int n)
{
    if(0==str) return 0;
    if('\0'==str[0]) return 0;
    if(n<0) return 0;
    if(0==n) return (char*)str;

    int iCount=0;
    size_t i = 0;
    while(true)
    {
        unsigned char c=str[i];
        if(0==c) break;
        if(0==(c&0x80)) i++; // англ. буква 1 байт
        else if(0xc0==(c&0xe0)) i+=2; // тут 2 байта
        else if(0xe0==(c&0xf0)) i+=3; // тут 3 байта
        else if(0xf0==(c&0xf8)) i+=4; // тут 4 байта
        else break; // это кривой символ
        iCount++;
        if(iCount==n) return (char*)(str+i);
    }
    return 0;
}



//*** Функции преобразования регистра
inline void aplUpperWchar(wchar_t &c)
{
    if(c==L'\0') return;
    if( (c>=L'a') && (c<=L'z')) c-= (L'z' - L'Z');
    else if( (c>=L'а') && (c<=L'я')) c-= (L'я' - L'Я');
    else if(c==L'ё') c=L'Ё';
};

inline void aplLowerWchar(wchar_t &c)
{
    if(c==L'\0') return;
    if( (c>=L'A') && (c<=L'Z')) c+= (L'z' - L'Z');
    else if( (c>=L'А') && (c<=L'Я')) c+= (L'я' - L'Я');
    else if(c==L'Ё') c=L'ё';
};

int aplCompareWStringIgnoreCase(const wchar_t *s1, const wchar_t *s2)
{
	if(0==s1)
	{
		if(0==s2)  return 0;
		if(s2[0]==L'\0')  return 0;
		return -1;
	}
	if(0==s2)  return 1;

	size_t i=-1;
	while(true)
	{
		i++;
		wchar_t c1=s1[i];
		wchar_t c2=s2[i];

		if(c1==c2)
		{
			if(c1==L'\0') return 0;
			continue;
		}
		if(c2==L'\0') return 1; // other короче

		aplUpperWchar(c1);
		aplUpperWchar(c2);

		if(c1==c2) continue;
		if(c1<c2) return -1;
		return 1;
	}
	if(s2[i]!=L'\0') return -1; // other длиннее
	return 0;
};

//**************************************************************

CaplStringW::CaplStringW() :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
	SetSize(m_length + 1);
	m_buffer[m_length] = L'\0';
}

CaplStringW::CaplStringW(const CaplStringW& other) :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
	*this = other;
}

CaplStringW::CaplStringW(const char* c_str) :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
	*this = c_str;
}

CaplStringW::CaplStringW(const wchar_t* w_str) :
	m_buffer(NULL)
	, m_length(0)
	, m_size(0)
{
	*this = w_str;
}

CaplStringW::CaplStringW(wchar_t ch, size_t nLength /*= 1*/) :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
    SetSize(nLength + 1);
    for (size_t i = 0; i < nLength; i++) m_buffer[i] = ch;
	m_length = nLength;
    m_buffer[m_length] = L'\0';
}

/*
CaplStringW::CaplStringW(int iValue)
{
	Format("%d", iValue);
}

CaplStringW::CaplStringW(long lValue)
{
	Format("%d", lValue);
}
*/

CaplStringW::~CaplStringW()
{
	if (m_buffer)
	{
		delete[] m_buffer;
		m_buffer=0;
	}
}

LPWSTR CaplStringW::GetBuffer(int nMinBufferLength /*= -1*/)
{
	if (nMinBufferLength > (int)m_size)
		SetSize(nMinBufferLength);

	return m_buffer;
}

LPWSTR CaplStringW::GetBufferSetLength(int nNewLength)
{
	GetBuffer(nNewLength+1);
	SetLength(nNewLength);
	return m_buffer;
}

void CaplStringW::ReleaseBuffer(int nNewLength /* = -1 */)
{
	if (nNewLength > -1)
		SetLength(nNewLength);
}

void CaplStringW::SetLength(size_t new_length)
{
	if(m_length==new_length) return;
	if ((new_length+1)  > m_size) SetSize(new_length+1);
	m_length = new_length;
    m_buffer[m_length] = L'\0';
}

size_t CaplStringW::SetSize(size_t new_size)
{
	if (new_size == 0) // удаление строки
	{
        if(0==m_length && 1==m_size) return m_size;
        if (m_buffer) delete[] m_buffer;
        m_length=0;
        m_buffer = new wchar_t[1];
        m_size=1;
        m_buffer[m_length] = L'\0';
        return m_size;
	}

	if(new_size==m_size) return m_size;
	if(new_size<(m_length+1)) new_size=(m_length+1);

	if(new_size<=m_size) return m_size; // Оставляем буффер, если оно больше

	m_size = new_size;
    wchar_t* new_buffer = new wchar_t[m_size];
	if (m_buffer)
    {
        if(0!=m_length) memcpy(new_buffer,m_buffer,m_length*sizeof(wchar_t));
        new_buffer[m_length] = L'\0';
        delete[] m_buffer;
    }
	m_buffer = new_buffer;
	return m_size;
}

void CaplStringW::Empty()
{
    SetSize(0);
}

CaplStringW& CaplStringW::operator=(const CaplStringW& other)
{
	if(this==&other) return *this; // На случай присваивания себя себя
    *this=other.GetCString();
	return *this;
}


CaplStringW& CaplStringW::operator=(const wchar_t* w_str)
{
    if(0==w_str || L'\0'==w_str[0]) {Empty(); return *this;}

	if(m_buffer==w_str) return *this;  // На случай присваивания себя себя

    m_length = 0;
	size_t l=wcslen(w_str);
    SetSize(l + 1);
    memcpy(m_buffer, w_str, l*sizeof(wchar_t));
	m_length=l;
    m_buffer[m_length] = _T('\0');
    return *this;
}

CaplStringW& CaplStringW::operator=(wchar_t ch)
{
    m_length = 0;
    SetSize(2);
	m_length = 1;
    m_buffer[0] = ch;
    m_buffer[m_length] = L'\0';
    return *this;
}

CaplStringW& CaplStringW::operator=(const char* c_str)
{
    if(0==c_str || '\0'==c_str[0]) {Empty(); return *this;}

#ifndef __GNUC__

	int lw = ::MultiByteToWideChar(CP_ACP, 0, c_str, -1, NULL, 0);
	
	if(lw <= 0) Empty();
	else
	{
		SetSize(lw + 1);
		int len2= ::MultiByteToWideChar(CP_ACP, 0,  c_str, -1, m_buffer, m_size);

		if(len2<=0) Empty();
		else 
		{
			m_length=len2-1;
			m_buffer[m_length] = L'\0'; // На всякий случай
		}
	}

#else

    int la = strlen(c_str)+1;
	BYTE *out_buf=0;
	int out_buf_len=0, out_buf_size=0;
	bConvertTextWithIconv(aplCurCharCode,(BYTE*)c_str,la,aplCurWcharCode,out_buf,out_buf_len, &out_buf_size);

	if(0!=out_buf)
	{
		if(m_buffer)delete [] m_buffer;
		m_buffer=(wchar_t*)out_buf;
		m_size=(int)(out_buf_size/sizeof(wchar_t));
		m_length=wcslen(m_buffer); // Можно использовать (int)(out_buf_len/sizeof(wchar_t)) - 1; но так надёжнее
	}
	else Empty();

#endif

	return *this;
}


CaplStringW& CaplStringW::operator+=(const CaplStringW& other)
{
	return *this += other.GetCString();
}


CaplStringW& CaplStringW::Append(LPCWSTR other, int nCount /* = -1 */)
{
    size_t length_other = wcslen(other);
	size_t nC = nCount < 0 ? length_other : nCount;

    if (nC > length_other) 	nC = length_other;
    if (nC > 0)
	{
		size_t old_length = m_length;
		SetSize(m_length + nC+ 1);
		m_length += nC;
        LPWSTR pNewStr = m_buffer + old_length;
        memcpy(pNewStr, other, nC*sizeof(wchar_t));
		m_buffer[m_length] = L'\0';
	}
	return *this;
}

CaplStringW& CaplStringW::operator+=(LPCWSTR other)
{
	if(0==other) return *this;
	if(L'\0'==other[0]) return *this;

	if(other==m_buffer) // Добавление себя к себе
	{
		// Ситуация редкая, поэтому с оптимальностью не заморачиваемся
		CaplStringW sTmp(*this);
		return (*this)+=sTmp;
	}

    size_t length_other = wcslen(other);
	if (length_other > 0)
	{
		size_t old_length = m_length;
		SetSize(m_length + length_other +1);
		m_length += length_other;
		LPWSTR pNewStr = m_buffer + old_length;
        memcpy(pNewStr, other, length_other*sizeof(wchar_t));
		m_buffer[m_length] = L'\0';
	}
	return *this;
}


CaplStringW& CaplStringW::operator+=(wchar_t ch)
{
    // Чаще всего эта функция используется при большом кол-ве добавлений.
    // Поэтому лучше сразу выделить буффер побольше
    if((m_length + 2)>m_size) SetSize(m_length + 16);

    m_buffer[m_length] = ch;
    m_length++;
    m_buffer[m_length] = L'\0';
	return *this;
}

int CaplStringW::Format(LPCWSTR _format, ...)
{
    if(0==_format) return 0;
	if(_T('\0')==_format[0]) return 0;
	
	// преобразуем %s в %S, если надо
#ifndef _MFC_VER
	LPWSTR pFormat2=0;
	{
		int i=0;
		bool bNeedConvert=false;
		while(_format[i]!=_T('\0'))
		{
			if(_format[i]==_T('%'))
			{
				if(_format[i+1]==_T('s'))  {bNeedConvert=true; break;};
			}
			i++;
		}
		if(bNeedConvert)
		{
			int l=wcslen(_format);
			pFormat2=new TCHAR[l+1];
			for(i=0;i<=l;i++)
			{
				pFormat2[i]=_format[i];
				if(_format[i]!=_T('%')) continue;
				if(_format[i+1]!=_T('s'))  continue;
				i++;
				pFormat2[i]=_T('S');
			}
			_format=pFormat2;
		}
	}
#endif

    int iRes = -1;
	int bufLen = APL_MAX_STRING_LENGTH;
	LPWSTR pBuf = 0;
#ifdef _MFC_VER
    int maxBufLen = MAXLONG32/2;
#else
    int maxBufLen = MAX_INT32/2;
#endif

    while(true)
    {
		if(pBuf != 0) delete[] pBuf;
        pBuf = new wchar_t[bufLen];

        va_list marker;
        va_start(marker, _format);	// устанавливаем маркер на 1-й неименованный параметр
        // передаем список неименованных параметров аналогу функции sprintf и пишем в наш буфер
        iRes = vswprintf(pBuf, bufLen, _format, marker);
        va_end(marker);				// очищаем

		if(iRes == -1)
		{
#if defined(_MFC_VER) || defined(__linux__)
            // Микрософтовский vswprintf возвращает -1 при любой ошибке, даже если буфера не хватило.
            // Линуксовый тоже
            // Увеличиваем буфер.
			bufLen *= 4;
            if((UINT)bufLen > (UINT)maxBufLen)
			{
                *this = _T("<Error: arguments too long for memory.>");
				break;
			}
			continue;
#else
            // Под виндой GNUшный  vswprintf возвращает -1 только при критической ошибке. Получать wchar_t строку с ошибкой поленился.
            *this = _T("<Error>");
            break;
#endif
		}
#ifdef __linux__
        if(iRes > 0)
        {
            *this = pBuf;
            break;
        }
#else
        if(iRes + (int)sizeof(wchar_t) <= bufLen)
        {
            *this = pBuf;
            break;
        }
        else
        {
            // Под виндой GNUшный  vswprintf возвращает размер буфера, нужного для операции
            bufLen = iRes + sizeof(wchar_t);
            if((UINT)bufLen > (UINT)maxBufLen)
            {
                *this = _T("<Error: arguments too long for memory.>");
                break;
            }
        }
#endif
    }

#ifndef _MFC_VER
	if(0!=pFormat2) delete[] pFormat2;
#endif
	if(pBuf != 0) delete[] pBuf;

	return iRes;
}

int CaplStringW::Compare(LPCWSTR other, bool bCaseSensitive /*= true*/) const
{
	if(0==m_buffer && 0==other) return 0;
	if(0!=m_buffer && 0==other) return 1;
	if(0==m_buffer && 0!=other) return -1;

	if (bCaseSensitive)	return wcscmp(m_buffer, other);
	
	//#ifdef _MFC_VER
	//       return _wcsicmp(m_buffer, other);
	//#else
		// return wcscasecmp(m_buffer, other);	 
	//#endif

	// ЯАИ _wcsicmp и wcscasecmp некорректно работают с русскими буквами
	// нормально работающего варианта найти не осилил
	// Сделал 'в лоб'. (Аналогично MakeLower и MakeUpper)

	if(0==m_length)
	{
		if(0==other)  return 0;
		if(other[0]==L'\0')  return 0;
		return -1;
	}
	if(0==other)  return 1;

	return aplCompareWStringIgnoreCase(m_buffer, (wchar_t*)other);
}


int CaplStringW::Delete(size_t iIndex, size_t nCount /* = 1 */)
{
	if ((int)iIndex<0) iIndex=0;
	if ( 0==nCount || 0==m_length || iIndex >= m_length ) return m_length;

    if(iIndex + nCount > m_length) nCount =m_length - iIndex;

    for(size_t i=iIndex; i<=(m_length-nCount); i++)
	{
        m_buffer[i] = m_buffer[i + nCount];
	}

    m_length -= nCount;
	m_buffer[m_length] =L'\0';  //На всякий случай
    return nCount;
}

// конкатенация двух строк
CaplStringW operator+(const CaplStringW& str1, const CaplStringW& str2)
{
	CaplStringW sResult = str1;
	sResult += str2;
	return sResult;
}

CaplStringW operator+(const CaplStringW& str, wchar_t ch)
{
	CaplStringW sResult = str;
	sResult += ch;
	return sResult;
}

CaplStringW operator+(wchar_t ch, const CaplStringW& str)
{
	CaplStringW sResult = ch;
	sResult += str;
	return sResult;
}


int CaplStringW::Find(LPCWSTR psSub, size_t iStart /* = 0 */) const
{
	if(0==m_length) return -1;
	if (iStart >= m_length)	return -1;

	if(0==psSub) return 0;
	if(L'\0'==psSub[0]) return 0;

	if ((int)iStart<0) iStart=0;

	size_t iSubLength = wcslen(psSub);
	if (iSubLength > m_length) return -1;
	size_t nLimit = m_length - iSubLength + 1;	// последний возможный индекс в строке, чтобы можно было вместить искомую подстроку
	size_t i = iStart;
	wchar_t cf=psSub[0];

	while (i<nLimit)
	{
		if (cf == m_buffer[i])
		{
			bool bEqu=true;
			for(size_t j=1; j<iSubLength;j++)
			{
				if(m_buffer[i+j]!=psSub[j]) {bEqu=false; break;}
			}
			if(bEqu) return i;
		}
		i++;
	}
	return -1;
}

int CaplStringW::Find(wchar_t ch, size_t iStart /* = 0 */) const
{
	if(0==m_length) return -1;
	if (iStart >= m_length)	return -1;
	if ((int)iStart<0) iStart=0;

    for (size_t i = iStart; i < m_length; i++)
	{
        if (ch == m_buffer[i]) return i;
	}
	return -1;
}

int CaplStringW::ReverseFind(wchar_t ch) const
{
    for (int i = m_length - 1; i >= 0; i--)
	{
        if (ch == m_buffer[i]) return i;
	}
	return -1;
}

CaplStringW CaplStringW::Left(int nCount) const
{
	if(0==m_length) return *this;
	if(nCount>=(int)m_length) return *this;
	CaplStringW sLeft;
	if (nCount>0) sLeft.Append(m_buffer, nCount);
	return sLeft;
}

CaplStringW CaplStringW::Right(int nCount) const
{
	if(0==m_length) return *this;
	if(nCount>=(int)m_length) return *this;
	CaplStringW sRight;
	if(nCount>0) sRight.Append((m_buffer + (m_length - nCount)), nCount);
	return sRight;
}

CaplStringW CaplStringW::Mid(int iFirst ) const
{
	return( Mid( iFirst, m_length-iFirst) );
}

CaplStringW CaplStringW::Mid(int iFirst, int nCount) const
{
    if(0==m_length) return *this;
	if (iFirst < 0)  iFirst = 0;

	CaplStringW sMid; 
	if (nCount <= 0)  return sMid;

	if( iFirst >= (int)m_length ) nCount = 0;
	if( (iFirst+nCount) > (int) m_length ) nCount = (int)m_length-iFirst;
	if(0==iFirst && nCount == (int)m_length) return( *this );

	if(nCount>0)
	{
		LPCWSTR pMidBuffer = m_buffer + iFirst;
		sMid.Append(pMidBuffer, nCount);
	}
	return sMid;
}

CaplStringW& CaplStringW::MakeReverse()
{
    wchar_t *p1, *p2;

    if (!m_buffer || !*m_buffer)    return *this;

    for (p1 = m_buffer, p2 = m_buffer + m_length - 1; p2 > p1; ++p1, --p2)
    {
        *p1 ^= *p2;
        *p2 ^= *p1;
        *p1 ^= *p2;
    }
	return *this;
}

CaplStringW& CaplStringW::MakeLower()
{
    if(0==m_length) return *this;

    // ЯАИ: под QT Creator не работает с русским языком
	// for (size_t i = 0; i < m_length; ++i) 	m_buffer[i] = towlower(m_buffer[i]);
	// Нормальной реализации не нашел. сделал в лоб  (аналогично и в MakeUpper())

    for (size_t i = 0; i < m_length; i++) aplLowerWchar(m_buffer[i]);
	return *this;
}

CaplStringW& CaplStringW::MakeUpper()
{
    if(0==m_length) return *this;

	// ЯАИ: под QT Creator не работает с русским языком
	// for (size_t i = 0; i < m_length; ++i) 	m_buffer[i] = towlower(m_buffer[i]);
	// Нормальной реализации не нашел. сделал в лоб  (аналогично и в MakeLower())

    for (size_t i = 0; i < m_length; i++) aplUpperWchar(m_buffer[i]);
	
	return *this;
}


int CaplStringW::Remove(wchar_t chRemove)
{
	int iFind(0), nCount(0);
	while (-1 < (iFind = Find(chRemove, iFind)))
	{
		Delete(iFind);
		++nCount;
	}

	return nCount;
}

int CaplStringW::Insert(int iIndex, LPCWSTR pcInsert)
{
	if(0==pcInsert) return m_length;
	if(L'\0'==pcInsert[0]) return m_length;
	if(iIndex<0) iIndex=0;
	else if(iIndex>(int)m_length)iIndex=m_length;

	int len2=wcslen(pcInsert);
	SetSize(m_length+len2+1);

	int i,iStart=iIndex;

	for(i=m_length; i>=iStart; i--)m_buffer[i+len2]=m_buffer[i];

	for(i=0;i<len2;i++) m_buffer[i+iStart]=pcInsert[i];

	m_length+=len2;
	//m_buffer[m_length]='\0';
	return m_length;
}

int CaplStringW::Insert(int iIndex, wchar_t chInsert)
{
	if('\0'==chInsert) return m_length;
	if(iIndex<0) iIndex=0;
	else if(iIndex>(int)m_length)iIndex=m_length;

	SetSize(m_length+2);

	int i,iStart=iIndex;
	for(i=m_length; i>=iStart; i--) m_buffer[i+1]=m_buffer[i];

	m_buffer[iStart]=chInsert;

	m_length++;
	//m_buffer[m_length]='\0';
	return m_length;
}

int CaplStringW::Replace(LPCWSTR pcOld, LPCWSTR pcNew)
{
	if(0==m_length) return 0;
    int nOldLength = wcslen(pcOld);
	if(0==nOldLength) return 0;
    int nNewLength = wcslen(pcNew);
	int iFind(0), nCount(0);
	while (-1 < (iFind = Find(pcOld, iFind)))
	{
		if ( nNewLength == nOldLength)
		{
			for (int i = 0; i < nOldLength; ++i)
			{
				m_buffer[iFind + i] = pcNew[i];
			}
		}
		else
		{
			//Delete(iFind, nOldLength);
			int i;
			for(i=iFind; i<=((int)m_length-nOldLength); i++) m_buffer[i] = m_buffer[i + nOldLength];
			m_length -= nOldLength;
			//Insert(iFind, pcNew);
			SetSize(m_length+nNewLength+1);
			for(i=m_length; i>=iFind; i--)m_buffer[i+nNewLength]=m_buffer[i];
			for(i=0;i<nNewLength;i++) m_buffer[i+iFind]=pcNew[i];

			m_length+=nNewLength;
		}
		iFind += nNewLength;
		++nCount;
	}

	return nCount;
}

int CaplStringW::Replace(wchar_t chOld, wchar_t chNew)
{
	if(0==m_length) return 0;
    int nCount=0;
    for(size_t i=0; i<m_length; i++) {
        if( chOld==m_buffer[i]) { m_buffer[i]=chNew; nCount++; }
    }
	return nCount;
}

#ifdef _MFC_VER

bool CaplStringW::LoadString(void * hInstance, unsigned int nId)
{
	if(0==hInstance || 0==nId) return false;

#ifdef _MFC_VER
	CString sMFCStr;
	bool bRet = sMFCStr.LoadString((HINSTANCE)hInstance, nId)?true:false;
	if (bRet)
		*this = sMFCStr;

#else 
	APL_STOP_IF_DEBUG  // Функция работает только в VisualStudio
	return false;
#endif

	return false;
}
#endif

wchar_t CaplStringW::operator[](int index)
{
	if (index < 0 || (size_t)index >= m_length)
	{
        //APL_STOP_IF_DEBUG // Выход за пределы массива
		APL_WRITE_TO_LOG(_T("CaplStringW::operator[] -> Выход за пределы массива"))
		return L'\0';
	}
	return m_buffer[index];
}

wchar_t CaplStringW::GetAt(int iChar ) const
{
	if (iChar < 0 || (size_t)iChar >= m_length)
	{
        //APL_STOP_IF_DEBUG // Выход за пределы массива
		APL_WRITE_TO_LOG(_T("CaplStringW::GetAt -> Выход за пределы массива"))
		return L'\0';
	}
	return m_buffer[iChar];
}


CaplStringW& CaplStringW::Trim(wchar_t chTarget)
{
	return (TrimRight(chTarget).TrimLeft(chTarget));
}

CaplStringW& CaplStringW::Trim(LPCWSTR pszTargets)
{
	return (TrimRight(pszTargets).TrimLeft(pszTargets));
}

CaplStringW& CaplStringW::Trim()
{
	return (TrimRight().TrimLeft());
}

CaplStringW& CaplStringW::TrimLeft(wchar_t chTarget)
{
    if(0==m_length) return (*this);

    size_t i;
    for(i=0; i<=m_length; i++)
    {
        if( m_buffer[i]!=chTarget) break;
    }
    if(0==i) return (*this);
    if(i==m_length) { Empty(); return (*this);}

    for(size_t j=i; j<=m_length; j++)
        m_buffer[j-i] = m_buffer[j];

    m_length-=i;
	return (*this);
}

bool aplFindWCharInString(LPCWSTR pszTargets, wchar_t chFind)
{
	bool bFind(false);
	for(size_t i=0; i<wcslen(pszTargets); ++i)
	{
		if (pszTargets[i]==chFind)
		{
			bFind = true;
			break;
		}
	}
	return bFind;
}

CaplStringW& CaplStringW::TrimLeft(LPCWSTR pszTargets)
{
	while(m_length>0 && aplFindWCharInString(pszTargets, m_buffer[0]))
	{
		--m_length;

		for(size_t i=0; i<m_length; ++i)
			m_buffer[i] = m_buffer[i+1];

		m_buffer[m_length] = L'\0';
	}

	return (*this);
}

CaplStringW& CaplStringW::TrimLeft()
{
	return TrimLeft(L' ');
}

CaplStringW& CaplStringW::TrimRight(wchar_t chTarget)
{
    if(0==m_length) return (*this);

    int i;
    for(i=m_length-1; i>=0; i--)
    {
        if( m_buffer[i]!=chTarget) break;
    }

    if(i<0) Empty();
    else if(i<(int)(m_length-1))
    {
		i++;
		 m_buffer[i]=L'\0';
		 m_length=(size_t)i;
    }
	return (*this);
}

CaplStringW& CaplStringW::TrimRight(LPCWSTR pszTargets)
{
	while(m_length>0 && aplFindWCharInString(pszTargets, m_buffer[m_length-1]))
	{
		m_buffer[m_length-1] = L'\0';
		--m_length;
	}

	return (*this);
}

CaplStringW& CaplStringW::TrimRight()
{
	return TrimRight(L' ');
}

int aplStringSpanIncludingW( LPCWSTR pszPlace, LPCWSTR pszTokens,  bool bInclude)
{
	int i=0;
	while(true)
	{
		wchar_t ci=pszPlace[i];
		if(ci==L'\0') return i;
		int j=0;
		bool bfound =false;
		while(true) 
		{
			wchar_t cj=pszTokens[j];
			if(cj==L'\0') break;
			if(ci==cj) {bfound=true; break;}
			j++;
		}
		if(bInclude)
		{
			if(!bfound) return i;
		}
		else
		{
			if(bfound) return i;
		}
		i++;
	} 
	ASSERT(FALSE);
	return -1; // По идее, этого быть не должно
}

CaplStringW CaplStringW::Tokenize(LPCWSTR pszTokens, int& iStart)
{
	if(0==m_buffer) {iStart=-1; return L"";}
	if(iStart < 0) return L"";

	ASSERT( iStart >= 0 );

	if( (pszTokens == NULL) || (*pszTokens == (TCHAR)0) )
	{
		if (iStart < (int)GetLength()) return Right(GetLength()-iStart);
	}
	else
	{
		wchar_t* pszPlace = m_buffer + iStart;
		wchar_t* pszEnd = m_buffer + GetLength();
		if( pszPlace < pszEnd )
		{
			//int nIncluding = StringTraits::StringSpanIncluding( pszPlace,	pszTokens );
			int nIncluding = aplStringSpanIncludingW( pszPlace,	pszTokens,true );

			if( (pszPlace+nIncluding) < pszEnd )
			{
				pszPlace += nIncluding;
				//int nExcluding = StringTraits::StringSpanExcluding( pszPlace, pszTokens );
				int nExcluding = aplStringSpanIncludingW( pszPlace, pszTokens,false );

				int iFrom = iStart+nIncluding;
				int nUntil = nExcluding;
				iStart = iFrom+nUntil+1;

				return Mid( iFrom, nUntil );
			}
		}
	}

	// return empty string, done tokenizing
	iStart = -1;
	return(L"");
}

BOOL CaplStringW::GetEnvironmentVariable(LPCWSTR pszVar )
{
	Empty();
	if(0==pszVar) return FALSE;
	if(L'\0'==pszVar[0]) return FALSE;


#ifdef _MFC_VER  

	/*wchar_t *env=_wgetenv(pszVar);
	if(0==env) return FALSE;
	if(L'\0'==env[0]) return FALSE;
	*this=env;*/

	size_t len=0;
	_wgetenv_s(&len,0,0,pszVar);
	if(len<=0) return FALSE;
	len++;
	SetSize(len);  
	_wgetenv_s(&len,m_buffer,len,pszVar);
	if(len>0)
	{
		m_length=len-1;
		m_buffer[m_length]=L'\0';
	}

#else

	char *env_a=getenv(CaplStringAdapter(pszVar));  // В Астре нет  _wgetenv
	if(0==env_a) return FALSE;
	if('\0'==env_a[0]) return FALSE;
#ifdef __linux__
	*this=env_a;
#else
    CaplStringAdapter str_ad(env_a);
    str_ad.m_bIsAnsiUtf8 = false;
    *this=(LPCWSTR)str_ad;
#endif

#endif

	return TRUE;
}

void CaplStringW::SetAt(int iChar, wchar_t ch )
{
	if (iChar < 0 || (size_t)iChar >= m_length)
	{
		APL_STOP_IF_DEBUG // Выход за пределы массива
		APL_WRITE_TO_LOG(_T("CaplStringW::GetAt -> Выход за пределы массива"))
		return;
	}

	m_buffer[iChar] = ch;
}

void CStringSplit(CString &str, TCHAR sep, CStringArray &sa)
{
	sa.RemoveAll();
	if(str.IsEmpty()) return;
	int pos_b(0), pos_e;
	do{
		pos_e=str.Find(sep, pos_b);
		if(pos_e==-1){if(str.Mid(pos_b).GetLength()>0){sa.Add(str.Mid(pos_b));}}
		else{ if(str.Mid(pos_b, pos_e-pos_b).GetLength()>0){ sa.Add(str.Mid(pos_b, pos_e-pos_b));}}
		pos_b=pos_e+1;
	}while(pos_e!=-1);
}

CString CStringJoin(CStringArray &sa, LPCTSTR sep)
{
	CString str(_T(""));
	for(int i=0; i<sa.GetSize(); i++)
	{
		if(sa.GetAt(i).IsEmpty())continue;
		if(!str.IsEmpty())str+=sep;
		str+=sa.GetAt(i);
	}
	return str;
}

#endif
