#include "stdafx.h"

// Слишком много Windows специфики. Под linux пока не нужен
#include "aplAggr.h"

#ifdef _MFC_VER
	#ifdef _DEBUG
		#define new DEBUG_NEW
	#endif
#elif __GNUC__
    #include <iconv.h>
#endif

static const BYTE BOM_UCS2[]= {0xFF,0xFE};         static const int BOM_LEN_UCS2 = 2;
static const BYTE BOM_UTF16BE[]={0xFE,0xFF};		static const int BOM_LEN_UTF16BE = 2;
static const BYTE BOM_UTF8[]={0xEF,0xBB,0xBF};     static const int BOM_LEN_UTF8 = 3;
static const BYTE BOM_UTF32BE[]={0x00,0x00,0xFE,0xFF};  static const int BOM_LEN_UTF32BE = 4;
static const BYTE BOM_UTF32LE[]={0xFF,0xFE,0x00,0x00};  static const int BOM_LEN_UTF32LE = 4;


const char *aplChar1251Code4iconv="CP1251";

#ifdef _MFC_VER
	const aplTextEncoding aplCurCharCode = aplANSI;
	const aplTextEncoding aplCurWcharCode = aplUCS2;
#else
	const aplTextEncoding aplCurCharCode = aplUTF8;

	#ifdef __linux__
		const aplTextEncoding aplCurWcharCode =aplUTF32LE;
	#else
		const aplTextEncoding aplCurWcharCode =aplUCS2;
	#endif
#endif

inline bool bom_compare(const BYTE *bom, int bom_len, const BYTE *data) // Считаем, что на конце BOM есть 0x00
{
	if(0==bom_len) return true;
	ASSERT(0!=bom && 0!=data && ( 0==bom_len || ( bom_len>=2 && bom_len<=4)));
	int i=0;
	for(i=0; i<bom_len; i++)
	{
		if(bom[i]!=data[i]) return false;
	}
	return true;
}

aplTextEncoding aplGetBufferEncodeByBOM( const BYTE *pSrcBuf, int pSrcBufLen, int *pBomLen)
{
	if(0!=pBomLen) *pBomLen=0;

	if(0==pSrcBuf || 0== pSrcBufLen) return aplUnknownEncoding;
	if(pSrcBufLen<3) return aplANSI;

	if(bom_compare(BOM_UCS2,BOM_LEN_UCS2,pSrcBuf)) 
	{
		// У UCS2 и UTF32LE первые 3 байта одинаковые
		if(pSrcBufLen>=4)
		{
			if(pSrcBuf[2]==BOM_UTF32LE[2] && pSrcBuf[3]==BOM_UTF32LE[3])
			{
				if(0!=pBomLen) *pBomLen=BOM_LEN_UTF32LE;
				return aplUTF32LE;
			}
		}
		if(0!=pBomLen) *pBomLen=BOM_LEN_UCS2;
		return aplUCS2; 
	}

	if(bom_compare(BOM_UTF16BE, BOM_LEN_UTF16BE, pSrcBuf))
	{
		if(0!=pBomLen) *pBomLen=BOM_LEN_UTF16BE;
		return aplUTF16BE;
	}

	if(pSrcBufLen>=3)
	{
		if(bom_compare(BOM_UTF8, BOM_LEN_UTF8, pSrcBuf))
		{
			if(0!=pBomLen) *pBomLen=BOM_LEN_UTF8;
			return aplUTF8; 
		}
		if(pSrcBufLen>=4)
		{
			if(bom_compare(BOM_UTF32BE, BOM_LEN_UTF32BE, pSrcBuf))
			{
				if(0!=pBomLen) *pBomLen=BOM_LEN_UTF32BE;
				return aplUTF32BE; 
			}
		}
	}
	return aplANSI;
}

// Возвращает данные BOM для кодировки
void GetBomData(aplTextEncoding code, BYTE *&pBom, int &pBomLen)
{
	pBom=0; pBomLen=0;

	switch (code)
	{
	case aplUCS2:	 pBom=(BYTE*)BOM_UCS2;		pBomLen=BOM_LEN_UCS2; 	break;
	case aplUTF8:	 pBom=(BYTE*)BOM_UTF8;		pBomLen=BOM_LEN_UTF8; 	break;
	case aplUTF16BE: pBom=(BYTE*)BOM_UTF16BE;	pBomLen=BOM_LEN_UTF16BE; 	break;
	case aplUTF32BE: pBom=(BYTE*)BOM_UTF32BE;	pBomLen=BOM_LEN_UTF32BE; 	break;
	case aplUTF32LE: pBom=(BYTE*)BOM_UTF32LE;	pBomLen=BOM_LEN_UTF32LE; 	break;
	default: return;
	}
}


// Проверяет - начинается ли буффер с нужного BOM.
// Если начинается, то возвращает длинну BOM, иначе возвращает 0;
// Фактически возвращает смещение, с котрого начинаются данные
int GetBomLenInBuffer(aplTextEncoding code,  const BYTE *pSrcBuf, int pSrcBufLen)
{
	if(0==pSrcBuf) return 0;
	if(pSrcBufLen<2) return 0;

	BYTE *bom;
	int bom_len;
	GetBomData(code,bom,bom_len);

	if(pSrcBufLen<bom_len) return 0;
	if(bom_compare(bom,bom_len,pSrcBuf)) return bom_len;

	return 0;
}
//*********************************************************************************
//*********************************************************************************
//*********************************************************************************
// Проверяет данные на соответствие принципам UTF-8
bool IsBufUtf8( const BYTE *pBuf, int iBufLen)
{
	if(0==pBuf) return false;
	if(0==iBufLen) return false;

	bool bNo8BitChar=true ; //  нет символов > 127 
	
	int i=0;
	while(i<iBufLen)
	{
		unsigned char c=pBuf[i];
		if('\0'==c) return false; // В UTF-8 не бывает нулей
		
		if(0==(c&0x80)) {i++; continue; } // англ. буква 1 байт

		bNo8BitChar =false;

		if(0xf0==(c&0xf8))
		{
			// тут 4 байта
			if( (i+4)>iBufLen) return false;
			c=pBuf[i+1];
			if(0x80!=(c&0xc0)) return false;
			c=pBuf[i+2];
			if(0x80!=(c&0xc0)) return false;
			c=pBuf[i+2];
			if(0x80!=(c&0xc0)) return false;
			i+=4; 
		}
		else if(0xe0==(c&0xf0))
		{
			// тут 3 байта
			if( (i+3)>iBufLen) return false;
			c=pBuf[i+1];
			if(0x80!=(c&0xc0)) return false;
			c=pBuf[i+2];
			if(0x80!=(c&0xc0)) return false;
			i+=3;
		}
		else if(0xc0==(c&0xe0))
		{
			// тут 2 байта
			if( (i+2)>iBufLen) return false;
			c=pBuf[i+1];
			if(0==(c&0x80)) return false;
			i+=2; 
		}

		else {
			return false;} // это не UTF8
	}

	if(bNo8BitChar) return false; // Это обычные 7-битные данные

	// В кодировке 1251 некоторые комбинации русских букв являются корректными символами UTF-8
	// (Например, "Печать" - это комбинация 2х байтового греческого и 4х байтового символов )
	// Почти все русские буквы 1251 являются кодами каких-то алфавитов в UTF8
	// В результате данные в кодировке 1251 могут определяться как UTF-8
	// Дабы этого избежать делаем дополнительную проверку 
	// исходя из предположения что символы >127 могут быть только русскими буквами
	// (второй байт русских букв из UTF-8 не пересекается с русскими буквами в 1251)
	// Список кодов русских букв: http://blog.kislenko.net/show.php?id=2045
	
	i=0;
	while(i<iBufLen)
	{
		unsigned char c=pBuf[i];
		i++;

		if(0==(c&0x80))  continue;  // англ. буква 1 байт
		
		unsigned char c1=pBuf[i];
		i++;

		if( 0xd0 == c)
		{
			if (0x81==c1) continue;
			if ((c1>=0x90) && (c1<=0xbf)) continue;
		}
		else if(0xd1 == c)
		{
			if (0x91==c1) continue;
			if ((c1>=0x80) && (c1<=0x8f)) continue;
		}
		else if(0xc2 == c) // Специальные духбайтовые символы
		{
			if (0xab==c1) continue;  // Левая 'русская' кавычка
			if (0xbb==c1) continue;  // Левая 'русская' кавычка
			if (0xb1==c1) continue;  // '+-'
			if (0xb5==c1) continue;  //  мю
		}
		else if(0xce == c) // Другие специальные духбайтовые символы
		{
			if (0xa9==c1) continue;  // Copyright
		}
		else if(0xe2 == c) // Специальные трехбайтовый символы
		{
			unsigned char c2=pBuf[i];
			if (0x84==c1 && 0x96==c2 ) {i++; continue;} // Это '№'
			if (0x80==c1)
			{	
				if(0x93==c2 ) {i++; continue;} // Это '-'
				if(0xa2==c2 ) {i++; continue;} // Большая жирная точка
				if(0xa6==c2 ) {i++; continue;} // непонятно что
			}
		}

		return false;
	}

	return true;
};
//*********************************************************************************
bool IsBufUtf32BE( const BYTE *pBuf, int iBufLen)
{
	if(0==pBuf) return false;
	if(0==iBufLen) return false;

	int i=0;
	while(i<iBufLen)
	{
		const BYTE *curbuf=pBuf+i;
		if(! ('\0'==curbuf[0] && '\0'==curbuf[1])) return false;
		i+=4;
	}
	return true;
}

bool IsBufUtf32LE( const BYTE *pBuf, int iBufLen)
{
	if(0==pBuf) return false;
	if(0==iBufLen) return false;

	int i=0;
	while(i<iBufLen)
	{
		const BYTE *curbuf=pBuf+i;
		if(! ('\0'==curbuf[2] && '\0'==curbuf[3])) return false;
		i+=4;
	}
	return true;
}
//*********************************************************************************
bool IsBufUCS2( const BYTE *pBuf, int iBufLen)
{
	if(0==pBuf) return false;
	if(0==iBufLen) return false;

	// Если символ, английский, то второй байт - 0
	// Если русский, то второй байт 0x04
	int i=0;
	while(i<iBufLen)
	{
		const BYTE *curbuf=pBuf+i;
		if('\0'==curbuf[0]) return false;
		if('\0'==curbuf[1])
		{	if( 0!=(0x80&curbuf[0])) return false;
		}
		else if('\4'!=curbuf[1]) return false;
		i+=2;
	}
	return true;
}
//*********************************************************************************
bool IsBufUtf16BE( const BYTE *pBuf, int iBufLen)
{
	if(0==pBuf) return false;
	if(0==iBufLen) return false;

	int i=0;
	while(i<iBufLen)
	{
		const BYTE *curbuf=pBuf+i;
		if('\0'==curbuf[1]) return false;
		if('\0'==curbuf[0])
		{	if( 0!=(0x80&curbuf[1])) return false;
		}
		else if('\4'!=curbuf[0]) return false;
		i+=2;
	}
	return true;
}
//*********************************************************************************
//*********************************************************************************
//*********************************************************************************

bool CaplStringFile::BufConvert_Utf16_BE2LE(BYTE *pBuf, UINT  pBufLen)
{
	if(0==pBufLen) return true;
	if(0==pBuf) return false;

	UINT i=0;
	while (i<pBufLen)
	{
		BYTE b=pBuf[i];
		pBuf[i]=pBuf[i+1];
		pBuf[i+1]=b;
		i+=2;
	}
	return true;
}
//*********************************************************************************
bool CaplStringFile::BufConvert_Utf32_BE2LE(BYTE *pBuf, UINT  pBufLen)
{
	if(0==pBufLen) return true;
	if(0==pBuf) return false;

	UINT i=0;
	while ((i+4)<=pBufLen)
	{
		BYTE b=pBuf[i];
		pBuf[i]=pBuf[i+3];
		pBuf[i+3]=b;

		b=pBuf[i+1];
		pBuf[i+1]=pBuf[i+2];
		pBuf[i+2]=b;

		i+=4;
	}
	return true;
}

//*********************************************************************************
#ifdef __GNUC__

// Функция предназначена для встаивания в другие, поэтому все проверки на разумность вх данных должны быть снаружи
// Сама подбирает нужный буфер и возвращает его в pDstBuf и DstBufSize
// iDstDataSize - количество байт полученных при конвертации
// iDstBufSize - размер выделенного буфера в байтах
// Т.к. нужный размер буффера не всегда очевиден, то функция итерацилнно подбирает нужный
// После использования буфер надо удалить самостоятельно!
// Если в pOutErrMess передается указатель на CString, то сообщения об ошибках выводся в него, а aplMessageBox не вызываются

bool  bConvertTextWithIconv(aplTextEncoding SrcEncoding, BYTE *pSrcBuf,  int pSrcBufLen,
							aplTextEncoding DstEncoding, BYTE *&pDstBuf, int &iDstDataSize, int *iDstBufSize, CString *pOutErrMess)
{
	pDstBuf=0; iDstDataSize=0; if(0!=iDstBufSize) *iDstBufSize=0;
	if(pOutErrMess!=0)*pOutErrMess=_T("");
	if(pSrcBufLen<=0) return false;
    if(0==SrcEncoding || 0== DstEncoding){APL_STOP_IF_DEBUG; return false;}

    const char *fromCode=aplGetCodingNameForIcov(SrcEncoding);
    const char *toCode=aplGetCodingNameForIcov(DstEncoding);

    if(0==fromCode || 0== toCode){APL_STOP_IF_DEBUG; return false;}

    iconv_t ic=iconv_open(toCode,fromCode);

    if(ic==(iconv_t)(-1))
    {
        APL_STOP_IF_DEBUG
        aplMessageBox(_T("\n\n Error iconv_open !!!"),MB_OK|MB_ICONERROR);
        return false;
    }

    //Считаем размер буффера
    int out_char_size = aplGetMaxBYTEInCharForEncoding(DstEncoding);
    int in_char_size = aplGetMinBYTEInCharForEncoding(SrcEncoding);
    if(0==in_char_size || 0==out_char_size)
    {
        APL_STOP_IF_DEBUG;
        return false;
    }
    int outbuf_size= out_char_size * (1+(pSrcBufLen/in_char_size));

    while(true)
    {
        // iconv портит параметры, поэтому делаем копию
        size_t inlen=(size_t)pSrcBufLen, outlen=(size_t)outbuf_size;
        char *out_buffer=new char[outlen];
        char *tmp_in=(char*)pSrcBuf, *tmp_out=(char*)out_buffer;

        size_t ick=iconv(ic,&tmp_in,&inlen,&tmp_out,&outlen);

        if(-1!=(int)ick)
        {
            iDstDataSize=outbuf_size-outlen;
			if(0!=iDstBufSize) *iDstBufSize=outlen;
            pDstBuf=(BYTE*)out_buffer;
            break;
        }

        delete[] out_buffer;
        if(E2BIG==errno)
        {
            // Нехватило места. Увеличиваем буфер на 30% и повторяем попытку
            outbuf_size*=1.3;
            continue;

        }
        iconv_close(ic);

		LPCTSTR pInternalErrMess;
		if(EILSEQ==errno ) pInternalErrMess = _T("iconv: An invalid multiBYTE sequence has been encountered in the input. "); // на входе для обработки находится неправильная многобайтовая последовательность
		else if(EINVAL==errno ) pInternalErrMess = _T("iconv: An incomplete multiBYTE sequence has been encountered in the input."); // на входе для обработки находится неполная многобайтовая последовательность
		else if(E2BIG==errno ) pInternalErrMess = _T("iconv: There is not sufficient room at *outbuf."); //недостаточно места в *outbuf
		else pInternalErrMess = _T("iconv: error.");

		if(pOutErrMess==0)
		{
			aplMessageBox(pInternalErrMess,MB_OK|MB_ICONSTOP);
			APL_STOP_IF_DEBUG
		}
		else
			*pOutErrMess = pInternalErrMess;
        return false;
    }
    iconv_close(ic);

    return true;
}
#endif
//*********************************************************************************


//*********************************************************************************
bool CaplStringFile::BufConvert_Utf32le_to_UCS2(BYTE *pBuf32, UINT pBuf32Len, BYTE *&pBuf16, UINT &pBuf16Len)
{
	pBuf16=0;
	pBuf16Len=0;

	if(0==pBuf32Len) return true;
	if(0==pBuf32) return false;

	UINT buflen=(UINT)(pBuf32Len/2);
	BYTE *buf = new BYTE[buflen];
	if(0==buf) return false;

	UINT i=0,j=0;
	while ((i+4)<=pBuf32Len)
	{
		if('\0'!=pBuf32[i+2] || '\0'!=pBuf32[i+3]) {delete[] buf; return false;}
		buf[j]=pBuf32[i];
		buf[j+1]=pBuf32[i+1];
		i+=4;
		j+=2;
	}
	pBuf16=buf;
	pBuf16Len=j;
	return true;
}

//*********************************************************************************
bool CaplStringFile::BufConvert_UCS2_to_Utf32le(BYTE *pBufUcs2, UINT pBufUcs2Len, BYTE *&pBuf32, UINT &pBuf32Len)
{
	pBuf32=0;
	pBuf32Len=0;

	if(0==pBufUcs2Len) return true;
	if(0==pBufUcs2) return false;

	UINT buflen=2*pBufUcs2Len;
	BYTE *buf = new BYTE[buflen];
	if(0==buf) return false;

	UINT i=0,j=0;
	while ((i+2)<=pBufUcs2Len)
	{
		buf[j]=pBufUcs2[i];
		buf[j+1]=pBufUcs2[i+1];
		buf[j+2]='\0';
		buf[j+3]='\0';
		i+=2;
		j+=4;
	}
	pBuf32=buf;
	pBuf32Len=j;
	return true;
}


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

 bool CaplStringFile::ConvertEncoding2UCS2(aplTextEncoding SrcEncoding, BYTE *pSrcBuf, int pSrcBufLen, BYTE *&pDstBuf, int &iDstBufSize)
{
	pDstBuf=0; iDstBufSize=0;

	if(0==pSrcBuf) return false;
	if(0==pSrcBufLen) return false;

	if(aplUCS2==SrcEncoding)
	{
		ASSERT(FALSE); // В таком варианте вызываться не должно
		pDstBuf=new BYTE[pSrcBufLen];
		if(0==pDstBuf) return false;
		memcpy(pDstBuf,pSrcBuf,pSrcBufLen);
		iDstBufSize=pSrcBufLen;
		return true;
	}

	if(aplUTF16BE==SrcEncoding)
	{
		pDstBuf=new BYTE[pSrcBufLen];
		if(0==pDstBuf) return false;
		memcpy(pDstBuf,pSrcBuf,pSrcBufLen);
		BufConvert_Utf16_BE2LE(pDstBuf,pSrcBufLen);
		iDstBufSize=pSrcBufLen;
		return true;
	}

#ifdef __GNUC__

	return bConvertTextWithIconv(SrcEncoding,pSrcBuf,pSrcBufLen,aplUCS2,pDstBuf,iDstBufSize);

#else

	if(aplANSI==SrcEncoding || aplUTF8==SrcEncoding)
	{
		UINT encoding=0;
		if(aplANSI==SrcEncoding) encoding=CP_ACP; else  encoding=CP_UTF8;

		int len= ::MultiByteToWideChar(encoding, 0, (LPCSTR)pSrcBuf, pSrcBufLen, NULL, 0);
		if(len>0) 
		{
			int buflen=2*len + 2; // символы в байты + 2 на всякий случай
			BYTE *buf2 = new BYTE[buflen];
			int len2= ::MultiByteToWideChar(encoding, 0,  (LPCSTR)pSrcBuf, pSrcBufLen, (LPWSTR)buf2, len);

			if(len2<=0)
			{
				if(0!=buf2) delete buf2;
				return false;
			}
			buf2[buflen-1] = 0;
			buf2[buflen-2] = 0;
			pDstBuf=buf2;
			iDstBufSize=len2*2;
			return true;
		}
	}

	ASSERT(FALSE); // Неизвестная кодировка
	return false;

#endif
}

//****************************************************************
bool CaplStringFile::ConverUCS2Encoding( BYTE *pSrcBuf, int pSrcBufLen, aplTextEncoding DstEncoding, BYTE *&pDstBuf, int &iDstBufSize)
{
	pDstBuf=0; iDstBufSize=0;

	if(0==pSrcBuf) return false;
	if(0==pSrcBufLen) return false;

	if(aplUCS2==DstEncoding)
	{
		ASSERT(FALSE); // В таком варианте вызываться не должно
		pDstBuf=new BYTE[pSrcBufLen];
		if(0==pDstBuf) return false;
		memcpy(pDstBuf,pSrcBuf,pSrcBufLen);
		iDstBufSize=pSrcBufLen;
		return true;
	}

	if(aplUTF16BE==DstEncoding)
	{
		pDstBuf=new BYTE[pSrcBufLen];
		if(0==pDstBuf) return false;
		memcpy(pDstBuf,pSrcBuf,pSrcBufLen);
		BufConvert_Utf16_BE2LE(pDstBuf,pSrcBufLen);
		iDstBufSize=pSrcBufLen;
		return true;
	}

#ifdef __GNUC__

	return bConvertTextWithIconv(aplUCS2,pSrcBuf,pSrcBufLen,DstEncoding,pDstBuf,iDstBufSize);

#else

	if(aplANSI==DstEncoding || aplUTF8==DstEncoding)
	{
		UINT encoding=0;
		if(aplANSI==DstEncoding) encoding=CP_ACP; else  encoding=CP_UTF8;

		int numchar=int (pSrcBufLen/2);

		LPWSTR usc2str=(LPWSTR)pSrcBuf;

		// чтобы не преобразовывать BOM
		if(pSrcBufLen>=2)
		{
			if(bom_compare(BOM_UCS2,BOM_LEN_UCS2,pSrcBuf))
			{
				usc2str=(LPWSTR)(pSrcBuf+2); // Иначе в результат попадет BOM
				numchar--;
			}
		}

		int len  = ::WideCharToMultiByte(encoding, 0, (LPCWSTR)usc2str, numchar, NULL,  0, NULL, NULL); 
		if(len>0)
		{	
			int buflen=len+1;
			BYTE *buf2 = new BYTE[buflen];
			int len2  = ::WideCharToMultiByte(encoding, 0, (LPCWSTR) usc2str, numchar, (LPSTR)buf2, len + 1, NULL, NULL); 

			if(len2<=0)
			{
				if(0!=buf2) delete buf2;
				return false;
			}
			pDstBuf=buf2;
			iDstBufSize=len2;
			return true;
		}
	}

	if(aplUTF32LE==DstEncoding || aplUTF32BE==DstEncoding)
	{
		UINT utf32len= 0;
		BYTE *utf32buf=0;
		if(!BufConvert_UCS2_to_Utf32le(pSrcBuf, pSrcBufLen,utf32buf,utf32len))
		{
			if(0!=utf32buf) delete utf32buf;
			return false;
		}
		
		if(aplUTF32BE==DstEncoding)	BufConvert_Utf32_BE2LE(utf32buf,utf32len);

		pDstBuf=utf32buf;
		iDstBufSize=utf32len;
		
		return true;
	}


	ASSERT(FALSE); // Неизвестная кодировка
	return false;

#endif
}

 //****************************************************************
bool  CaplStringFile::ConvertEncoding(aplTextEncoding SrcEncoding, BYTE *pSrcBuf,  int pSrcBufLen,
									  aplTextEncoding DstEncoding, BYTE *&pDstBuf, int &iDstBufSize) 
{
	ASSERT(SrcEncoding!=DstEncoding);

	pDstBuf=0; iDstBufSize=0;

	if(0==pSrcBuf) return false;
	if(0==pSrcBufLen) return false;

	if(aplUnknownEncoding==SrcEncoding || aplUnknownEncoding==DstEncoding)
	{
		APL_STOP_IF_DEBUG;
		return false;
	}

	if(SrcEncoding==DstEncoding)
	{
		ASSERT(FALSE);// В таком варианте вызываться не должно. Проверка должна быть снаружи
		pDstBuf=new BYTE[pSrcBufLen];
		if(0==pDstBuf) return false;
		memcpy(pDstBuf,pSrcBuf,pSrcBufLen);
		iDstBufSize=pSrcBufLen;
		return true;
	}

#ifdef __GNUC__

	return bConvertTextWithIconv(SrcEncoding,pSrcBuf,pSrcBufLen,DstEncoding,pDstBuf,iDstBufSize);

#else

	BYTE *ucs_buf=0;
	int ucs_buf_len=0;

	if(aplUCS2==SrcEncoding)
	{
		ucs_buf=pSrcBuf; ucs_buf_len=pSrcBufLen;
	}
	else
	{
		bool b=ConvertEncoding2UCS2(SrcEncoding, pSrcBuf, pSrcBufLen, ucs_buf, ucs_buf_len);
		if(0==ucs_buf_len || 0== ucs_buf)
		{
			if(0!=ucs_buf) delete ucs_buf; 
			return false;
		}
	}

	if(DstEncoding==aplUCS2)
	{
		pDstBuf=ucs_buf;
		iDstBufSize=ucs_buf_len;
		return true;
	}

	BYTE *mb_buf=0;
	int mb_buf_len=0;
	bool b=ConverUCS2Encoding(ucs_buf, ucs_buf_len, DstEncoding, mb_buf, mb_buf_len);
	if(0==mb_buf || 0== mb_buf_len)
	{
		if(0!=ucs_buf) delete ucs_buf; 
		if(0!=mb_buf) delete mb_buf; 
		return false;
	}

	if(ucs_buf!=pSrcBuf) delete ucs_buf;

	pDstBuf=mb_buf;
	iDstBufSize=mb_buf_len;

	return true;

#endif
}


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

CaplStringFile::CaplStringFile(void)
{
	Reset(); 
}
CaplStringFile::CaplStringFile(LPCTSTR lpszFileName, UINT nOpenFlags)
{
	Reset();
	Open(lpszFileName,nOpenFlags);
}

void CaplStringFile::Reset()
{
	m_bDataInMemory=false;
	m_bDataChanged=false;
	m_pBuf=0;
	m_iBufSize=0;
	m_iDataSize=0;
	m_iCurPosRead=0; 

	m_FileCoding=aplUnknownEncoding;
	m_UseEncodingIfNotBOM=aplUnknownEncoding;

#ifdef __linux__
	m_BufferCoding=aplUTF32LE;
#elif _UNICODE
	m_BufferCoding=aplUCS2;
#else
	m_BufferCoding=aplANSI;
#endif
}

CaplStringFile::~CaplStringFile(void)
{
	if (CFile::hFileNull != m_file.m_hFile) Close();
}

//****************************************************************
void CaplStringFile::FreeBuffer()
{
	if(0!=m_pBuf)delete[] m_pBuf;

	m_pBuf=0;
	m_iBufSize=0;
	m_iDataSize=0;
	m_iCurPosRead=0;	
}
//****************************************************************
bool CaplStringFile::SetBufSize(UINT iNewBufSize)
{
	if(iNewBufSize<=0) {FreeBuffer(); return false;}
	if(m_iBufSize>=iNewBufSize) return true;

	if( (iNewBufSize-m_iBufSize) < 4096) iNewBufSize=m_iBufSize+4096; // Добавляем блоками по 4К

	BYTE *m_pNewBuf=new BYTE[iNewBufSize];
	if(0==m_pNewBuf) return false;

	if(0!=m_pBuf && m_iBufSize>0 && m_iDataSize>0) memcpy(m_pNewBuf,m_pBuf,m_iDataSize);

	if(0!=m_pBuf)delete[] m_pBuf;
	m_pBuf=m_pNewBuf;

	m_iBufSize=iNewBufSize;
	return true;
}


//****************************************************************
void CaplStringFile::SeekToBegin( )
{
	m_iCurPosRead = 0;

	// Проматываем BOM, если он есть
	if(aplANSI!=m_BufferCoding) m_iCurPosRead=GetBomLenInBuffer(m_BufferCoding,m_pBuf,m_iDataSize);
}
//****************************************************************
UINT CaplStringFile::GetPosition() {return m_iCurPosRead;}

UINT CaplStringFile::SetPosition(UINT pos)
{
	UINT minpos=0;
	if(aplANSI!=m_BufferCoding) minpos=GetBomLenInBuffer(m_BufferCoding,m_pBuf,m_iDataSize);

	if(pos<minpos) pos=minpos;
	if(pos>=m_iDataSize) pos=m_iDataSize-1;

	m_iCurPosRead=pos;
	
	return 	m_iCurPosRead;
}

//****************************************************************
BOOL CaplStringFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags, aplTextEncoding prev_FileCoding )
{
	if (CFile::hFileNull != m_file.m_hFile) Close();

	// На вский случай переставляем флаги
	nOpenFlags&= ~(CFile::typeText);
	nOpenFlags|=CFile::typeBinary;

	m_FileCoding = prev_FileCoding;

#ifdef _MFC_VER

	CFileException Exception;
	BOOL retval;
	retval = m_file.Open(lpszFileName, nOpenFlags, &Exception);
	if(!retval)
	{
		TCHAR   szCause[255];  szCause[0]=_T('\0');
		Exception.GetErrorMessage(szCause, 255); 
		m_ErrorMessage = APL_T("Ошибка при открытии файла: ");
		m_ErrorMessage += szCause;
	}
	return retval;

#else

	BOOL retval;
	retval = m_file.Open(lpszFileName, nOpenFlags);
	if(!retval)
	{
		m_ErrorMessage = APL_T("Ошибка при открытии файла: ");
		CString buf;
		aplGetDescriptionSystemError(errno, buf, false);
		m_ErrorMessage += buf;
	}
	return retval;

#endif
}

//****************************************************************
void CaplStringFile::Close()
{
	if (CFile::hFileNull !=m_file.m_hFile)
	{
		if(m_bDataChanged) Flush();
		m_file.Close();
	}

	if(0!=m_pBuf){delete[] m_pBuf; m_pBuf=0;}
	Reset();
}

//****************************************************************
aplTextEncoding CaplStringFile::GetBufferEncode( BYTE *pSrcBuf, int pSrcBufLen)
{
	return aplGetBufferEncodeByBOM(pSrcBuf,pSrcBufLen);
}
//****************************************************************
BOOL CaplStringFile::Load2Memory()
{
	if (CFile::hFileNull == m_file.m_hFile) return FALSE;
	if(m_bDataInMemory) return TRUE;
	
	ULONGLONG ullFsize=m_file.GetLength();

	// С файлами более 1 Гб не работаем  т.к. если больше, то все равно в память ничего не влезет
	if(ullFsize >= (1024*1024*1024)) return FALSE; 

	int fSize=(int)ullFsize;

	if(0!=m_pBuf) FreeBuffer();

	if(0==fSize) return TRUE;

	m_pBuf=new BYTE[fSize];
	if(0==m_pBuf) return false;

	m_file.SeekToBegin();
	int uifs=m_file.Read(m_pBuf,fSize);
	if(uifs!=fSize)
	{
		delete[] m_pBuf; m_pBuf=0;
		return FALSE;
	}

	m_iBufSize=fSize;
	m_iDataSize=fSize;
	m_iCurPosRead=0; 
	m_bDataInMemory=true;
	m_bDataChanged=false;

	int iBomLen=0;
	m_FileCoding=aplGetBufferEncodeByBOM(m_pBuf,m_iDataSize,&iBomLen); // Определение кодировки

	if(0==iBomLen) 
	{
		if(aplUnknownEncoding!=m_UseEncodingIfNotBOM) m_FileCoding=m_UseEncodingIfNotBOM;
		else
		{
			// BOM-а не было, пытаемся опрелить эвристически
			if(IsBufUtf32BE(m_pBuf,m_iDataSize)) m_FileCoding=aplUTF32BE;
			else if(IsBufUtf32LE(m_pBuf,m_iDataSize)) m_FileCoding=aplUTF32LE;
			else if(IsBufUtf8(m_pBuf,m_iDataSize)) m_FileCoding=aplUTF8;
			else if(IsBufUCS2(m_pBuf,m_iDataSize)) m_FileCoding=aplUCS2;
			else if(IsBufUtf16BE(m_pBuf,m_iDataSize)) m_FileCoding=aplUTF16BE;
		}
	}
	
	if(aplUnknownEncoding == m_FileCoding ) m_FileCoding=aplANSI;

	aplTextEncoding curEncoding=m_FileCoding;

	if(aplUTF32LE==m_FileCoding || aplUTF32BE==m_FileCoding)
	{
        if(aplUTF32BE==m_FileCoding) {BufConvert_Utf32_BE2LE(m_pBuf,m_iDataSize); }

		BYTE *buf_ucs;
		UINT buf_ucs_len;

		if(!BufConvert_Utf32le_to_UCS2(m_pBuf+iBomLen,m_iDataSize-iBomLen,buf_ucs,buf_ucs_len))
		{
			if(0!=buf_ucs) delete[] buf_ucs;
			delete[] m_pBuf; Reset(); return FALSE;
		}
		curEncoding=aplUCS2;
		delete[] m_pBuf;
		iBomLen=0;
		m_pBuf=buf_ucs;
		m_iDataSize=buf_ucs_len;
	}
		

	// Конвертируем буффер в нужную кодировку

#ifdef __linux__
	aplTextEncoding trgEncoding=aplUTF32LE;
#elif _UNICODE
	aplTextEncoding trgEncoding=aplUCS2;
#else 
	aplTextEncoding trgEncoding=aplANSI;
#endif

	if(trgEncoding!=curEncoding)
	{
		BYTE *tmp_buf=0;
		int tmp_buf_len=0;
		bool b=ConvertEncoding(curEncoding,m_pBuf+iBomLen,m_iDataSize-iBomLen, trgEncoding, tmp_buf, tmp_buf_len);
		if(0==tmp_buf_len || 0== tmp_buf || b==false)
		{
			delete[] m_pBuf; Reset(); return FALSE;
		}
		delete[] m_pBuf;
		m_pBuf=tmp_buf;
		m_iBufSize=m_iDataSize=tmp_buf_len;
		m_BufferCoding=trgEncoding;
	}

	SeekToBegin(); // Проматываем BOM, если он есть
	
	return TRUE;
}

//****************************************************************
BOOL CaplStringFile::ReadString(CString& rString)
{
	rString=_T("");

	if (CFile::hFileNull == m_file.m_hFile) return FALSE;
	if(!m_bDataInMemory) {if(!Load2Memory()) return FALSE;}
	if(!m_bDataInMemory) return FALSE;
	if(0==m_iDataSize || 0==m_pBuf) return FALSE;

	if(m_iCurPosRead>=m_iDataSize) return false; // данные кончились

	// Внутренний буффер всегда должен быть в текущей кодировке (в этом и смысл класса)
#ifdef __linux__
	if(aplUTF32LE!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#elif _UNICODE
	if(aplUCS2!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#else
	if(aplANSI!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#endif

	UINT i = m_iCurPosRead; // позиция в байтах
	while(i < m_iDataSize)
	{
		TCHAR c=*(TCHAR*)(m_pBuf+i);
		i+=sizeof(TCHAR);
		if(_T('\0') == c || _T('\r')==c || _T('\n')==c ) break; // Считаем '\0' таким же разделителем как и '\n'
		rString+=c;
	}
	// проматываем разделители
	while(i < m_iDataSize)
	{
		TCHAR c=*(TCHAR*)(m_pBuf+i);
		if( ! (_T('\0') == c || _T('\r')==c || _T('\n')==c) ) break; // пошли не разделители
		i+=sizeof(TCHAR);
	}
	m_iCurPosRead=i;
	return true;
}


//****************************************************************
BOOL CaplStringFile::ReadAllDataToString(CString& rString)
{
	rString=_T("");

	if (CFile::hFileNull == m_file.m_hFile) return FALSE;
	if(!m_bDataInMemory) {if(!Load2Memory()) return FALSE;}
	if(!m_bDataInMemory) return FALSE;
	if(0==m_iDataSize || 0==m_pBuf) return FALSE;

	if(m_iCurPosRead>=m_iDataSize) return false; // данные кончились

	// Внутренний буффер всегда должен быть в текущей кодировке (в этом и смысл класса)
#ifdef __linux__
	if(aplUTF32LE!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#elif _UNICODE
	if(aplUCS2!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#else
	if(aplANSI!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#endif

	LPTSTR str_buf=rString.GetBuffer(m_iDataSize-m_iCurPosRead+sizeof(TCHAR));
	memcpy(str_buf,m_pBuf+m_iCurPosRead,m_iDataSize-m_iCurPosRead);
	*(TCHAR*)(str_buf+m_iDataSize-m_iCurPosRead)=_T('\0');
	rString.ReleaseBuffer((m_iDataSize-m_iCurPosRead)/sizeof(TCHAR));
	return true;
}

//****************************************************************
BOOL CaplStringFile::WriteString(LPCTSTR lpsz)
{
	if (CFile::hFileNull == m_file.m_hFile) return FALSE;
	if(0==lpsz) return FALSE;
	if(_T('\0')==lpsz[0]) return FALSE;

		// Внутренний буффер всегда должен быть в текущей кодировке (в этом и смысл класса)
#ifdef __linux__
	if (aplUnknownEncoding==m_BufferCoding && 0==m_iDataSize) m_BufferCoding=aplUTF32LE;
	else if(aplUTF32LE!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#elif _UNICODE
	if (aplUnknownEncoding==m_BufferCoding && 0==m_iDataSize) m_BufferCoding=aplUCS2;
	else if(aplUCS2!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#else
	if (aplUnknownEncoding==m_BufferCoding && 0==m_iDataSize) m_BufferCoding=aplANSI;
	else if(aplANSI!=m_BufferCoding) {APL_STOP_IF_DEBUG; return false;}
#endif

	CString  sTmpBuf; // Промежуточный CString как задел на случай если будет более мудрёное преобразование

	sTmpBuf.GetBuffer(16 + _strlen(lpsz));
	sTmpBuf.ReleaseBuffer();

	int i=0; // в символах

	while(true)
	{
		TCHAR c=lpsz[i];
		if(_T('\0')==c) break;

		// Ковертируем \n в 2 символа, если надо
		if(c==_T('\n'))
		{
			if(i==0) sTmpBuf+=_T('\r');
			else
			{
				TCHAR c1=lpsz[i-1];
				if(c1!=_T('\r')) 
					sTmpBuf+=_T('\r'); 
			}
		}
		sTmpBuf+=c; 
		i++;
	}

	UINT iAddLenSym=sTmpBuf.GetLength();
	UINT iAddLenBYTEs=sizeof(TCHAR)*iAddLenSym;

	UINT iNewBufSize= m_iDataSize + ((iAddLenSym + 1) * sizeof(TCHAR));
	if(m_iBufSize<iNewBufSize) 
	{
		if(!SetBufSize(iNewBufSize)) return FALSE;
	}

	memcpy(m_pBuf+m_iDataSize,LPCTSTR(sTmpBuf),iAddLenBYTEs);

	m_iDataSize+=iAddLenBYTEs;
	*((TCHAR*)(m_pBuf+m_iDataSize))=_T('\0'); // Исключительно для удобства просмотра памяти

	m_bDataChanged=true;
	return TRUE;
}

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

BOOL CaplStringFile::Flush()
{
	
	if (m_file.m_hFile == CFile::hFileNull) return FALSE;
	if(!m_bDataChanged)  return TRUE;  // Скидывать нечего
	if(0==m_iDataSize) {m_bDataChanged=false;  return TRUE;}  // Скидывать нечего
	if(0==m_pBuf) {m_bDataChanged=false;  return TRUE;}		 // Скидывать нечего
	
	if(aplUnknownEncoding==m_FileCoding) //неизвестная кодировка
	{
#ifdef __linux__
		m_FileCoding=aplUCS2;
#elif _UNICODE
		m_FileCoding=aplUCS2;
#else
		m_FileCoding=aplANSI;
#endif
	}

	int iStartData=GetBomLenInBuffer(m_BufferCoding,m_pBuf,m_iDataSize); // Смещение от начала в байтах

	m_file.SeekToBegin();	
	if(m_FileCoding==m_BufferCoding)
	{
		if(aplANSI!=m_BufferCoding && 0==iStartData ) // Нет BOM в данных
		{
			BYTE *bom;
			int bom_len;
			GetBomData(m_BufferCoding,bom,bom_len);
			m_file.Write(bom,bom_len);
		}
		m_file.Write(m_pBuf,m_iDataSize);
	}
	else
	{
		//надо преобразовать формат

		BYTE *tmp_buf=0;
		int tmp_buf_len=0;
		bool b=ConvertEncoding(m_BufferCoding,m_pBuf+iStartData,m_iDataSize-iStartData, m_FileCoding, tmp_buf, tmp_buf_len);
		if(!b || 0==tmp_buf_len || 0== tmp_buf)
		{
			if(0!=tmp_buf) delete[] tmp_buf;
			return FALSE;
		}

		int j=GetBomLenInBuffer(m_FileCoding,tmp_buf,tmp_buf_len); // Вдруг конвертер пишет BOM

		if(aplANSI!=m_FileCoding && 0==j ) // Нет BOM в данных
		{
			BYTE *bom;
			int bom_len;
			GetBomData(m_FileCoding,bom,bom_len);
			m_file.Write(bom,bom_len);
		}
		m_file.Write(tmp_buf,tmp_buf_len);
				
		if(0!=tmp_buf) delete[] tmp_buf;
	}
	m_file.Flush();
	m_bDataChanged=false;
	
	return TRUE;
}


