// aplAggr.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "aplAggr.h"
#include <memory.h>

#ifdef _MFC_VER
	#include <dde.h>
	#include <afxpriv.h>

	#ifdef _DEBUG
		#define new DEBUG_NEW
	#endif

#else
	#include <time.h>
	#include <atomic>
	//#include <stdatomic.h>
	#include <stdarg.h>
	#include <string.h>
#ifndef __linux__
	#include <winnls.h>
#endif
#endif

#ifdef __linux__
	#include <unistd.h>
    #include <limits.h>
#endif

bool bAplAggrStopInDebugEnable=true;

// Обработка исключений SaplErrorDescription: получение описания ошибок уровня Transport
extern CString (__cdecl *GetTransportErrorDescription)(int ErrorCode );

// Обработка исключений SaplErrorDescription: получение описание ошибок уровня StepData
extern CString (__cdecl *GetStepDataErrorDescription)(int ErrorCode );

// Обработка исключений SaplErrorDescription: получение описание ошибок уровня сервера
extern CString (__cdecl *GetServerErrorDescription)(int ErrorCode );


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

#ifndef _MFC_VER

#ifdef __linux__
void GetLocalTime(PSYSTEMTIME lpSystemTime)
{
	if(lpSystemTime==0)
		return;
    time_t rawtime;
    struct tm * timeinfo;

    time (&rawtime);
    timeinfo = localtime (&rawtime);
	if(timeinfo==0)
		return;
    lpSystemTime->wYear = timeinfo->tm_year + 1900;
    lpSystemTime->wMonth = timeinfo->tm_mon + 1;
    lpSystemTime->wDayOfWeek = timeinfo->tm_wday;
    lpSystemTime->wDay = timeinfo->tm_mday;
    lpSystemTime->wHour = timeinfo->tm_hour;
    lpSystemTime->wMinute = timeinfo->tm_min;
    lpSystemTime->wSecond = timeinfo->tm_sec;
    lpSystemTime->wMilliseconds = 0;
}
#endif
// потоки


#endif

int _sprintf(LPTSTR string, LPCTSTR format, ...)
{
	va_list marker;
	va_start(marker, format);	// устанавливаем маркер на 1-й неименованный параметр

	// передаем список неименованных параметров функции sprintf и пишем в наш буфер
#ifdef _UNICODE
	int iRet = vswprintf(string, APL_MAX_STRING_LENGTH, format, marker);
#else
	int iRet = vsprintf(string, format, marker);
#endif

	va_end(marker);				// очищаем

	return iRet;
}


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

CaplDataBuf::CaplDataBuf()
{
	m_IsExternalData=false;
	m_Size=0;
	m_data=0;
	m_MaxSize=0;
	m_Iterator=0;
	SetSize(4096);
	m_Pos_Sizer=0;
#ifdef _UNICODE
	m_ansi_string = false;
#else // #ifdef _UNICODE
	m_ansi_string = true;
#endif // #ifdef _UNICODE

}

CaplDataBuf::CaplDataBuf(int max_size)
{
	m_IsExternalData=false;
	m_Size=0; m_data=0;
	m_MaxSize=0; m_Iterator=0;m_Pos_Sizer=0;
	SetSize(max_size);
#ifdef _UNICODE
	m_ansi_string = false;
#else // #ifdef _UNICODE
	m_ansi_string = true;
#endif // #ifdef _UNICODE
}

CaplDataBuf::CaplDataBuf(char* data,int size)
{
	m_IsExternalData=true;
	m_Size=size; m_data=data;
	m_MaxSize=0; m_Iterator=0;m_Pos_Sizer=0;
#ifdef _UNICODE
	m_ansi_string = false;
#else // #ifdef _UNICODE
	m_ansi_string = true;
#endif // #ifdef _UNICODE
}

CaplDataBuf::~CaplDataBuf()
{
	if(!m_IsExternalData)
		delete []m_data;
}

bool CaplDataBuf::SetSize(int max_size)
{
	if(m_IsExternalData)return false;

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-overflow"
#endif

	if(max_size<m_Size) return false;

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

	if(max_size==m_Size) return true;
	
	char* newdata=new char[max_size+1];
	if(m_data!=0)
	{
		memcpy(newdata,m_data,m_Size);
//		for(int i=0;i<m_Size;i++)
//			newdata[i]=m_data[i];
//		newdata[i]='\0';
		newdata[m_Size]='\0';
		delete []m_data;
	}
	m_MaxSize=max_size;
	m_data=newdata;
	return true;
}

void CaplDataBuf::SetExternalData(void* data,int size){
	if(!m_IsExternalData){
		delete []m_data;
	}
	m_IsExternalData=true;
	m_Size=size; 
	m_data=(char*)data;
	m_Iterator=0;
}

void CaplDataBuf::AttachExternalData(void* data,int size)
{
	if(!m_IsExternalData)
	{
		delete []m_data;
	}
	m_IsExternalData = false;
	if(size != 0 && data != 0)
	{
		m_Size = size; 
		m_MaxSize = size; 
		m_data = (char*)data;
	}
	else
	{
		m_Size = 0; 
		m_MaxSize = 0; 
		m_data = (char*)0;
		SetSize(4096);
	}
	m_Iterator = 0;
	m_Pos_Sizer = 0;
}

void CaplDataBuf::Clear()
{
	m_Iterator=0;m_Size=0;
	if(m_IsExternalData)
	{
		m_IsExternalData=false;
		m_data=0;
		SetSize(4096);
	}
}
int CaplDataBuf::AddStr(LPCSTR str)
{
	if(0==str) return -1;
	if(str[0]=='\0') return m_Size;

	int i=0;
	while(true)
	{
		if(((char*)str)[i]=='\0')break;
		i++;
	}
	return Add((void*)str,i);
}

int CaplDataBuf::AddStr(LPCWSTR str)
{
	if(0==str) return -1;
	if(str[0]==_T('\0')) return m_Size;

	int i=0;
	while(true)
	{
		if(str[i]==_T('\0'))break;
		i++;
	}
	if(m_ansi_string)
	{
		CaplStringAdapter str_ad(str);
#ifndef _MFC_VER
		str_ad.m_bIsAnsiUtf8=false; // корреспондент ждет в буфере 1251
#endif
		return Add((void*)(const char*)str_ad,i);
	}

	#ifndef __linux__
		ASSERT(2==sizeof(TCHAR));
		return Add((void*)str,i*sizeof(wchar_t));
	#else
		ASSERT(4==sizeof(TCHAR));
		BYTE *strUcs2=0; 
		int strUcs2Len=0;
		if(bConvertTextWithIconv(aplUTF32LE,(BYTE*)str,i*sizeof(TCHAR),aplUCS2,strUcs2,strUcs2Len))
		{
			if(0!=strUcs2)
			{
				Add((void*)strUcs2,strUcs2Len);
				delete[] strUcs2;
			}
		}
		return m_Size;
	#endif
}


int CaplDataBuf::Add(const void *buf, const int size)
{
	if(m_IsExternalData) return -1;
	if(buf==0) return -1;
	if(size<0) return -1;
	if(0==size) return m_Size;

	int newsize=m_Size+size;
	try
	{
		if((newsize)>=m_MaxSize)
		{
			int k=newsize+1;// чтобы m_data[newsize]= ... не упало
			if((m_MaxSize+1)*2>newsize)k=(m_MaxSize+1)*2;
			SetSize(k);
		}
		memcpy(m_data+m_Size,buf,size);
		m_data[newsize]='\0';
	}
#ifdef _MFC_VER
	catch(CMemoryException* e)
	{
		TCHAR   szCause[255]; szCause[0]=_T('\0');
		e->GetErrorMessage(szCause,255);
		CString sErr; 
		sErr.Format(_T("Memory exeption in CaplDataBuf::Add : %s"), szCause);
		aplMessageBox(sErr,MB_OK|MB_ICONERROR);
	}
#else
    catch(const exception& e)
    {
        APL_STOP_IF_DEBUG
		CString sErr;
        sErr.Format(_T("Memory exeption in CaplDataBuf::Add : %s"), e.what());
        aplMessageBox(sErr,MB_OK|MB_ICONERROR);
    }
#endif
	m_Size=newsize;
	return m_Size;
}

/** Добавляет размер строки в символах и строку (в зависимости от переменной m_ansi_string) */
int CaplDataBuf::AddStrBuf(LPCTSTR str)
{
	if(m_IsExternalData) return -1;
	int k;
	if(str == 0) k = 0;
	else k = _strlen(str) + 1;

	if(k == 0)
	{
		Add(&k, 4);
		return m_Size;
	}
#ifdef _UNICODE
	if(m_ansi_string)
	{
		CaplStringAdapter str_ad(str);
#ifndef _MFC_VER
		str_ad.m_bIsAnsiUtf8=false; // корреспондент ждет в буфере 1251
#endif
		Add(&k, 4);
		Add((void*)(const char*)str_ad, k);
	}
	else
	{
		#ifndef __linux__
			ASSERT(2==sizeof(TCHAR));
			Add(&k, 4);
			Add((void*)str, k*sizeof(TCHAR));
		#else
			ASSERT(4==sizeof(TCHAR));
			BYTE *strUcs2=0; 
			int strUcs2Len=0;
			if(bConvertTextWithIconv(aplUTF32LE,(BYTE*)str,k*sizeof(TCHAR),aplUCS2,strUcs2,strUcs2Len))
			{
				if(0!=strUcs2)
				{
					Add(&k, 4);
					Add((void*)strUcs2,strUcs2Len);
					delete[] strUcs2;
				}
				else
				{
					k=0;
					Add(&k, 4);
				}
			}
			else
			{
				k=0;
				Add(&k, 4);
			}
		#endif
	}
#else // #ifdef _UNICODE
	Add(&k, 4);
	if(m_ansi_string)
	{
		Add((void*)str, k);
	}
	else
	{
		CStringW str_w; str_w = str;
		Add((void*)str_w.GetBuffer(), k*2); // В юникодном буфере 2х-байтовые символы
	}
#endif // #ifdef _UNICODE
	return m_Size;
}

void *CaplDataBuf::GetBuffer(){return m_data;}

void *CaplDataBuf::GetCurBuffer(){return m_data+m_Iterator;}
void *CaplDataBuf::GetCurBuffer(const int size){
	int old_iterator=m_Iterator;
	if(m_Iterator+size>m_Size)return 0;
	m_Iterator+=size;
	return m_data+old_iterator;
}

bool CaplDataBuf::Read(INT32 *buf)
{
	if (m_data==0) return false;
	if ((m_Iterator+4)>m_Size){*buf=0;return false;}
	*buf=*(INT32*)(m_data+m_Iterator);
	m_Iterator+=4;
	return true;
}

bool CaplDataBuf::Read(UINT32 *buf)
{
	if (m_data==0) return false;
	if ((m_Iterator+4)>m_Size){*buf=0;return false;}
	*buf=*(UINT32*)(m_data+m_Iterator);
	m_Iterator+=4;
	return true;
}

bool CaplDataBuf::Read(void *buffer, int size)
{
	if (m_data==0) return false;
	if (size<1) return false;
	try{
		if ((m_Iterator+size)>m_Size)
		{
			memset(buffer,0,size);
			return false;
		}
		//*****int i=size;
		//*****while(i--)((char*)buffer)[i]=m_data[i+m_Iterator];
// 		char *buf1=m_data+m_Iterator;
// 		for(int i=0;i<size;i++)
// 			((char*)buffer)[i]=buf1[i];
		memcpy(buffer,m_data+m_Iterator,size);
	}
#ifdef _MFC_VER
	catch(CMemoryException* e)
	{
		TCHAR   szCause[255];  szCause[0]=_T('\0');
		e->GetErrorMessage(szCause,255);
		CString sErr;
		sErr.Format(_T("Memory exeption in CaplDataBuf::Read : %s"),szCause);
		aplMessageBox(sErr,MB_OK|MB_ICONERROR);
	}
#else
    catch(const exception& e)
    {
		APL_STOP_IF_DEBUG
        CString sErr;
        sErr.Format(_T("Memory exeption in CaplDataBuf::Read : %s"), e.what());
        aplMessageBox(sErr,MB_OK|MB_ICONERROR);
    }
#endif
	
	m_Iterator+=size;
	return true;
}

/**<Сдвигает текущее положение курсора.*/
void CaplDataBuf::MoveCurPos(int delta)
{
    m_Iterator+=delta;
    if(m_Iterator>=m_Size)m_Iterator=m_Size-1;
    if(m_Iterator<0)m_Iterator=0;
}


/** Читаем строку (в зависимости от переменной m_ansi_string и согласно длине строки перед телом) */
bool CaplDataBuf::ReadStrBuf(CString &str)
{
	str = _T("");
	if (m_data==0) return false;
	if ((m_Iterator+4) > m_Size){return false;}

	int k;
	if(!Read(&k, 4)){return false;}
	if(k == 0) return true;
	if(m_Iterator+k > m_Size) return false;

	char* cbuf = new char[(k+1)*sizeof(TCHAR)];
	bool retval = false;

#ifdef _UNICODE
	if(m_ansi_string)
	{
		if(Read((void*)cbuf, k))
		{
			cbuf[k] = 0;
			CaplStringAdapter str_ad((const char*)cbuf);
#ifndef _MFC_VER
			str_ad.m_bIsAnsiUtf8=false; // корреспондент ждет в буфере 1251
#endif
			str = (LPCTSTR)str_ad;
			retval = true;
		}
	}
	else
	{
		k*=2; // В буффере всегда 2х -байтовые символы
		if(Read((void*)cbuf, k))
		{
			cbuf[k] = 0;
			cbuf[k+1] = 0;
			
			#ifndef __linux__
				ASSERT(2==sizeof(TCHAR));
				str = (LPCTSTR) cbuf;
			#else
				ASSERT(4==sizeof(TCHAR));
				BYTE *strUtf32=0; 
				int strUtf32Len=0;
				if(bConvertTextWithIconv(aplUCS2,(BYTE*)cbuf,k+2,aplUTF32LE,strUtf32,strUtf32Len))
				{
					if(0!=strUtf32)
					{
						str = (LPCWSTR) strUtf32;
						delete[] strUtf32;
					}
				}
			#endif			
			
			retval = true;
		}
	}

#else // #ifdef _UNICODE
	if(m_ansi_string)
	{
		if(Read((void*)cbuf, k))
		{
			cbuf[k] = 0;
			str = (LPCTSTR) cbuf;
			retval = true;
		}
	}
	else
	{
		const int buf_char_size=2; // В юникодном буффере 2х-байтовые символы
		delete[] cbuf;
		cbuf = new char[(k+1)*buf_char_size];

		if(Read((void*)cbuf, k*buf_char_size))
		{
			cbuf[k*buf_char_size] = 0;
			cbuf[k*buf_char_size+1] = 0;

			CaplStringAdapter str_ad((const wchar_t*)cbuf);
			str = (LPCTSTR) str_ad;

			retval = true;
		}
	}
#endif // #ifdef _UNICODE

	delete[] cbuf;
	return retval;
}


// добавил Дещере 05_03_10
bool CaplDataBuf::ReadString(CString &rString)
{
	rString = _T("");
	char curr_c; // тут правильный char, 
	CStringA cString;

	if (m_data==0) return false;
	if(m_Iterator>=m_Size) return false;

#ifdef _UNICODE

	wchar_t* curr_w_p;
	int Size = m_ansi_string?m_Size:m_Size-1;
	if(m_Iterator>=Size) return false;
	

	while(m_Iterator<Size)
	{
		curr_c = m_data[m_Iterator];
		curr_w_p = (wchar_t*)(m_data + m_Iterator);
		m_Iterator++;

		if(m_ansi_string)
		{
			if(curr_c=='\n' )
			{
				if(m_Iterator<Size && m_data[m_Iterator] == '\r')m_Iterator++;
				break;
			}
			if(curr_c=='\r' )
			{
				if(m_Iterator<Size && m_data[m_Iterator] == '\n')m_Iterator++;
				break;
			}
		}
		else
		{
			// при юникодной строке каждый символ в двух байтах
			m_Iterator++;
			if(*curr_w_p ==_T('\n') )
			{
				if(m_Iterator<Size && m_data[m_Iterator] == _T('\r'))m_Iterator+=2;
				break;
			}
			if(*curr_w_p ==_T('\r') )
			{
				if(m_Iterator<Size && m_data[m_Iterator] == _T('\n'))m_Iterator+=2;
				break;
			}
		}
		if(m_ansi_string)
			cString+=curr_c;
		else
			rString+=*curr_w_p;

		if(m_ansi_string && curr_c==0)
			break;
		else if(!m_ansi_string && *curr_w_p==0)
			break;
		if(m_Iterator>=Size) 
			break;
	}
	if(m_ansi_string)
	{
		CaplStringAdapter str_ad((const char*)cString);
#ifndef _MFC_VER
		str_ad.m_bIsAnsiUtf8=false; // корреспондент ждет в буфере 1251
#endif
		rString =(const wchar_t*)str_ad;
	}

#else
	while(m_Iterator<m_Size)
	{
		curr_c = m_data[m_Iterator];
		m_Iterator++;
		if(curr_c=='\n' )
		{
			if(m_Iterator<m_Size && m_data[m_Iterator] == '\r')m_Iterator++;
			break;
		}
		if(curr_c=='\r' )
		{
			if(m_Iterator<m_Size && m_data[m_Iterator] == '\n')m_Iterator++;
			break;
		}
		rString+=curr_c;
		if(curr_c==0) 
			return true;
		if(m_Iterator>=m_Size) 
			return true;
	}
#endif // #ifdef _UNICODE

	return true;
}


bool CaplDataBuf::BeginPosSizer()
{
	if((m_Size+4)>m_MaxSize && !SetSize(m_Size+4))return false;
	memset(m_data+m_Size,0,4);
	m_Pos_Sizer=m_Size;
	m_Size+=4;
	m_Iterator+=4;
	return true;
}
// выделяет буфер, увеличивает размер данных, забивает данные нулями
void *CaplDataBuf::GetNewBuffer(int size)
{
	int old_size=m_Size;
	int newsize=m_Size+size;
	if((newsize)>m_MaxSize)
	{
		int k=newsize;
		if((m_MaxSize+1)*2>newsize)k=(m_MaxSize+1)*2;
		SetSize(k);
	}
	memset(m_data+m_Size,0,size);
	m_Size=newsize;
	return m_data+old_size;
}
void CaplDataBuf::EndSiezer(int size)
{
	if(m_Pos_Sizer+4>m_Size) return;
	if(size==-1){
		size=m_Size-m_Pos_Sizer-4;
	}else{
		if(size+m_Pos_Sizer+4>m_Size) return;
		m_Size=size+m_Pos_Sizer+4;
	}
	memcpy(m_data+m_Pos_Sizer,&size,4);
}


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

void CaplDataBufEx::SetExternalData(void* data, int size, int MaxSize)
{
	if(!m_IsExternalData)
	{
		delete[] m_data;
	}
	m_IsExternalData = true;
	m_Size = size;
	if(MaxSize == 0){m_MaxSize = size;}else{m_MaxSize = MaxSize;}
	m_data = (char*)data;
	m_Iterator = 0;
}

int CaplDataBufEx::Add2Iterator(void *buf, int size)
{
	if(buf == 0) return -1;
	int newIterator = m_Iterator + size;
	if((newIterator+1)>=m_MaxSize)
	{
		if(m_IsExternalData)return 0;
		else SetSize(newIterator+100);
	}
	memcpy(m_data+m_Iterator, buf, size);
	m_data[newIterator]=_T('\0');
	m_Iterator = newIterator;
	m_Size = newIterator+1; // размер выделенной памяти хранится в m_MaxSize, а в m_Size - размер реально записанных данных
	return newIterator;
}

int CaplDataBufEx::AddStrBuf_TCHAR_2Iterator(CString &str)
{
	UINT32 size = str.GetLength() + 1;

	int newIterator = m_Iterator + size;
	if(m_IsExternalData)
	{
		if((newIterator+4)>=m_MaxSize)
		{
			if(m_Iterator+4 > m_MaxSize)
				return 0;
			size = 0;
		}
	}
	int i1 = Add2Iterator(&size, 4);
	if(size == 0) return i1;
	// этот буфер будет прочитан сервером на той же платформе, так что передаем оригинальный буфер и не тратим время на перекодировку
	return Add2Iterator((void*)str.GetBuffer(), size*sizeof(TCHAR));
}


int CaplDataBufEx::ReadStrBuf_TCHAR_r(CString &str)
{
	int str_len;
	TCHAR* t_buf = 0;
	str.Empty();

	if(!Read(&str_len, 4))
		return -1;

	if(str_len!=0)
	{
		t_buf = new TCHAR[str_len+1];
		// этот буфер был записан сервером на этой же платформе
		if(!Read(t_buf, str_len*sizeof(TCHAR)))
		{
			delete[] t_buf;
			return -1;
		}
		t_buf[str_len]=0;
		str = t_buf;
		delete[] t_buf;
		t_buf = 0;
	}
	return str_len;
}



#define APL_NET_SRV_MISSED_DATA		101

int CaplDataBufEx::ReadStrBuf_TCHAR_(CString &str)
{
	int ret = CaplDataBufEx::ReadStrBuf_TCHAR_r(str);
	if(ret==-1)
		throw(SaplErrorDescription(APL_NET_SRV_MISSED_DATA, _T(__FILE__), _T(__DATE__), __LINE__, APL_T("Ошибка при чтении строки из буфера"), SaplErrorDescriptionLevel_Server, __APL_FUNC__, 0));

	return  ret;
}


bool CaplDataBufEx::CopyDataTo(int offset, void *buf, int size)
{
	if(offset+size > m_MaxSize) return false;
	memcpy(m_data+offset, buf, size);
	return true;
}

void CaplDataBufEx::Set2Begin()
{
	m_Iterator = 0;
	m_Size = 0;
}

int CaplDataBufEx::ReadFromIterator(void *buffer, int size)
{
	int readed = 0;
	if (m_data == 0) return 0;
	if (size<1) return 0;
	if ((m_Iterator+size)>m_Size)
	{
		readed = m_Size - m_Iterator;
	}
	else
	{
		readed = size;
	}
	if(readed > 0)
	{
		memcpy(buffer, m_data+m_Iterator, readed);
		m_Iterator += readed;
	}
	return readed;
}

// Ищет в буфере последовательность BYTEs размером size байт начиная с позиции start.
// если найдено - возвращает позицию начала последоватлеьности.
// если не найдено - возвращает -1
int CaplDataBufEx::FindBytes(int start, void* BYTEs, int size)
{
	if(BYTEs == 0) return -1;
	if(start < 0 || size <0 || start + size > m_Size) return -1;

	int curr_pos = start;
	BYTE* bBYTEs = (BYTE*) BYTEs;

	while(curr_pos + size <= m_Size)
	{
		if(m_data[curr_pos] == bBYTEs[0])
		{
			bool is_equal = true;
			for(int i=0; i< size; i++)
			{
				if(m_data[curr_pos + i] != bBYTEs[i])
				{
					is_equal = false;
					break;
				}
			}
			if(is_equal)
				return curr_pos;
		}
		curr_pos++;
	}
	return -1;
}

bool CaplDataBufEx::GetBytes(int start, int size, CaplDataBufEx &buf)
{
	buf.Clear();
	if(start <0  || size <=0 || start + size > m_Size) return false;

	buf.SetExternalData(m_data+start, size, size);

	return true;
}

bool CaplDataBufEx::GetBytes(int start, int size, CString &str, bool convertFromUTF8)
{
	str = "";
	if(start <0  || size <=0 || start + size > m_Size) return false;
	char* buf = new char[size+1];
	memcpy(buf, m_data+start, size);
	buf[size] = 0;
#ifndef __linux__
	if(convertFromUTF8)
	{
		int len= MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
		wchar_t *wbuf;
		wbuf=new wchar_t[len*2 + 1];

		MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, len);
		wbuf[len]= _T('\0');

		str = wbuf;
		delete[] wbuf;
	}
	else
#else
	(void)convertFromUTF8; // замена Q_UNUSED
	// В линуксе char* это и есть UTF8. CStringW сам преобразует символы как надо.
#endif
	{
		str = buf;
	}
	delete[] buf;

	return true;
}


// конвертирует строку в utf-8 и записывает в буфер длину строки (4 байта) и саму строку
bool CaplDataBufEx::Utf8StringAdd2Iterator(CString str)
{
#ifdef _UNICODE
	CaplStringAdapter ad_u(str);
#else
	CaplStringAdapter ad_w(str);
	CaplStringAdapter ad_u((const wchar_t*)ad_w);
#endif
	ad_u.m_bIsAnsiUtf8 = true;
	CStringA str_a = (const char*)ad_u;
	DWORD len = str_a.GetLength();
	Add2Iterator(&len, 4);
	Add2Iterator(str_a.GetBuffer(), len);

	return true;
}

// читает из буфера 4 байта длинну и строку в utf-8 и конвертирует ее в текущую кодировку
bool CaplDataBufEx::Utf8StringReadFromIterator(CString &str)
{
	DWORD str_len=0;
	str.Empty();

	if(ReadFromIterator(&str_len, 4)==4 && str_len!=0)
	{
		char* c_buf = new char[str_len+1];
		if(!ReadFromIterator(c_buf, str_len*sizeof(char)))
		{
			delete[] c_buf; 
			return false;
		}
		c_buf[str_len]=0; 
#ifndef __linux__
		CaplStringAdapter ad_u(c_buf);
		ad_u.m_bIsAnsiUtf8 = true;
	#ifdef _UNICODE
		str = (const wchar_t*) ad_u;
	#else
		CaplStringAdapter ad_w((const wchar_t*) ad_u);
		str = ((const char*)ad_w);
	#endif
#else
		// В линуксе char* это и есть UTF8. CStringW сам преобразует символы как надо.
		str = c_buf;
#endif
		delete[] c_buf; 
	}
	return true;
}

bool CaplDataBufEx::MoveData2DBuf(CaplDataBuf &dest)
{
	if(m_IsExternalData) return false;
	if(m_data==0 || m_MaxSize==0) return false;

	dest.AttachExternalData(m_data, m_Size);
	m_Size=0; m_data=0; m_MaxSize=0; m_Iterator=0;
	SetSize(100);

	return true;
}


bool CaplDataBufEx::EnMaskTail(int start, const char* mask, int size)
{
	if(start >= m_Size) return false;
	if(mask == 0) return false;
	if(size == 0) return false;
	int pos_m = size, pos_d = start;
	do{
		pos_m--;
		m_data[pos_d] ^= mask[pos_m];
		pos_d++;
		if(pos_m <= 0) pos_m = size;

	}while(pos_d < m_Size);
	return true;
}



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

CaplMap::CaplMap()
{
	Data=0;
	Size=0;
	MaxSize=0;
	bAutoSort=false;
//	UniqueIn=false;
	m_sorted=true;
	NotFullFoudInQuick=true;
	//SetSize(32); // ЯАИ: Так как большинство ни разу не используются, пусть по умолчанию будут пустыми
}



CaplMap::CaplMap(int max_size)
{
	Data=0;
	Size=0;
	MaxSize=0;
	bAutoSort=false;
//	UniqueIn=false;
	m_sorted=true;
	NotFullFoudInQuick=true;
	SetSize(max_size);
}

CaplMap::~CaplMap()
{
	if(Data!=0) delete []Data;
	Size=0; Data=0;MaxSize=0; // Чтобы не валилось, если вдруг будет обращение к удаленному объекту
}

//********************************************************
void  CaplMap::Clear()
{
	Size=0;
}

void  CaplMap::RemoveAll()
{
	Size=0;MaxSize=0;
	if(Data) {delete []Data; Data=0;}
}

bool CaplMap::SetSize(int max_size) // число элементов
{
	if(max_size<Size) return false;
	if(max_size==Size) return true;
	if(max_size<32) max_size=32;
	
	aplMap_element *data1=new aplMap_element[max_size];

	if(Data!=0)
	{
		//for(int i=0;i<Size;i++)	data1[i]=Data[i];
		// заменил Дещере
		memcpy(data1,Data,Size*sizeof(aplMap_element));
		delete []Data;
	}
	Data=data1;
	MaxSize=max_size;
	return true;
}

//********************************************************
int CaplMap::Add(INT_PTR in, INT_PTR out)
{
	int new_indx=-1;
	if(Size+1>MaxSize) 	SetSize((MaxSize+1)*2);
	if(!bAutoSort){ Data[Size].in=in; Data[Size].out=out; m_sorted=false;new_indx=Size;}

	else if(Size==0)	{ Data[0].in=in; Data[0].out=out;new_indx=0;}
	else if(in<=Data[0].in)
	{
		if(in==Data[0].in) { Data[0].out=out;Size--;}
		else
		{
			for(int i=Size-1; i>=0;i--) Data[i+1]=Data[i];
			Data[0].in=in; Data[0].out=out;new_indx=0;
		}
		new_indx=0;
	}
	else if(in>=Data[Size-1].in)
	{
		if(in==Data[Size-1].in)  Size--;
		Data[Size].in=in; Data[Size].out=out;
		new_indx=Size;
	}
	else
	{
		//Поиск нужного индекса
        int i=0, i0=0, i1=Size-1, io=-1;
		// поиск упорядоченного значения
		do
		{
			if((i1-i0)<=1) 
			{
				for(int i=Size-1; i>=i1;i--) Data[i+1]=Data[i];
				Data[i1].in=in; Data[i1].out=out;
				new_indx=i1;
				break;
			}
			io=i;
			i=(i0+i1)/2;
			if(io==i) break;
			if(Data[i].in==in) { Data[i].out=out;new_indx=i; Size--; break;}

			if(in>Data[i].in) i0=i;
			else i1=i;
		}
		while(1);
	}
	Size++;

	// внимание! Долгое время здесь стояла return size; но по уму должно стоять size-1 DIV
	return new_indx;

/*
	Data[Size].in=in;
	Data[Size].out=out;
	Size++;
	m_sorted=false;
 	return Size-1;
 */
}
bool CaplMap::Remove(int index)
{
	if(index<0)return false;
	if(index>=Size)return false;

	for(int i=index;i<Size-1;i++){
		Data[i]=Data[i+1];
	}
	Size--;

	return true;
}
//********************************************************

int CaplMap::FindByOut(INT_PTR data) const
{
	for(int i=0;i<Size;i++)
		if(Data[i].out==data) return i;
	return -1;
}

void *CaplMap::GetByInP(INT_PTR data)
{
	int i=FindByIn(data);
	if(i==-1) return 0;
	return (void*)Data[i].out;
}

INT_PTR CaplMap::GetByIn(INT_PTR data)
{
	int i=FindByIn(data);
	if(i==-1) return -1;
	return Data[i].out;
}
INT_PTR CaplMap::GetByOut(INT_PTR data) const
{
	int i=FindByOut(data);
	if(i==-1) return -1;
	return Data[i].in;
}

//********************************************************
int compare_aplMap_elements( const void *arg1, const void *arg2 )
{
   if(((aplMap_element*)arg1)->in>((aplMap_element*)arg2)->in) return 1;
   if(((aplMap_element*)arg1)->in==((aplMap_element*)arg2)->in) return 0;
   return -1;
}

void CaplMap::SortIn()
{
	if(Data==0) return;
	if(Size<2) return;
	//if(m_sorted) return; // На случай, если данные изменили снаружи
	
	qsort(Data,Size,sizeof(aplMap_element),compare_aplMap_elements);
/*	int i,j,k;
	aplMap_element n;
	for(i=1;i<Size;i++)
	{
		if(Data[i].in<Data[i-1].in)
		{
			for(j=i-1;j>0;j--)if(Data[j-1].in<Data[i].in) break;
			n=Data[i];
			for(k=i;k>j;k--)Data[k]=Data[k-1];
			Data[j]=n;
		}
	}*/
	m_sorted=true;
	
}

//********************************************************
int CaplMap::QFindByIn(INT_PTR value)
{
	if(Data==0) return -1;
	if(Size==0) return -1;

	if(Size<10) // Если меньше, то перебором быстрее
	{
		for(int i=0;i<Size;i++)
			if(Data[i].in==value) return i;
		return -1;
	}

	if(!m_sorted) SortIn();

	bool dir_low_high=true;
	if(Size>1) if(Data[0].in>Data[1].in) dir_low_high=false;

	int i=0, i0=0, i1=Size-1, io=-1, index=-1;
	// поиск упорядоченного значения
	do
	{
		io=i;
		i=(i0+i1)/2;
		if(io==i) break;
		if(Data[i].in==value){index=i; break;}
		if(dir_low_high)
		{
			if(value>Data[i].in) i0=i;
			else i1=i;
		}
		else
		{
			if(value>Data[i].in) i1=i;
			else i0=i;
		}
	} while(1);
	if(index!=-1) return index;
	if(Data[i1].in==value)return i1;
	if(Data[i0].in==value)return i0;

	// поиск неупорядоченного значения
	if(!NotFullFoudInQuick)
	{
		for(i=0;i<Size;i++) if(Data[i].in==value)return i;
	}

	return -1;
}
//********************************************************
INT_PTR CaplMap::QGetByIn(INT_PTR data)
{
	int i=QFindByIn(data);
	if(i==-1) return -1;
	return Data[i].out;
}
void *CaplMap::QGetPointerByIn(INT_PTR data)
{
	int i=QFindByIn(data);
	if(i==-1) return 0;
	return (void*)Data[i].out;
}

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

//*************************************************************************
int CaplStrMap::compare_CaplStrMapItem_NoCase( const void *arg1, const void *arg2 )
{
	return (*(CaplStrMapItem**)arg1)->str.CompareNoCase((*(CaplStrMapItem**)arg2)->str);
}

int CaplStrMap::compare_CaplStrMapItem_AsInt(const void *arg1, const void *arg2)
{
	int iArg1 = _atoi((*(CaplStrMapItem**)arg1)->str);
	int iArg2 = _atoi((*(CaplStrMapItem**)arg2)->str);
	if (iArg1 < iArg2)
		return -1;
	else if (iArg1 > iArg2)
		return 1;
	else
		return 0;
}

int CaplStrMap::compare_CaplStrMapItem( const void *arg1, const void *arg2 )
{
	return (*(CaplStrMapItem**)arg1)->str.Compare((*(CaplStrMapItem**)arg2)->str);
}
//*************************************************************************
void  CaplStrMap::Clear(){ items.Clear();}
//*************************************************************************
int CaplStrMap::Add(LPCTSTR str, INT_PTR val)
{
	//items.strsert(0, new CaplStrMapItem(str,val));
	int new_indx=-1;

	// вставляем так чтобы было упорядоченно
	if(m_bCompareCase)
	{
		if(items.Size==0)
		{
			items.Add(new CaplStrMapItem(str,val));
			new_indx=0;
		}
		else if(items[0]->str>=str)
		{
			if(items.Data[0]->str==str)
				items[0]->val=val;
			else
				items.Insert(0, new CaplStrMapItem(str,val));
			new_indx=0;
		}
		else if(items[items.Size-1]->str<=str)
		{
			if(items[items.Size-1]->str==str)
				items[items.Size-1]->val=val;
			else
				items.Add( new CaplStrMapItem(str,val));
			new_indx=items.Size-1;
		}
		else
		{
			//Поиск нужного индекса
            int i=0, i0=0, i1=items.Size-1, io=-1;
			// поиск упорядоченного значения
			do
			{
				if((i1-i0)<=1) 
				{
					if(items.Data[i1]->str==str){
						items[i1]->val=val;
					}else{
						items.Insert(i1, new CaplStrMapItem(str,val));
					}
					new_indx=i1;
					break;
				}
				io=i;
				i=(i0+i1)/2;
				if(io==i) break;
				if(items[i]->str==str) { items[i]->val=val; new_indx=i; break;}

				if(items[i]->str<=str) i0=i;
				else i1=i;
			}
			while(1);
		}
	}
	else
	{
		CString sStr(str);
		sStr.MakeLower();
		if(items.Size==0)
		{
			items.Add(new CaplStrMapItem(sStr,val));
			new_indx=0;
		}
		else if(items[0]->str.CompareNoCase(sStr)>0)
		{
			if(items.Data[0]->str==sStr)
				items[0]->val=val;
			else
				items.Insert(0, new CaplStrMapItem(sStr,val));
			new_indx=0;
		}
		else if(items[items.Size-1]->str.CompareNoCase(sStr)<0)
		{
			if(items[items.Size-1]->str==sStr)
				items[items.Size-1]->val=val;
			else
				items.Add( new CaplStrMapItem(sStr,val));
			new_indx=items.Size-1;
		}
		else
		{
			//Поиск нужного индекса
            int i=0, i0=0, i1=items.Size-1, io=-1;
			// поиск упорядоченного значения
			do
			{
				if((i1-i0)<=1) 
				{
					if(items.Data[i1]->str.CompareNoCase(sStr)==0)
					{
						items[i1]->val=val;
					}
					else
					{
						items.Insert(i1, new CaplStrMapItem(sStr,val));
					}
					new_indx=i1;
					break;
				}
				io=i;
				i=(i0+i1)/2;
				if(io==i) break;
				if(items[i]->str.CompareNoCase(sStr)==0)
				{
					items[i]->val=val;
					new_indx=i;
					break;
				}

				if(items[i]->str.CompareNoCase(sStr)<0)
					i0=i;
				else
					i1=i;
			}
			while(1);
		}
	}
/*	CString buf;
	TRACE("\n");
	for(int i=0;i<items.Size;i++)
	{
		buf.Format(" %s",LPCSTR(items[i]->str));
		TRACE(buf);
	}*/
//	items.Add( new CaplStrMapItem(str,val));
	return new_indx;
}

void CaplStrMap::SetSize(int nSize)
{
	items.SetSize(nSize);
}
//********************************************************
void CaplStrMap::Sort()
{
	if(items.Size<2) return;
	if (m_bCompareAsInt)
	{
		qsort(items.Data, items.Size, sizeof(CaplStrMapItem*), compare_CaplStrMapItem_AsInt);
	}
	else
	{
		if (m_bCompareCase)
			qsort(items.Data, items.Size, sizeof(CaplStrMapItem*), compare_CaplStrMapItem);
		else
			qsort(items.Data, items.Size, sizeof(CaplStrMapItem*), compare_CaplStrMapItem_NoCase);
	}

}
//********************************************************
long CaplStrMap::Get(LPCTSTR str) const
{
	if(items.Size<1) return -1;

	if(bNotUseApiInFind)
	{
		int i=Find(str);
		if(i==-1) return -1;
		return items[i]->val;
	}

	CaplStrMapItem **item=NULL;
	if(m_bCompareCase)
	{
	  	CaplStrMapItem item0(str,0);
		CaplStrMapItem *pitem0=&item0;
		item=(CaplStrMapItem**)bsearch(&pitem0,items.Data,items.Size,sizeof(CaplStrMapItem*),compare_CaplStrMapItem);
	}
	else
	{
		CString sStr(str);
		sStr.MakeLower();
	  	CaplStrMapItem item0(sStr, 0);
		CaplStrMapItem *pitem0=&item0;
		item=(CaplStrMapItem**)bsearch(&pitem0, items.Data, items.Size, sizeof(CaplStrMapItem*), compare_CaplStrMapItem_NoCase);
	}
	if(item==0) return -1;
	return (*item)->val;
}
//********************************************************
void *CaplStrMap::GetP(LPCTSTR str) const
{
	if (items.Size < 1) return 0;

	if(bNotUseApiInFind)
	{
		int i=Find(str);
		if(i==-1) return 0;
		return (void*)(items[i]->val);
	}

	CaplStrMapItem **item;
	if(m_bCompareCase)
	{
		CaplStrMapItem item0(str,0);
		CaplStrMapItem *pitem0=&item0;
		item=(CaplStrMapItem**)bsearch(&pitem0,items.Data,items.Size,sizeof(CaplStrMapItem*),compare_CaplStrMapItem);
	}
	else
	{
		CString sStr(str);
		sStr.MakeLower();
	  	CaplStrMapItem item0(sStr, 0);
		CaplStrMapItem *pitem0=&item0;
		item=(CaplStrMapItem**)bsearch(&pitem0,items.Data,items.Size,sizeof(CaplStrMapItem*),compare_CaplStrMapItem_NoCase);
	}
	if(item==0) return 0;
	return (void*)((*item)->val);
}//********************************************************
int  CaplStrMap::Find(LPCTSTR str) const
{
	if(items.Size<1) return -1;

	int i=0, i0=0, i1=items.Size-1, io=-1, index=-1;
	// поиск упорядоченного значения
	if(m_bCompareCase)
	{
		do
		{
			io = i;
			i = (i0+i1) >> 1;
			if (io == i) break;
			if( items[i]->str == str){index = i; break;}

			if (items[i]->str<str) i0=i;
			else i1 = i;
		}
		while(1);

		if(index!=-1) return index;
		if(items[i1]->str==str)return i1;
		if(items[i0]->str==str)return i0;
	}
	else
	{
		CString sStr(str);
		do
		{
			io = i;
			i = (i0+i1) >> 1;
			if (io == i) break;
			if( items[i]->str.CompareNoCase(sStr)==0){index = i; break;}

			if (items[i]->str.CompareNoCase(sStr)<0) i0=i;
			else i1 = i;
		}
		while(1);

		if(index!=-1) return index;
		if(items[i1]->str.CompareNoCase(str)==0)return i1;
		if(items[i0]->str.CompareNoCase(str)==0)return i0;
	}

	return -1;
}
int  CaplStrMap::Find(INT_PTR val) const
{
	for(int i=0;i<items.Size;i++)
	{
		if (items[i]->val == val)return i;
	}
	return -1;
}

bool CaplStrMap::Remove(int index)
{
	if (items.GetSize() <= index) return false;
	items.Remove(index);
	return true;
}

CaplStrMap::CaplStrMapItem* CaplStrMap::GetAt(int index) const
{
	return items[index];
}

bool CaplStrMap::SetAt(int index, INT_PTR val)
{
	if(index<0 || index>items.GetSize()-1) return false;
	
	items[index]->val= val;
	return true;
}

//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
int CaplStrStrMap::compare_CaplStrStrMapItem( const void *arg1, const void *arg2 )
{
	return (*(CaplStrStrMapItem**)arg1)->str.Compare((*(CaplStrStrMapItem**)arg2)->str);
}

int CaplStrStrMap::compareNoCase_CaplStrStrMapItem( const void *arg1, const void *arg2 )
{
	CString s1=(*(CaplStrStrMapItem**)arg1)->str;
	CString s2=(*(CaplStrStrMapItem**)arg2)->str;
	s1.MakeLower();
	s2.MakeLower();
	return s1.Compare(s2);
}

//*************************************************************************
void  CaplStrStrMap::Clear(){ items.Clear();}
//*************************************************************************
int CaplStrStrMap::Add(LPCTSTR str, LPCTSTR val)
{
	//items.strsert(0, new CaplStrMapItem(str,val));
	int new_indx=-1;

	// вставляем так чтобы было упорядоченно
	if(items.Size==0)	{ items.Add( new CaplStrStrMapItem(str,val));new_indx=0;}
	else if(items[0]->str>=str)
	{
		if(items.Data[0]->str==str) items[0]->val=val;
		else items.Insert(0, new CaplStrStrMapItem(str,val));
		new_indx=0;
	}
	else if(items[items.Size-1]->str<=str)
	{
		if(items[items.Size-1]->str==str)  items[items.Size-1]->val=val;
		else items.Add( new CaplStrStrMapItem(str,val));
		new_indx=items.Size-1;
	}
	else
	{
		//Поиск нужного индекса
        int i=0, i0=0, i1=items.Size-1, io=-1;
		// поиск упорядоченного значения
		do
		{
			if((i1-i0)<=1) 
			{
				if(items.Data[i1]->str==str){
					items[i1]->val=val;
				}else{
					items.Insert(i1, new CaplStrStrMapItem(str,val));
				}
				new_indx=i1;
				break;
			}
			io=i;
			i=(i0+i1)/2;
			if(io==i) break;
			if(items[i]->str==str) { items[i]->val=val;new_indx=i; break;}

			if(items[i]->str<=str) i0=i;
			else i1=i;
		}
		while(1);
	}

	//return items.Size;
	return new_indx;
}
//********************************************************
void CaplStrStrMap::Sort()
{
	if(items.Size<2) return;
	qsort(items.Data,items.Size,sizeof(CaplStrStrMapItem*),compare_CaplStrStrMapItem);
}
//********************************************************
CString CaplStrStrMap::Get(LPCTSTR str, bool bNocase) const
{
	if(items.Size<1) return _T("");

	if(bNotUseApiInFind)
	{
		int i=FindByIn(str);
		if(i==-1) return _T("");
		return items[i]->val;
	}

	CaplStrStrMapItem item0(str,0);
	CaplStrStrMapItem *pitem0=&item0;

	CaplStrStrMapItem **item=0;
	if(bNocase)item=(CaplStrStrMapItem**)bsearch(&pitem0,items.Data,items.Size,sizeof(CaplStrStrMapItem*),compareNoCase_CaplStrStrMapItem);
	else item=(CaplStrStrMapItem**)bsearch(&pitem0,items.Data,items.Size,sizeof(CaplStrStrMapItem*),compare_CaplStrStrMapItem);
	if(item==0) return _T("");
	return (*item)->val;
}
//********************************************************
int  CaplStrStrMap::FindByIn(LPCTSTR str) const
{
	if(items.Size<1) return -1;

	int i=0, i0=0, i1=items.Size-1, io=-1, index=-1;
	// поиск упорядоченного значения
	do
	{
		io = i;
		i = (i0 + i1) >> 1; // деление пополам следует заменить побитовым сдвигом на 1 (работает быстрее)
		if(io==i) break;
		if(items[i]->str==str){index=i; break;}

		if(items[i]->str<str) i0=i;
		else i1=i;
	}
	while(1);
	
	if(index!=-1) return index;
	if(items[i1]->str==str)return i1;
	if(items[i0]->str==str)return i0;

	return -1;
}
int  CaplStrStrMap::FindByOut(LPCTSTR val) const
{
	for(int i=0;i<items.Size;i++){
		if(items[i]->val==val)return i;
	}
	return -1;
}

bool CaplStrStrMap::Remove(int index)
{
	if(items.GetSize()<=index) return false;
	items.Remove(index);
	return true;
}

CaplStrStrMap::CaplStrStrMapItem* CaplStrStrMap::GetAt(int index) const
{
	return items[index];
}

bool CaplStrStrMap::SetAt(int index, LPCTSTR val)
{
	if(index<0 || index>items.GetSize()-1) return false;
	
	items[index]->val= val;
	return true;
}


SaplErrorDescription::SaplErrorDescription(int _err_code, LPCTSTR _filename, LPCTSTR _datecompile, int _err_line,
										   LPCTSTR _err_descr, int _core_level, LPCTSTR _funct_name
										   ,LPCTSTR _cls_info , LPCTSTR  _err_descr2)
{
	m_err_code=_err_code;
	m_filename=_filename;
	m_datecompile=_datecompile;
	m_err_line=_err_line;

	m_err_descr=_err_descr;
	m_core_level=_core_level;
	m_funct_name=_funct_name;
	m_cls_info =  _cls_info;
	m_err_descr2 =_err_descr2;
}

SaplErrorDescription::SaplErrorDescription(const SaplErrorDescription& src)
{
    Set(src);
}

SaplErrorDescription::SaplErrorDescription(const SaplErrorDescription& src, LPCTSTR  _filename, LPCTSTR _datecompile, int _err_line,
										   LPCTSTR _err_descr, LPCTSTR _funct_name, LPCTSTR _cls_info)
{
	Set(src,_filename, _datecompile, _err_line,_err_descr, _funct_name,_cls_info);
}

SaplErrorDescription::SaplErrorDescription()
{
	m_err_code=-1;
	m_filename=_T("");
	m_datecompile=_T("");
	m_err_line=0;
	m_err_descr=_T("");
	m_core_level=-1;
	m_funct_name=_T("");
	m_cls_info =  _T("");
	m_err_descr2 =_T("");
}

void SaplErrorDescription::Set(const SaplErrorDescription& src)
{
	SaplErrorDescription *err_tmp;
	int i;

	m_err_code = src.m_err_code;
	m_filename = src.m_filename;
	m_datecompile = src.m_datecompile;
	m_err_line = src.m_err_line;

	m_err_descr = src.m_err_descr;
	m_core_level = src.m_core_level;
	m_funct_name = src.m_funct_name;
	m_cls_info = src.m_cls_info;
	m_err_descr2 = src.m_err_descr2;

	for(i=0;i<src.m_stack.GetSize();i++)
	{
		err_tmp = src.m_stack.GetAt(i);
		m_stack.Add(new SaplErrorDescription(*err_tmp));
	}

}

SaplErrorDescription::~SaplErrorDescription()
{
	int i;
	for(i=0;i<m_stack.GetSize();i++)
	{
		delete m_stack.GetAt(i);
	}
	m_stack.RemoveAll();
}

void SaplErrorDescription::Set(const SaplErrorDescription& src, LPCTSTR _filename, LPCTSTR _datecompile, int _err_line,
							   LPCTSTR _err_descr, LPCTSTR _funct_name, LPCTSTR _cls_info)
{
	SaplErrorDescription *err_tmp;

	Set(src);

    err_tmp = new SaplErrorDescription(-1,_filename, _datecompile, _err_line,_err_descr,0,_funct_name,_cls_info, 0);
	m_stack.Add(err_tmp);

}

CString SaplErrorDescription::Print(LPCTSTR add_info, bool for_mb)
{
	CString description,tmp_buf;
	SaplErrorDescription *err_tmp;
	int i;
	if(for_mb){description = APL_T("Модуль: ");}else{description = _T("Core: ");}
	switch(m_core_level)
	{
	case SaplErrorDescriptionLevel_Transport:
		if(for_mb){description +=  APL_T("транспорт; ");}else{description +=  APL_T("Transport; ");}
		break;
	case SaplErrorDescriptionLevel_CaplData:
		description += APL_T("CaplData; ");
		break;
	case SaplErrorDescriptionLevel_NetStepData:
		description += APL_T("AplTransportClient; ");
		break;
	case SaplErrorDescriptionLevel_Server:
	case SaplErrorDescriptionLevel_ServerHttp:
		if(for_mb){description +=  APL_T("сервер; ");}else{description +=  APL_T("Server; ");}
		break;
	default :
		tmp_buf.Format(_T(" %i; "),m_core_level);
		description += tmp_buf;
	}

	if(m_err_code != -1)
	{
		if(for_mb){description += APL_T(" ");}else{description += APL_T(" Err: ");}
		if(m_core_level == SaplErrorDescriptionLevel_Transport && GetTransportErrorDescription != 0)
			description+= GetTransportErrorDescription(m_err_code);
		if(m_core_level == SaplErrorDescriptionLevel_CaplData && GetStepDataErrorDescription != 0)
			description+= GetStepDataErrorDescription(m_err_code);
		else if(m_core_level == SaplErrorDescriptionLevel_Server && GetServerErrorDescription != 0)
			description+= GetServerErrorDescription(m_err_code);
		else
		{
			// 	case SaplErrorDescriptionLevel_NetStepData:
			//  case SaplErrorDescriptionLevel_ServerHttp:
			tmp_buf.Format(_T(" %i;"), m_err_code);
			description+=tmp_buf;
		}
	}

	if(m_funct_name != _T(""))
	{
		if(for_mb){description += APL_T("\r\nФункция: ");}else{description += _T("\r\n\t\tFun: ");}
		description+=m_funct_name;
	}
	if(add_info != 0) {description+= APL_T("; ")+CString(add_info);}
	if(m_err_descr != _T(""))
	{
		if(for_mb){description += _T("\r\n");}else{description += _T("\r\n\t\tDescr: ");}
		description+= m_err_descr;
	}
	if(m_err_descr2 != _T(""))
	{
		if(for_mb){description += _T("\r\n");}else{description += _T("\r\n\t\tAdd: ");}
		description+= m_err_descr2;
	}
	if(m_cls_info != _T(""))
	{
		if(for_mb){description += APL_T("\r\nКласс: ");}else{description += _T("\r\n\t\tCls: ");}
		description+=m_cls_info;
	}

	if(m_filename != _T(""))
	{
		if(for_mb){description += APL_T("\r\nИсходный код: ");}else{description += _T("\r\n\t\tSrc: ");}
		description+=m_filename;
		if(m_datecompile != _T("")){description+=APL_T(" от ")+m_datecompile;}
	}
	if(m_err_line!=0){tmp_buf.Format(_T(" str %i"),m_err_line); description+=tmp_buf;}

	if(!for_mb && m_stack.GetSize() > 0)
	{
		description += _T("\r\n\t\tErr stack: ");
		for(i=0;i<m_stack.GetSize();i++)
		{
			err_tmp = m_stack.GetAt(i);
			description+=_T("\r\n\t");
			description+=err_tmp->Print(0, for_mb);
		}
	}

	return description;
}

// Если false, то при исключении в CaplStepData::ApploadFromDataBuf показывает MessageBox (режим клиентского приложения)
// Если true то пересылает исключение дальше (режим сервера)
bool retranslate_memory_exeption = false;

void SaplErrorDescription::SetRetranslateMemoryExeption(bool is_retranslate){retranslate_memory_exeption = is_retranslate;}
bool SaplErrorDescription::GetRetranslateMemoryExeption(){return retranslate_memory_exeption;}

// Используется при выводе в строку информации об исключении
// Если источник исключения StepData и функция GetStepDataErrorDescription определена, она возвращает код ошибки для StepData
CString (__cdecl *GetTransportErrorDescription)(int ErrorCode ) = 0;

void SetGetTransportErrorDescription(CString (__cdecl *_GetTransportErrorDescription)(int ErrorCode ))
{
	if(GetTransportErrorDescription == 0) GetTransportErrorDescription = _GetTransportErrorDescription;
}

CString (__cdecl *GetStepDataErrorDescription)(int ErrorCode ) = 0;

void SetGetStepDataErrorDescription(CString (__cdecl *_GetStepDataErrorDescription)(int ErrorCode ))
{
	if(GetStepDataErrorDescription == 0) GetStepDataErrorDescription = _GetStepDataErrorDescription;
}

CString (__cdecl *GetServerErrorDescription)(int ErrorCode ) = 0;

void APL_AGGR_API SetGetServerErrorDescription(CString (__cdecl *_GetServerErrorDescription)(int))
{
	if(GetServerErrorDescription == 0) GetServerErrorDescription = _GetServerErrorDescription;
}


#ifdef __linux__
// Структуры для получения из ядра линукса сведений о состоянии нити
typedef struct __user_cap_header_struct {
   uint32_t version;
   int pid;
} *cap_user_header_t;

typedef struct __user_cap_data_struct {
   uint32_t effective;
   uint32_t permitted;
   uint32_t inheritable;
} *cap_user_data_t;

#define _LINUX_CAPABILITY_VERSION  0x19980330
#endif


////////////////////////////////////////////////////////////////////////////
//
// Класс и массив структур, которые в полуавтоматическмо режиме хранят информацию о текущем стеке вызовов
// В структуру попадает информация для тех функций, в которых определен объект CaplStackLogger
// при выходе из функции - объект автоматом удаляется и информация в структуре обновляется
// в процессе выполнения функции можно задавать комментарии, которые будут записываться в структуру к текущей функции.
//
class CGlobalStackLog : public CaplTAggr<StackLoggerThreadEl*,StackLoggerThreadEl*,APLAGGR_LIST|APLAGGR_AUTOKILLREF>
{
public:
	CGlobalStackLog()
	{
		AppLogFunct = 0;
		StackEmergencyLogFunct = 0;

		pSTACK_LOGGER_PROTECT = new CAplCriticalSection();
	}
	~CGlobalStackLog()
	{
		Clear();
		delete pSTACK_LOGGER_PROTECT;
		pSTACK_LOGGER_PROTECT = 0;
	}

// управление доступом к структурам
	CAplCriticalSection *pSTACK_LOGGER_PROTECT;

	// Функция вывода сообщений в лог приложения. Используется только если задана.
	// С помощью этой функции вызывающему модулю передается информация об возникших ошибках.
	void (__cdecl *AppLogFunct)(LPCTSTR mess, LPCTSTR funct, LPCTSTR class_info);

	// функция вывода сообщений в аварийный лог сервера. В нее выводится вся информация о текущих действиях по стеку (т.е. может замедляь работу!)
	void (__cdecl *StackEmergencyLogFunct)(LPCTSTR mess);

	
	int Add(StackLoggerThreadEl* data)
	{
		Renew();
		return CaplTAggr<StackLoggerThreadEl*,StackLoggerThreadEl*,APLAGGR_LIST|APLAGGR_AUTOKILLREF>::Add(data);
	}


	inline void Renew()
	{
		CaplEnterCriticalSection CriticalSection(pSTACK_LOGGER_PROTECT);
		StackLoggerThreadEl* el;
		for(int i=0; i<Size; i++)
		{
			el = GetAt(i);
#ifdef __linux__
			//проверяем, жива ли нитка, через системный вызов
			__user_cap_header_struct hdrp;
			__user_cap_data_struct datap;
			hdrp.version = _LINUX_CAPABILITY_VERSION;
			hdrp.pid = el->m_thread_id;
			int err1 = syscall(SYS_capget, &hdrp, &datap);
			if(err1 == -1)
			{
				err1 = errno;
				if(err1 == ESRCH)
				{
					// удаляем запись для этой нити
					Remove(i);
					i--;
				}
			}

#else
			DWORD ExitCode = 0;
			if(GetExitCodeThread(el->m_thread_h, &ExitCode) == TRUE && ExitCode != STILL_ACTIVE)
			{
				// удаляем запись для этой нити
				Remove(i);
				i--;
			}
#endif
		}
	}


	CAplAtomicLong StackLoggerMode;

};

CGlobalStackLog* pGlobalStackLog = 0;

bool GlobalStackLogInit(void (*_AppLogFunct)(LPCTSTR mess, LPCTSTR funct, LPCTSTR class_info), void (*_StackEmergencyLogFunct)(LPCTSTR mess))
{
	if(pGlobalStackLog==0)
	{
		pGlobalStackLog = new CGlobalStackLog();
	}
	if(pGlobalStackLog!=0)
	{
		CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);
		pGlobalStackLog->StackLoggerMode.Set(1);
		pGlobalStackLog->AppLogFunct = _AppLogFunct;
		pGlobalStackLog->StackEmergencyLogFunct = _StackEmergencyLogFunct;
	}
	return pGlobalStackLog != 0;
}

void GlobalStackLogDestroy()
{
	if(pGlobalStackLog!=0) delete pGlobalStackLog;
	pGlobalStackLog = 0;
}


// В этом массиве хранятся данные о стеке вызовов всех нитей, в функциях которых засветились экземпляры класса CaplStackLogger
// для каждой нити - хранится один экземпляр класса StackLoggerThreadEl

bool SetStackLoggerMode(bool mode)
{
	if(pGlobalStackLog!=0)
	{
		pGlobalStackLog->StackLoggerMode.Set(mode?1:0);
		return (pGlobalStackLog->StackLoggerMode.Get() > 0);
	}
	return false;
}

#ifdef __linux__
	#define THREAD_FORMAT   _T("t%04i")
#else
	#define THREAD_FORMAT   _T("t0x%04X")
#endif


// В экземплярах этой структуры хранится информация о текущем стеке вызовов (количество уровней+названия функций+комментарии к ним, если они заданы) одной нити
// названия функций и комментарии хоранятся в строковоq карте CaplStrStrMap
StackLoggerThreadEl::StackLoggerThreadEl()
{
	m_thread_id = GetCurrentThreadId();
#ifndef __linux__
	m_thread_h = OpenThread(THREAD_QUERY_INFORMATION, FALSE, m_thread_id);
#endif
	m_level = 0;

	if(pGlobalStackLog!=0){	pGlobalStackLog->Add(this);}

	m_PrevFunctName = _T("");
	m_count_duplicate = 0;
	m_level_dup = 0;

}
StackLoggerThreadEl::~StackLoggerThreadEl()
{
	m_level = 0;
#ifndef __linux__
	CloseHandle(m_thread_h);
#endif
}

void StackLoggerThreadEl::Enter(CString &funct_name,bool force_write)
{
	int indx;
	int prev_duplicate = 0;
	SYSTEMTIME m_Time;
	CString tmp,funct_name_orig = funct_name;
	bool time_set = false;

	if(pGlobalStackLog==0) return;

	tmp.Format(THREAD_FORMAT _T(" L%03i:%05i "), m_thread_id, m_level, GetTickCount());
	funct_name = tmp + funct_name_orig;
	m_stack_log.Add(funct_name,_T(""));
	
	if( m_level == 1050 && pGlobalStackLog->AppLogFunct !=0 )
	{
		CString str;
		GetLocalTime(&m_Time);
		tmp.Format(_T("%02i:%02i:%02i.%03i "),m_Time.wHour,m_Time.wMinute,m_Time.wSecond,m_Time.wMilliseconds);
		time_set = true;

		str.Format(tmp + APL_T("Глубина стека превысила %i !! Функция "),m_level);
		str += funct_name;
		str += APL_T("\r\nПервые 10 записей: ");
		for(indx = 0; indx < 10;indx++)
		{
			str += _T("\r\n") + m_stack_log.GetAt(indx)->str;
			if(m_stack_log.GetAt(indx)->val != _T("")) str += _T(" (") + m_stack_log.GetAt(indx)->val + _T(")");
		}
		str += APL_T("\r\nПоследние 10 записей: ");
		for(indx = m_stack_log.GetSize()-2; indx > m_stack_log.GetSize()-12;indx--)
		{
			str += _T("\r\n") + m_stack_log.GetAt(indx)->str;
			if(m_stack_log.GetAt(indx)->val != _T("")) str += _T(" (") + m_stack_log.GetAt(indx)->val + _T(")");
		}
		pGlobalStackLog->AppLogFunct(str,_T("StackLoggerThreadEl::Leave"),0);
	}
	if( m_level >= 1100 )
	{
		CString str;
		if(!time_set)
		{
			GetLocalTime(&m_Time);
			tmp.Format(_T("%02i:%02i:%02i.%03i "),m_Time.wHour,m_Time.wMinute,m_Time.wSecond,m_Time.wMilliseconds);
			time_set = true;
		}
		str.Format(APL_T("Глубина стека превысила %i !! Функция "),m_level);
		str += funct_name;
        APL_THROW_EX(106,tmp + str);
	}
	if(!force_write && m_PrevFunctName == funct_name_orig)
	{
		m_count_duplicate++;
		m_level_dup = m_level;
	}
	else
	{
		prev_duplicate = m_count_duplicate;
		m_count_duplicate = 0;
		m_level_dup = 0;
	}
	m_level++;

	if(pGlobalStackLog->StackEmergencyLogFunct !=0 )
	{
		CString str;
		if(!time_set)
		{
			GetLocalTime(&m_Time);
			tmp.Format(_T("%02i:%02i:%02i.%03i "),m_Time.wHour,m_Time.wMinute,m_Time.wSecond,m_Time.wMilliseconds);
			time_set = true;
		}
		if(prev_duplicate > 0)
		{
			str.Format(funct_name + m_PrevFunctName + _T(" in repeated: %i "),prev_duplicate);
			pGlobalStackLog->StackEmergencyLogFunct(tmp + str);
		}
		if(m_count_duplicate == 0)
		{
			str = funct_name;
			str+= _T("-<");
			pGlobalStackLog->StackEmergencyLogFunct(tmp + str);
		}
	}
	m_PrevFunctName = funct_name_orig;
}

void StackLoggerThreadEl::Leave(CString &funct_name)
{
	SYSTEMTIME m_Time;
	CString tmp;
	CString str;
	bool time_set = false;

	if(pGlobalStackLog == 0) return;

	int indx = m_stack_log.FindByIn(funct_name);
	if(indx != m_stack_log.GetSize()-1 && pGlobalStackLog->AppLogFunct !=0 )
	{
		GetLocalTime(&m_Time);
		tmp.Format(_T("%02i:%02i:%02i.%03i "),m_Time.wHour,m_Time.wMinute,m_Time.wSecond,m_Time.wMilliseconds);
		time_set = true;
		str = funct_name;
		if(m_stack_log.GetAt(indx)->val != "") str += _T("(") + m_stack_log.GetAt(indx)->val + _T(")");
		str += APL_T(" Не последняя в стеке, а из нее произведен выход!");
		pGlobalStackLog->AppLogFunct(tmp + str, _T("StackLoggerThreadEl::Leave"),0);
	}
	m_stack_log.Remove(indx);
	m_level--;

	if(pGlobalStackLog->StackEmergencyLogFunct !=0)
	{
		if(m_count_duplicate!=0 && m_level < m_level_dup)
		{
			if(!time_set)
			{
				GetLocalTime(&m_Time);
				tmp.Format(_T("%02i:%02i:%02i.%03i "),m_Time.wHour,m_Time.wMinute,m_Time.wSecond,m_Time.wMilliseconds);
				time_set = true;
			}
			str.Format(funct_name + m_PrevFunctName + _T(" out repeated: %i "),m_count_duplicate);
			pGlobalStackLog->StackEmergencyLogFunct(tmp + str);
			m_count_duplicate = 0;
			m_level_dup = 0;
		}
		if(m_count_duplicate == 0)
		{
			if(!time_set)
			{
				GetLocalTime(&m_Time);
				tmp.Format(_T("%02i:%02i:%02i.%03i "),m_Time.wHour,m_Time.wMinute,m_Time.wSecond,m_Time.wMilliseconds);
				time_set = true;
			}
			str = funct_name;
			str+= _T("->");
			pGlobalStackLog->StackEmergencyLogFunct(tmp + str);
		}
	}
}

CString StackLoggerThreadEl::GetCurrStackList()
{
	CString ret_val=_T("StackLog:");
	for(DWORD i=0;i<m_level;i++)
	{
		ret_val+=_T("\n");
		ret_val+=m_stack_log.GetAt(i)->str;
	}
	ret_val+=_T("\n");
	return ret_val;
}

void StackLoggerThreadEl::SetMess(CString &funct_name, LPCTSTR message)
{
	if(pGlobalStackLog==0) return;

	m_stack_log.Add(funct_name,message);
	if(pGlobalStackLog->StackEmergencyLogFunct !=0 )
	{
		CString str;
		SYSTEMTIME m_Time;
		CString tmp;
		GetLocalTime(&m_Time);
		tmp.Format(_T(" %02i:%02i:%02i.%03i "),m_Time.wHour,m_Time.wMinute,m_Time.wSecond,m_Time.wMilliseconds);
		
		str = funct_name;
		if(m_count_duplicate > 0)
		{
			CString str1;
			str1.Format(_T(" N %i"),m_count_duplicate);
			str+= str1;
		}
		str+= _T("-: ");
		str+= message;
		pGlobalStackLog->StackEmergencyLogFunct(tmp + str);
	}
}

/////////////////////////////////////////////////////////////////////////////////////
CaplStackLogger::CaplStackLogger(LPCTSTR function_name,bool force_write)
{
	ASSERT(function_name);

	int i;

	m_cur_el=0;
	m_p_global_stack = 0;
	m_thread_id = 0;
	m_id = _T(" ");

	if(pGlobalStackLog==0) return;

	CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);

	if(pGlobalStackLog->StackLoggerMode.Get()>0)
	{
		m_thread_id = GetCurrentThreadId();
		m_id = _T("Fun ");
		m_id += function_name;

		m_p_global_stack = pGlobalStackLog;

		for(i=0;i<pGlobalStackLog->GetSize();i++)
		{
			if(pGlobalStackLog->GetAt(i)->m_thread_id == m_thread_id)
			{
				m_cur_el = pGlobalStackLog->GetAt(i);
				break;
			}
		}
		if(m_cur_el == 0)
		{
			m_cur_el = new StackLoggerThreadEl();
		}
		try{
			m_cur_el->Enter(m_id,force_write);
		}
		catch(SaplErrorDescription error)
		{
			DEBUG_BEEP(1000,100);
			DEBUG_BEEP(1400,100);
		}
	}
}

CaplStackLogger::~CaplStackLogger()
{
	Leave();
}

void CaplStackLogger::Leave()
{
	if(pGlobalStackLog==0) return;

	if(pGlobalStackLog->StackLoggerMode.Get()>0)
	{
		CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);
		if(m_cur_el != 0)
		{
			try{
				m_cur_el->Leave(m_id);
			}
			catch(SaplErrorDescription error)
			{
				DEBUG_BEEP(1000,100);
				DEBUG_BEEP(1400,100);
			}

			m_cur_el = 0;
		}
	}
}

void CaplStackLogger::SetFunctionInfo(LPCTSTR function_info)
{
	if(pGlobalStackLog==0) return;

	if(pGlobalStackLog->StackLoggerMode.Get()>0)
	{
		CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);
		if(m_cur_el != 0)
		{
			m_cur_el->SetMess(m_id,function_info);
		}
	}
}

CString CaplStackLogger::GetCurrStackList()
{
	CString ret_val = _T("");

	if(pGlobalStackLog==0) return ret_val ;

	if(pGlobalStackLog->StackLoggerMode.Get()>0)
	{
		CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);
		if(m_cur_el != 0)
		{
			ret_val = m_cur_el->GetCurrStackList();
		}
	}
	return ret_val;
}

void CaplStackLogger::ExternalEnter(CString &function_name,bool force_write)
{
	int i;

	if(pGlobalStackLog==0) return;

	DWORD thread_id = GetCurrentThreadId();

	StackLoggerThreadEl* cur_el = 0;
	CString id = function_name;

	if(pGlobalStackLog->StackLoggerMode.Get()>0)
	{
		CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);

		for(i=0;i<pGlobalStackLog->GetSize();i++)
		{
			if(pGlobalStackLog->GetAt(i)->m_thread_id == thread_id)
			{
				cur_el = pGlobalStackLog->GetAt(i);
				break;
			}
		}
		if(cur_el == 0)
		{
			cur_el = new StackLoggerThreadEl();
		}
		try{
			cur_el->Enter(id,force_write);
		}
		catch(SaplErrorDescription error)
		{
			DEBUG_BEEP(1000,100);
			DEBUG_BEEP(1400,100);
		}
		function_name = id;
	}
}

void CaplStackLogger::ExternalLeave(CString &function_name)
{
	int i;

	if(pGlobalStackLog==0) return;

	DWORD thread_id = GetCurrentThreadId();

	StackLoggerThreadEl* cur_el = 0;

	if(pGlobalStackLog->StackLoggerMode.Get()>0)
	{
		CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);
		for(i=0;i<pGlobalStackLog->GetSize();i++)
		{
			if(pGlobalStackLog->GetAt(i)->m_thread_id == thread_id)
			{
				cur_el = pGlobalStackLog->GetAt(i);
				break;
			}
		}
		if(cur_el != 0)
		{
			try{
				cur_el->Leave(function_name);
			}
			catch(SaplErrorDescription error)
			{
				DEBUG_BEEP(1000,100);
				DEBUG_BEEP(1400,100);
			}

		}
	}
}



void CaplStackLogger::ExternalSetFunctionInfo(CString &function_name, LPCTSTR function_info)
{
	int i;

	if(pGlobalStackLog==0) return;

	DWORD thread_id = GetCurrentThreadId();

	StackLoggerThreadEl* cur_el = 0;

	if(pGlobalStackLog->StackLoggerMode.Get()>0)
	{
		CaplEnterCriticalSection CriticalSection(pGlobalStackLog->pSTACK_LOGGER_PROTECT);
		for(i=0;i<pGlobalStackLog->GetSize();i++)
		{
			if(pGlobalStackLog->GetAt(i)->m_thread_id == thread_id)
			{
				cur_el = pGlobalStackLog->GetAt(i);
				break;
			}
		}
		if(cur_el != 0)
		{
			cur_el->SetMess(function_name,function_info);
		}
	}
}


//////////////////////////////////////////////////////////////
// Класс для быстрого измерения тайм-аутов, с защитой от переполнения DWORDа
//
CaplTimeMeter::CaplTimeMeter(bool isStart)
{
    if(isStart)	Start();
    else Zero();
}
CaplTimeMeter::~CaplTimeMeter()
{
}
// сбрасывает значения - чтобы при первой проверке чека - всегда выдавалось true
void CaplTimeMeter::Zero()
{
	CaplEnterCriticalSection CriticalSection(&m_DataThreadProtect);
	m_TickStart = 0;
	m_TickOffset = 0;
}
long gl_HalfDWORD = 0;

DWORD CaplTimeMeter::GetHalfDWORD()
{
	if(gl_HalfDWORD==0)
	{
		int size = sizeof(DWORD);
		gl_HalfDWORD = 0x80;
		gl_HalfDWORD <<=((size-1)*8);
	}
	return gl_HalfDWORD;
}

// устанавливает счетчик
DWORD CaplTimeMeter::Start()
{
	DWORD retval;
	CaplEnterCriticalSection CriticalSection(&m_DataThreadProtect);
	m_TickStart = GetTickCount();
	if(m_TickStart > GetHalfDWORD())
	{
		m_TickOffset = GetHalfDWORD();
		m_TickStart-=m_TickOffset;
	}
	else
	{
		m_TickOffset = 0;
	}
	retval = m_TickStart;
	return retval;
}
// проверяет, прошло ли с момента старта TickDelta милисекунд. Если прошло - true, иначе - false
bool CaplTimeMeter::CheckTimeOut(DWORD TickDelta)
{
	bool retval;
	CaplEnterCriticalSection CriticalSection(&m_DataThreadProtect);
	retval = (GetTickCount()- m_TickOffset) > (m_TickStart + TickDelta);
	return retval;
}

DWORD CaplTimeMeter::GetTickDelta()
{
	DWORD retval;
	CaplEnterCriticalSection CriticalSection(&m_DataThreadProtect);
	retval = GetTickCount()- m_TickOffset - m_TickStart;
	return retval;
}

#ifdef _MFC_VER
int InitializeExcell(IDispatch *&pXlApp, bool bShowMessge) 
{
	HRESULT hr;
	CLSID clsid;

	//Инциализируем COM
	hr = CoInitialize(NULL);
	if (FAILED(hr))
	{
		if(bShowMessge)
			aplMessageBox(APL_T("Не удалось получить инициализировать подсистему OLE-COM!"), MB_OK|MB_ICONERROR );
		return 1;
	}

	//Получаем ID на Excel		
	hr = CLSIDFromProgID(L"Excel.Application", &clsid);
	if(FAILED(hr)) 
	{
		if(bShowMessge)
			aplMessageBox(APL_T("Не удалось получить clsid для Microsoft Excel. Попробуйте переустановить MS Excel."), MB_OK|MB_ICONERROR ) ;
		CoUninitialize();

		return 1;
	}

	return 0;
}

bool aplCheckExcel(bool bShowMessge)
{
	IDispatch *pXlApp = 0;
	int result = InitializeExcell(pXlApp, bShowMessge);

	if(pXlApp)
	{
		pXlApp->Release();
		::CoUninitialize();
	}

	return (result == 0 ? true : false);
}
#endif

void aplCorrectFileName(CString& sFilename, TCHAR cReplaceWith/*=_T('_')*/)
{
	if(sFilename.IsEmpty()==true)
	{
		return;
	}
	sFilename.Replace(_T('\n'),cReplaceWith);
	sFilename.Replace(_T('\\'),cReplaceWith);
	sFilename.Replace(_T('/'),cReplaceWith);
	sFilename.Replace(_T(':'),cReplaceWith);
	sFilename.Replace(_T('*'),cReplaceWith);
	sFilename.Replace(_T('?'),cReplaceWith);
	sFilename.Replace(_T('"'),cReplaceWith);
	sFilename.Replace(_T('<'),cReplaceWith);
	sFilename.Replace(_T('>'),cReplaceWith);
	sFilename.Replace(_T('|'),cReplaceWith);
}

// Проверяет, можно ли использовать строку в имени Lite БД или в имени подключения
bool APL_AGGR_API IsNameCorrect(CString &tested, CString &err_mess, bool is_db_name)
{
	err_mess.Empty();
	bool isErr = false;
	if(tested.GetLength() < 2)
	{
		if(is_db_name) err_mess = APL_T("Имя БД должно быть длиной два символа или больше!");
		else err_mess = APL_T("Имя подключения должно быть длиной два символа или больше!");
		return false;
	}
	isErr = (tested.Find(_T(':'))!=-1 || tested.Find(_T('\\'))!=-1 || tested.Find(_T('/'))!=-1);
	if(!isErr) 
	{
		if(is_db_name)
		{
			isErr = (tested.Find(_T('<'))!=-1 || tested.Find(_T('>'))!=-1 || 
				tested.Find(_T('*'))!=-1 || tested.Find(_T('?'))!=-1 || 
				tested.Find(_T('|'))!=-1 || tested.Find(_T('"'))!=-1);
		}
		else
			isErr = (tested.Find(_T('='))!=-1 );
	}		

	if(isErr)
	{
		if(is_db_name) err_mess = APL_T("Имя БД не должно включать символы \'\\\', \'/\', \'<\', \'>\', \'*\', \'?\', \':\', \'\"\' и \'|\'!");
		else err_mess = APL_T("Имя подключения не должно включать символы \':\', \'\\\', \'/\' и \'=\'");
		return false;
	}
	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Функция для перевода идентификатора windows message в текстовый вид
// Пример см в aplAggr.h

#ifdef _MFC_VER
struct AFX_MAP_MESSAGE
{
	UINT    nMsg;
	LPCSTR  lpszMsg;
};
#define DEFINE_MESSAGE(wm){ wm, #wm }
static const AFX_MAP_MESSAGE allMessages[] =
{
	DEFINE_MESSAGE(WM_CREATE),
	DEFINE_MESSAGE(WM_DESTROY),
	DEFINE_MESSAGE(WM_MOVE),
	DEFINE_MESSAGE(WM_SIZE),
	DEFINE_MESSAGE(WM_ACTIVATE),
	DEFINE_MESSAGE(WM_SETFOCUS),
	DEFINE_MESSAGE(WM_KILLFOCUS),
	DEFINE_MESSAGE(WM_ENABLE),
	DEFINE_MESSAGE(WM_SETREDRAW),
	DEFINE_MESSAGE(WM_SETTEXT),
	DEFINE_MESSAGE(WM_GETTEXT),
	DEFINE_MESSAGE(WM_GETTEXTLENGTH),
	DEFINE_MESSAGE(WM_PAINT),
	DEFINE_MESSAGE(WM_CLOSE),
	DEFINE_MESSAGE(WM_QUERYENDSESSION),
	DEFINE_MESSAGE(WM_QUIT),
	DEFINE_MESSAGE(WM_QUERYOPEN),
	DEFINE_MESSAGE(WM_ERASEBKGND),
	DEFINE_MESSAGE(WM_SYSCOLORCHANGE),
	DEFINE_MESSAGE(WM_ENDSESSION),
	DEFINE_MESSAGE(WM_SHOWWINDOW),
	DEFINE_MESSAGE(WM_CTLCOLORMSGBOX),
	DEFINE_MESSAGE(WM_CTLCOLOREDIT),
	DEFINE_MESSAGE(WM_CTLCOLORLISTBOX),
	DEFINE_MESSAGE(WM_CTLCOLORBTN),
	DEFINE_MESSAGE(WM_CTLCOLORDLG),
	DEFINE_MESSAGE(WM_CTLCOLORSCROLLBAR),
	DEFINE_MESSAGE(WM_CTLCOLORSTATIC),
	DEFINE_MESSAGE(WM_WININICHANGE),
	DEFINE_MESSAGE(WM_SETTINGCHANGE),
	DEFINE_MESSAGE(WM_DEVMODECHANGE),
	DEFINE_MESSAGE(WM_ACTIVATEAPP),
	DEFINE_MESSAGE(WM_FONTCHANGE),
	DEFINE_MESSAGE(WM_TIMECHANGE),
	DEFINE_MESSAGE(WM_CANCELMODE),
	DEFINE_MESSAGE(WM_SETCURSOR),
	DEFINE_MESSAGE(WM_MOUSEACTIVATE),
	DEFINE_MESSAGE(WM_CHILDACTIVATE),
	DEFINE_MESSAGE(WM_QUEUESYNC),
	DEFINE_MESSAGE(WM_GETMINMAXINFO),
	DEFINE_MESSAGE(WM_ICONERASEBKGND),
	DEFINE_MESSAGE(WM_NEXTDLGCTL),
	DEFINE_MESSAGE(WM_SPOOLERSTATUS),
	DEFINE_MESSAGE(WM_DRAWITEM),
	DEFINE_MESSAGE(WM_MEASUREITEM),
	DEFINE_MESSAGE(WM_DELETEITEM),
	DEFINE_MESSAGE(WM_VKEYTOITEM),
	DEFINE_MESSAGE(WM_CHARTOITEM),
	DEFINE_MESSAGE(WM_SETFONT),
	DEFINE_MESSAGE(WM_GETFONT),
	DEFINE_MESSAGE(WM_QUERYDRAGICON),
	DEFINE_MESSAGE(WM_COMPAREITEM),
	DEFINE_MESSAGE(WM_COMPACTING),
	DEFINE_MESSAGE(WM_NCCREATE),
	DEFINE_MESSAGE(WM_NCDESTROY),
	DEFINE_MESSAGE(WM_NCCALCSIZE),
	DEFINE_MESSAGE(WM_NCHITTEST),
	DEFINE_MESSAGE(WM_NCPAINT),
	DEFINE_MESSAGE(WM_NCACTIVATE),
	DEFINE_MESSAGE(WM_GETDLGCODE),
	DEFINE_MESSAGE(WM_NCMOUSEMOVE),
	DEFINE_MESSAGE(WM_NCLBUTTONDOWN),
	DEFINE_MESSAGE(WM_NCLBUTTONUP),
	DEFINE_MESSAGE(WM_NCLBUTTONDBLCLK),
	DEFINE_MESSAGE(WM_NCRBUTTONDOWN),
	DEFINE_MESSAGE(WM_NCRBUTTONUP),
	DEFINE_MESSAGE(WM_NCRBUTTONDBLCLK),
	DEFINE_MESSAGE(WM_NCMBUTTONDOWN),
	DEFINE_MESSAGE(WM_NCMBUTTONUP),
	DEFINE_MESSAGE(WM_NCMBUTTONDBLCLK),
	DEFINE_MESSAGE(WM_KEYDOWN),
	DEFINE_MESSAGE(WM_KEYUP),
	DEFINE_MESSAGE(WM_CHAR),
	DEFINE_MESSAGE(WM_DEADCHAR),
	DEFINE_MESSAGE(WM_SYSKEYDOWN),
	DEFINE_MESSAGE(WM_SYSKEYUP),
	DEFINE_MESSAGE(WM_SYSCHAR),
	DEFINE_MESSAGE(WM_SYSDEADCHAR),
	DEFINE_MESSAGE(WM_KEYLAST),
	DEFINE_MESSAGE(WM_INITDIALOG),
	DEFINE_MESSAGE(WM_COMMAND),
	DEFINE_MESSAGE(WM_SYSCOMMAND),
	DEFINE_MESSAGE(WM_TIMER),
	DEFINE_MESSAGE(WM_HSCROLL),
	DEFINE_MESSAGE(WM_VSCROLL),
	DEFINE_MESSAGE(WM_INITMENU),
	DEFINE_MESSAGE(WM_INITMENUPOPUP),
	DEFINE_MESSAGE(WM_MENUSELECT),
	DEFINE_MESSAGE(WM_MENUCHAR),
	DEFINE_MESSAGE(WM_ENTERIDLE),
	DEFINE_MESSAGE(WM_MOUSEWHEEL),
	DEFINE_MESSAGE(WM_MOUSEMOVE),
	DEFINE_MESSAGE(WM_LBUTTONDOWN),
	DEFINE_MESSAGE(WM_LBUTTONUP),
	DEFINE_MESSAGE(WM_LBUTTONDBLCLK),
	DEFINE_MESSAGE(WM_RBUTTONDOWN),
	DEFINE_MESSAGE(WM_RBUTTONUP),
	DEFINE_MESSAGE(WM_RBUTTONDBLCLK),
	DEFINE_MESSAGE(WM_MBUTTONDOWN),
	DEFINE_MESSAGE(WM_MBUTTONUP),
	DEFINE_MESSAGE(WM_MBUTTONDBLCLK),
	DEFINE_MESSAGE(WM_PARENTNOTIFY),
	DEFINE_MESSAGE(WM_MDICREATE),
	DEFINE_MESSAGE(WM_MDIDESTROY),
	DEFINE_MESSAGE(WM_MDIACTIVATE),
	DEFINE_MESSAGE(WM_MDIRESTORE),
	DEFINE_MESSAGE(WM_MDINEXT),
	DEFINE_MESSAGE(WM_MDIMAXIMIZE),
	DEFINE_MESSAGE(WM_MDITILE),
	DEFINE_MESSAGE(WM_MDICASCADE),
	DEFINE_MESSAGE(WM_MDIICONARRANGE),
	DEFINE_MESSAGE(WM_MDIGETACTIVE),
	DEFINE_MESSAGE(WM_MDISETMENU),
	DEFINE_MESSAGE(WM_CUT),
	DEFINE_MESSAGE(WM_COPYDATA),
	DEFINE_MESSAGE(WM_COPY),
	DEFINE_MESSAGE(WM_PASTE),
	DEFINE_MESSAGE(WM_CLEAR),
	DEFINE_MESSAGE(WM_UNDO),
	DEFINE_MESSAGE(WM_RENDERFORMAT),
	DEFINE_MESSAGE(WM_RENDERALLFORMATS),
	DEFINE_MESSAGE(WM_DESTROYCLIPBOARD),
	DEFINE_MESSAGE(WM_DRAWCLIPBOARD),
	DEFINE_MESSAGE(WM_PAINTCLIPBOARD),
	DEFINE_MESSAGE(WM_VSCROLLCLIPBOARD),
	DEFINE_MESSAGE(WM_SIZECLIPBOARD),
	DEFINE_MESSAGE(WM_ASKCBFORMATNAME),
	DEFINE_MESSAGE(WM_CHANGECBCHAIN),
	DEFINE_MESSAGE(WM_HSCROLLCLIPBOARD),
	DEFINE_MESSAGE(WM_QUERYNEWPALETTE),
	DEFINE_MESSAGE(WM_PALETTEISCHANGING),
	DEFINE_MESSAGE(WM_PALETTECHANGED),
	DEFINE_MESSAGE(WM_DDE_INITIATE),
	DEFINE_MESSAGE(WM_DDE_TERMINATE),
	DEFINE_MESSAGE(WM_DDE_ADVISE),
	DEFINE_MESSAGE(WM_DDE_UNADVISE),
	DEFINE_MESSAGE(WM_DDE_ACK),
	DEFINE_MESSAGE(WM_DDE_DATA),
	DEFINE_MESSAGE(WM_DDE_REQUEST),
	DEFINE_MESSAGE(WM_DDE_POKE),
	DEFINE_MESSAGE(WM_DDE_EXECUTE),
	DEFINE_MESSAGE(WM_DROPFILES),
	DEFINE_MESSAGE(WM_POWER),
	DEFINE_MESSAGE(WM_WINDOWPOSCHANGED),
	DEFINE_MESSAGE(WM_WINDOWPOSCHANGING),
// MFC specific messages
	DEFINE_MESSAGE(WM_SIZEPARENT),
	DEFINE_MESSAGE(WM_SETMESSAGESTRING),
	DEFINE_MESSAGE(WM_IDLEUPDATECMDUI),
	DEFINE_MESSAGE(WM_INITIALUPDATE),
	DEFINE_MESSAGE(WM_COMMANDHELP),
	DEFINE_MESSAGE(WM_HELPHITTEST),
	DEFINE_MESSAGE(WM_EXITHELPMODE),
	DEFINE_MESSAGE(WM_HELP),
	DEFINE_MESSAGE(WM_NOTIFY),
	DEFINE_MESSAGE(WM_CONTEXTMENU),
	DEFINE_MESSAGE(WM_TCARD),
	DEFINE_MESSAGE(WM_MDIREFRESHMENU),
	DEFINE_MESSAGE(WM_MOVING),
	DEFINE_MESSAGE(WM_STYLECHANGED),
	DEFINE_MESSAGE(WM_STYLECHANGING),
	DEFINE_MESSAGE(WM_SIZING),
	DEFINE_MESSAGE(WM_SETHOTKEY),
	DEFINE_MESSAGE(WM_PRINT),
	DEFINE_MESSAGE(WM_PRINTCLIENT),
	DEFINE_MESSAGE(WM_POWERBROADCAST),
	DEFINE_MESSAGE(WM_HOTKEY),
	DEFINE_MESSAGE(WM_GETICON),
	DEFINE_MESSAGE(WM_EXITMENULOOP),
	DEFINE_MESSAGE(WM_ENTERMENULOOP),
	DEFINE_MESSAGE(WM_DISPLAYCHANGE),
	DEFINE_MESSAGE(WM_STYLECHANGED),
	DEFINE_MESSAGE(WM_STYLECHANGING),
	DEFINE_MESSAGE(WM_GETICON),
	DEFINE_MESSAGE(WM_SETICON),
	DEFINE_MESSAGE(WM_SIZING),
	DEFINE_MESSAGE(WM_MOVING),
	DEFINE_MESSAGE(WM_CAPTURECHANGED),
	DEFINE_MESSAGE(WM_DEVICECHANGE),
	DEFINE_MESSAGE(WM_PRINT),
	DEFINE_MESSAGE(WM_PRINTCLIENT),
	{ 0, NULL, }    // end of message list
};

char * aplGetStringFromMsg( DWORD dwMessage, bool bShowFrequentMessages )
{
	if ( !bShowFrequentMessages && 
		( dwMessage == WM_MOUSEMOVE         || 
		  dwMessage == WM_NCMOUSEMOVE		||
		  dwMessage == WM_NCHITTEST		    || 
		  dwMessage == WM_SETCURSOR		    ||
		  dwMessage == WM_CTLCOLORBTN		||
		  dwMessage == WM_CTLCOLORDLG		||
		  dwMessage == WM_CTLCOLOREDIT	    ||
		  dwMessage == WM_CTLCOLORLISTBOX	||
		  dwMessage == WM_CTLCOLORMSGBOX    ||
		  dwMessage == WM_CTLCOLORSCROLLBAR ||
		  dwMessage == WM_CTLCOLORSTATIC    ||
		  dwMessage == WM_ENTERIDLE         || 
		  dwMessage == WM_CANCELMODE        ||
		  dwMessage == 0x0118) )    // WM_SYSTIMER (caret blink)
	{
		// don't report very frequently sent messages
		return "";
	}
	const AFX_MAP_MESSAGE* pMapMsg = allMessages;
	for (/*null*/; pMapMsg->lpszMsg != NULL; pMapMsg++)
	{
		if (pMapMsg->nMsg == dwMessage )
		{
			return (char *)pMapMsg->lpszMsg;
		}
	}
	return "";
}
#endif


void aplGetDescriptionSystemError(int error_id,CString &buf, bool full_mess)
{
#ifdef _MFC_VER
	LPVOID cbuf;
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,0,error_id,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &cbuf,0,0);

	if(full_mess)
		buf.Format( APL_T("Код ошибки: %i;\nОписание ошибки: %s"), error_id, (LPTSTR)cbuf);
	else
		buf.Format( APL_T("Описание ошибки: %s"), (LPTSTR)cbuf);
	LocalFree( cbuf );
	buf.Remove(_T('\r'));

#else

	char *err_desr=strerror(error_id);
	CString sErrDecr;
	if(0!=err_desr) sErrDecr=(LPCTSTR)CaplStringAdapter(err_desr); else sErrDecr=_T("?");
	if(full_mess)
		buf.Format( APL_T("Код: %i;\nОписание: %s"), error_id, (LPCTSTR)sErrDecr);
	else
		buf.Format( APL_T("Описание ошибки: %s"), (LPCTSTR)sErrDecr);
#endif
}


void aplGetTempPath(CString &tmp_path)
{
#ifdef __linux__
	tmp_path.GetEnvironmentVariable(_T("HOME"));
#else
	tmp_path.GetEnvironmentVariable(_T("TEMP"));
#endif
	if(tmp_path==_T("")) return;
	if(tmp_path[tmp_path.GetLength()-1]!=aplDirRazd) 	tmp_path+=aplDirRazd;	
}

//*****************************************************
bool aplGetTempFileName(LPCTSTR prefix, LPCTSTR ext, CString &fname)
{
	fname=_T("");
	CString mask;
	aplGetTempPath(mask);
	mask+=prefix; mask+=_T("%i."); mask+=ext;

	int i=0;
	while(true)
	{
		i++;
		fname.Format(LPCTSTR(mask),i);
		if(CaplFile::IsFileExist(fname)) continue;
		break;
	}
	return true;
}
//*****************************************************
bool aplGetModuleFileName(CString &fname)
{
	fname=_T("");

#ifndef __linux__
	bool brez=true; 
	TCHAR *szBuff =new TCHAR[MAX_PATH];
	if(0==szBuff) return false;

	// Найти в lib-ах _get_pgmptr для QtCreator не получилось, а сама не подключалась
	// + куча сообщений что не всегда работает, в частности есть официальный косяк под Win10

	if(0==GetModuleFileName(0,szBuff,MAX_PATH)) brez=false;
	else fname=szBuff;

	delete[] szBuff;
	return brez;

#else

	bool brez=true;
	char *szBuff =new char[PATH_MAX];
	int BYTEs = readlink("/proc/self/exe", szBuff, PATH_MAX - 1);
	if(BYTEs > 0 && BYTEs <= (PATH_MAX - 1))
		szBuff[BYTEs] = _T('\0');
	else
	{
		//Вариант 2
		char szTmp[32];
		sprintf(szTmp, "/proc/%d/exe", getpid());
		BYTEs = readlink(szTmp, szBuff, PATH_MAX);
		if(BYTEs > 0 && BYTEs <= (PATH_MAX - 1))
			szBuff[BYTEs] = _T('\0');
		else brez=false;
	}
	if(brez) fname=LPCTSTR(CaplStringAdapter(szBuff));
	delete[] szBuff;
	return brez;

#endif
}
//*****************************************************
bool aplGetModuleFilePath(CString &fpath)
{
	fpath=_T("");
	CString fname;
	if(!aplGetModuleFileName(fname)) return false;

	int i=fname.ReverseFind(aplDirRazd);
	if(i>=0) fpath=fname.Left(i+1);
	return true;
}
//*****************************************************


#ifndef _MFC_VER

////////////////////////////////////////////////////////////////////////////
#define _AFXCOLL_INLINE

//?? Обратить внимание на замены Checked::memcpy_s, Checked::memmove_s, AfxThrowInvalidArgException()

CUIntArray::CUIntArray()
{
	m_pData = NULL;
	m_nSize = m_nMaxSize = m_nGrowBy = 0;
}

CUIntArray::~CUIntArray()
{
	//?? макрос не определен под QT - ASSERT_VALID(this);

	delete[] (BYTE*)m_pData;
}

void CUIntArray::SetSize(INT_PTR nNewSize, INT_PTR nGrowBy)
{
	//?? макрос не определен под QT - ASSERT_VALID(this);
	ASSERT(nNewSize >= 0);

	if(nNewSize < 0 )
		//?? функция не определена под QT - AfxThrowInvalidArgException();
		return;

	if (nGrowBy >= 0)
		m_nGrowBy = nGrowBy;  // set new size

	if (nNewSize == 0)
	{
		// shrink to nothing
		delete[] (BYTE*)m_pData;
		m_pData = NULL;
		m_nSize = m_nMaxSize = 0;
	}
	else if (m_pData == NULL)
	{
		// create one with exact size
#ifdef SIZE_T_MAX
		ASSERT(nNewSize <= SIZE_T_MAX/sizeof(UINT));    // no overflow
#endif
		m_pData = (UINT*) new BYTE[nNewSize * sizeof(UINT)];

		memset(m_pData, 0, nNewSize * sizeof(UINT));  // zero fill

		m_nSize = m_nMaxSize = nNewSize;
	}
	else if (nNewSize <= m_nMaxSize)
	{
		// it fits
		if (nNewSize > m_nSize)
		{
			// initialize the new elements

			memset(&m_pData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(UINT));

		}

		m_nSize = nNewSize;
	}
	else
	{
		// otherwise, grow array
		INT_PTR nGrowArrayBy = m_nGrowBy;
		if (nGrowArrayBy == 0)
		{
			// heuristically determine growth when nGrowArrayBy == 0
			//  (this avoids heap fragmentation in many situations)
			nGrowArrayBy = min(1024, max(4, (int)m_nSize / 8));
		}
		INT_PTR nNewMax;
		if (nNewSize < m_nMaxSize + nGrowArrayBy)
			nNewMax = m_nMaxSize + nGrowArrayBy;  // granularity
		else
			nNewMax = nNewSize;  // no slush

		ASSERT(nNewMax >= m_nMaxSize);  // no wrap around

		if(nNewMax  < m_nMaxSize)
			//?? функция не определена под QT - AfxThrowInvalidArgException();
			return;

#ifdef SIZE_T_MAX
		ASSERT(nNewMax <= SIZE_T_MAX/sizeof(UINT)); // no overflow
#endif
		UINT* pNewData = (UINT*) new BYTE[nNewMax * sizeof(UINT)];

		// copy new data from old
		//?? В MFC - Checked::memcpy_s
		memcpy(pNewData, m_pData, m_nSize * sizeof(UINT));

		// construct remaining elements
		ASSERT(nNewSize > m_nSize);

		memset(&pNewData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(UINT));

		// get rid of old stuff (note: no destructors called)
		delete[] (BYTE*)m_pData;
		m_pData = pNewData;
		m_nSize = nNewSize;
		m_nMaxSize = nNewMax;
	}
}

INT_PTR CUIntArray::Append(const CUIntArray& src)
{
	//?? макрос не определен под QT - ASSERT_VALID(this);
	ASSERT(this != &src);   // cannot append to itself

	if(this == &src)
	{
		//?? макрос не определен под QT - AfxThrowInvalidArgException();
		return 0;
	}

	INT_PTR nOldSize = m_nSize;
	SetSize(m_nSize + src.m_nSize);

	//?? В MFC - Checked::memcpy_s
	memcpy(m_pData + nOldSize, src.m_pData, src.m_nSize * sizeof(UINT));

	return nOldSize;
}

void CUIntArray::Copy(const CUIntArray& src)
{
	//?? макрос не определен под QT - ASSERT_VALID(this);
	ASSERT(this != &src);   // cannot append to itself

	if(this != &src)
	{
		SetSize(src.m_nSize);

		//?? В MFC - Checked::memcpy_s
		memcpy(m_pData, src.m_pData, src.m_nSize * sizeof(UINT));
	}

}

void CUIntArray::FreeExtra()
{
	//?? макрос не определен под QT - ASSERT_VALID(this);

	if (m_nSize != m_nMaxSize)
	{
		// shrink to desired size
#ifdef SIZE_T_MAX
		ASSERT(m_nSize <= SIZE_T_MAX/sizeof(UINT)); // no overflow
#endif
		UINT* pNewData = NULL;
		if (m_nSize != 0)
		{
			pNewData = (UINT*) new BYTE[m_nSize * sizeof(UINT)];

			// copy new data from old
			//?? В MFC - Checked::memcpy_s
			memcpy(pNewData, m_pData, m_nSize * sizeof(UINT));
		}

		// get rid of old stuff (note: no destructors called)
		delete[] (BYTE*)m_pData;
		m_pData = pNewData;
		m_nMaxSize = m_nSize;
	}
}

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

void CUIntArray::SetAtGrow(INT_PTR nIndex, UINT newElement)
{
	//?? макрос не определен под QT - ASSERT_VALID(this);
	ASSERT(nIndex >= 0);

	if(nIndex < 0)
		//?? функция не определена под QT - AfxThrowInvalidArgException();
		return;

	if (nIndex >= m_nSize)
		SetSize(nIndex+1);
	m_pData[nIndex] = newElement;
}





void CUIntArray::InsertAt(INT_PTR nIndex, UINT newElement, INT_PTR nCount)
{

	//?? макрос не определен под QT - ASSERT_VALID(this);
	ASSERT(nIndex >= 0);    // will expand to meet need
	ASSERT(nCount > 0);     // zero or negative size not allowed

	if(nIndex < 0 || nCount <= 0)
		//?? функция не определена под QT - AfxThrowInvalidArgException();
		return;

	if (nIndex >= m_nSize)
	{
		// adding after the end of the array
		SetSize(nIndex + nCount);  // grow so nIndex is valid
	}
	else
	{
		// inserting in the middle of the array
		INT_PTR nOldSize = m_nSize;
		SetSize(m_nSize + nCount);  // grow it to new size

		// shift old data up to fill gap
		//?? В MFC - Checked::memmove_s
		memmove(&m_pData[nIndex+nCount], &m_pData[nIndex], (nOldSize-nIndex) * sizeof(UINT));

		// re-init slots we copied from
		memset(&m_pData[nIndex], 0, nCount * sizeof(UINT));
	}

	// insert new value in the gap
	ASSERT(nIndex + nCount <= m_nSize);



	// copy elements into the empty space
	while (nCount--)
		m_pData[nIndex++] = newElement;

}



void CUIntArray::RemoveAt(INT_PTR nIndex, INT_PTR nCount)
{
	//?? макрос не определен под QT - ASSERT_VALID(this);
	ASSERT(nIndex >= 0);
	ASSERT(nCount >= 0);
	INT_PTR nUpperBound = nIndex + nCount;
	ASSERT(nUpperBound <= m_nSize && nUpperBound >= nIndex && nUpperBound >= nCount);

	if(nIndex < 0 || nCount < 0 || (nUpperBound > m_nSize) || (nUpperBound < nIndex) || (nUpperBound < nCount))
		//?? функция не определена под QT - AfxThrowInvalidArgException();
		return;

	// just remove a range
	INT_PTR nMoveCount = m_nSize - (nUpperBound);

	if (nMoveCount)
	{
		//?? В MFC - Checked::memmove_s
		memmove(&m_pData[nIndex], &m_pData[nUpperBound], (size_t)nMoveCount * sizeof(UINT));
	}

	m_nSize -= nCount;
}

void CUIntArray::InsertAt(INT_PTR nStartIndex, CUIntArray* pNewArray)
{
	//?? макрос не определен под QT - ASSERT_VALID(this);
	ASSERT(pNewArray != NULL);
	//?? макрос не определен под QT - ASSERT_KINDOF(CUIntArray, pNewArray);
	//?? макрос не определен под QT - ASSERT_VALID(pNewArray);
	ASSERT(nStartIndex >= 0);

	if(pNewArray == NULL || nStartIndex < 0)
		//?? функция не определена под QT - AfxThrowInvalidArgException();
		return;

	if (pNewArray->GetSize() > 0)
	{
		InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize());
		for (INT_PTR i = 0; i < pNewArray->GetSize(); i++)
			SetAt(nStartIndex + i, pNewArray->GetAt(i));
	}
}

/////////////////////////////////////////////////////////////////////////////
// Diagnostics
/*//??
#ifdef _DEBUG
void CUIntArray::Dump(CDumpContext& dc) const
{
	CObject::Dump(dc);

	dc << "with " << m_nSize << " elements";
	if (dc.GetDepth() > 0)
	{
		for (INT_PTR i = 0; i < m_nSize; i++)
			dc << "\n\t[" << i << "] = " << m_pData[i];
	}

	dc << "\n";
}

void CUIntArray::AssertValid() const
{
	CObject::AssertValid();

	if (m_pData == NULL)
	{
		ASSERT(m_nSize == 0);
		ASSERT(m_nMaxSize == 0);
	}
	else
	{
		ASSERT(m_nSize >= 0);
		ASSERT(m_nMaxSize >= 0);
		ASSERT(m_nSize <= m_nMaxSize);
		ASSERT(AfxIsValidAddress(m_pData, m_nMaxSize * sizeof(UINT)));
	}
}
#endif //_DEBUG
*/


_AFXCOLL_INLINE INT_PTR CUIntArray::GetSize() const
	{ return m_nSize; }
_AFXCOLL_INLINE INT_PTR CUIntArray::GetCount() const
	{ return m_nSize; }
_AFXCOLL_INLINE BOOL CUIntArray::IsEmpty() const
	{ return m_nSize == 0; }
_AFXCOLL_INLINE INT_PTR CUIntArray::GetUpperBound() const
	{ return m_nSize-1; }
_AFXCOLL_INLINE void CUIntArray::RemoveAll()
	{ SetSize(0); }
_AFXCOLL_INLINE UINT CUIntArray::GetAt(INT_PTR nIndex) const
{
	ASSERT(nIndex >= 0 && nIndex < m_nSize);
	if( nIndex < 0 || nIndex >= m_nSize )
	{
		//??AfxThrowInvalidArgException();
		return 0;
	}
	return m_pData[nIndex]; 
}
_AFXCOLL_INLINE void CUIntArray::SetAt(INT_PTR nIndex, UINT newElement)
{ 
	ASSERT(nIndex >= 0 && nIndex < m_nSize);
	if( nIndex < 0 || nIndex >= m_nSize )
	{
		//??AfxThrowInvalidArgException();
		return;
	}
	m_pData[nIndex] = newElement; 
}
/*//?? непонятно, что возвращать при ошибке
_AFXCOLL_INLINE UINT& CUIntArray::ElementAt(INT_PTR nIndex)
{
	ASSERT(nIndex >= 0 && nIndex < m_nSize);
	if( nIndex < 0 || nIndex >= m_nSize )
	{
		//??AfxThrowInvalidArgException();
		return 0;
	}
	return m_pData[nIndex]; 
}
*/
_AFXCOLL_INLINE const UINT* CUIntArray::GetData() const
	{ return (const UINT*)m_pData; }
_AFXCOLL_INLINE UINT* CUIntArray::GetData()
	{ return (UINT*)m_pData; }
_AFXCOLL_INLINE INT_PTR CUIntArray::Add(UINT newElement)
{
	INT_PTR nIndex = m_nSize;
	SetAtGrow(nIndex, newElement);
	return nIndex; 
}

_AFXCOLL_INLINE UINT CUIntArray::operator[](INT_PTR nIndex) const
	{ return GetAt(nIndex); }
/*//?? непонятно, что возвращать при ошибке
_AFXCOLL_INLINE UINT& CUIntArray::operator[](INT_PTR nIndex)
{ return ElementAt(nIndex); }
*/


#endif

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

bool g_bShow_aplMessageBox_Error=true;
bool g_bShow_aplMessageBox_Warning=true;
bool g_bShow_aplMessageBox_Information=true;
bool g_bShow_aplMessageBox_WaitEnter=true;
bool g_bShow_aplMessageBox_InRussian=false;

int aplMessageBoxConsole(LPCTSTR lpszText, UINT nType)
{
#ifdef _DEBUG
	if(nType&MB_OKCANCEL || nType&MB_ABORTRETRYIGNORE || nType&MB_YESNOCANCEL ||
		nType&MB_YESNO ||  nType&MB_RETRYCANCEL ||  nType&MB_CANCELTRYCONTINUE)
	{
		APL_STOP_IF_DEBUG; // Так вызывать нельзя. Эта функция только для диагностики
	}
#endif

	// не следует забывать, что MB_ICONWARNING = MB_ICONERROR|MB_ICONQUESTION
	if(nType&MB_ICONERROR)
	{
		if(MB_ICONWARNING==(nType&MB_ICONWARNING))
		{
			if(!g_bShow_aplMessageBox_Warning) return IDOK;
		}
		else if(!g_bShow_aplMessageBox_Error) return IDOK;
	}
	else if(nType&MB_ICONINFORMATION)
	{
		if(!g_bShow_aplMessageBox_Information) return IDOK;
	}


#ifdef _MFC_VER
	return AfxMessageBox(lpszText,nType);
#else
	if(0==lpszText) return IDOK;
	if(_T('\0')==lpszText[0]) return IDOK;

	aplMessagePrint(lpszText,nType);
	if(g_bShow_aplMessageBox_WaitEnter)
	{
		printf("\n\nPress ENTER ...");
		getchar();
	}
	return IDOK;
#endif
}

#if !defined(__linux__) && !defined(_MFC_VER)
// это только для Qt под виндой 
#include <windef.h>
#include <processenv.h> // для GetStdHandle
#include <wingdi.h> // определяет LF_FACESIZE без которой в QtCreator 6.X не грузится  <wincon.h>
#include <wincon.h> // для SetConsoleTextAttribute

#ifndef FOREGROUND_BLUE
	#define FOREGROUND_BLUE 0x1
	#define FOREGROUND_GREEN 0x2
	#define FOREGROUND_RED 0x4
	#define FOREGROUND_INTENSITY 0x8
	#define BACKGROUND_BLUE 0x10
	#define BACKGROUND_GREEN 0x20
	#define BACKGROUND_RED 0x40
	#define BACKGROUND_INTENSITY 0x80
	#define COMMON_LVB_LEADING_BYTE 0x100
	#define COMMON_LVB_TRAILING_BYTE 0x200
	#define COMMON_LVB_GRID_HORIZONTAL 0x400
	#define COMMON_LVB_GRID_LVERTICAL 0x800
	#define COMMON_LVB_GRID_RVERTICAL 0x1000
	#define COMMON_LVB_REVERSE_VIDEO 0x4000
	#define COMMON_LVB_UNDERSCORE 0x8000
#endif

#endif

CAplCriticalSection protectMessage;
CString *global_mess_wrapper=0;
CString *global_err_wrapper=0;
#ifndef _MFC_VER
// Защита от запроса к protectMessage из инициализации других модулей до инициализации aplAggr
bool global_aggr_inited=false;
aplTestInit global_test_inited(&global_aggr_inited);
#else
bool global_aggr_inited=true;
#endif

CAplCriticalSection* aplGetProtectMess()
{
	if(!global_aggr_inited) return 0;
	return &protectMessage;
}
CString* aplGetInternalMessWrapper(){if(!global_aggr_inited) return 0; CaplEnterCriticalSection ecs(&protectMessage); return global_mess_wrapper;}
CString* aplGetInternalErrWrapper(){if(!global_aggr_inited) return 0; CaplEnterCriticalSection ecs(&protectMessage); return global_err_wrapper;}


bool AFX_EXT_API aplInitMessageWrappers()
{
	protectMessage.EnterAplCriticalSection();
	if(global_mess_wrapper==0) global_mess_wrapper = new CString();
	if(global_err_wrapper==0) global_err_wrapper = new CString();
	protectMessage.LeaveAplCriticalSection();
	return true;
}
bool AFX_EXT_API aplClearMessageWrappers()
{
	CString csError(_T("")), csMessage(_T(""));
	protectMessage.EnterAplCriticalSection();
	if(global_mess_wrapper!=0){ csMessage = *global_mess_wrapper; delete global_mess_wrapper; global_mess_wrapper=0;}
	if(global_err_wrapper!=0){ csError = *global_err_wrapper; delete global_err_wrapper; global_err_wrapper=0;}
	protectMessage.LeaveAplCriticalSection();

	if(!csError.IsEmpty()) aplMessagePrint(csError, MB_ICONERROR);
	if(!csMessage.IsEmpty()) aplMessagePrint(csMessage, MB_ICONWARNING);
	return true;
}
// Возвращает строчки, которые были выведены функциями aplMessage*
//	В csError записываются сообщения об ошибках, в csMessage остальные сообщения
//	После вывода буфера очищаются.
bool AFX_EXT_API aplGetMessageWrappers(CString &csError, CString &csMessage)
{
	csError.Empty(); csMessage.Empty();
	protectMessage.EnterAplCriticalSection();
	if(global_mess_wrapper!=0){ csMessage = *global_mess_wrapper; global_mess_wrapper->Empty();}
	if(global_err_wrapper!=0){ csError = *global_err_wrapper; global_err_wrapper->Empty();}
	protectMessage.LeaveAplCriticalSection();
	return true;
}

bool AFX_EXT_API aplTestMessageWrappers(bool testWarn)
{
	if(!global_aggr_inited) return false;
	CaplEnterCriticalSection ecs(&protectMessage);
	if(global_err_wrapper!=0 && !global_err_wrapper->IsEmpty()) return true;
	if(testWarn && global_mess_wrapper!=0 && !global_mess_wrapper->IsEmpty()) return true;
	return false;
}


int aplMessagePrint(LPCTSTR lpszText, UINT nType)
{
	static const char *sError="\n\napl Error: ";
	static const char *sWarning="\n\napl Warning: ";
	static const char *sInfo="\n\napl Info: ";
	static const char *sZero="\n\n";
	if(g_bShow_aplMessageBox_InRussian)
	{
		sError="\n\nОшибка: ";
		sWarning="\n\nПредупреждение: ";
		sInfo="\n\n ";
	}
	bool isErr=false;
	LPSTR sPref=(LPSTR)sZero;
#ifdef __linux__
	const char *color_in = ""; //для отладки "\x1b[0;37m";
	const char *color_out = "\033[0m";
#else
	HANDLE  hConsole;
	hConsole = GetStdHandle(((DWORD)-11)); // STD_OUTPUT_HANDLE
	int color_in=15;
	int color_out=15;
	static DWORD consCP=0;
	if(consCP==0)consCP=GetConsoleOutputCP();
#endif
	// не следует забывать, что MB_ICONWARNING = MB_ICONERROR|MB_ICONQUESTION
	if(nType&MB_ICONERROR) //0x00000010L
	{
		if(MB_ICONWARNING==(nType&MB_ICONWARNING)) //0x00000030L
		{
			if(!g_bShow_aplMessageBox_Warning) return IDOK;
			sPref=(LPSTR)sWarning;
#ifdef __linux__
			color_in = "\x1B[93m";
#else
			color_in= FOREGROUND_RED + FOREGROUND_GREEN + FOREGROUND_INTENSITY;
#endif
		}
		else
		{
			if(!g_bShow_aplMessageBox_Error) return IDOK;
			sPref=(LPSTR)sError;
			isErr=true;
#ifdef __linux__
			color_in = "\x1B[1;31m";
#else
			color_in= FOREGROUND_RED + FOREGROUND_INTENSITY;
#endif
		}
	}
	else if(nType&MB_ICONINFORMATION) // 0x00000040L
	{
		if(!g_bShow_aplMessageBox_Information) return IDOK;
		sPref=(LPSTR)sInfo;
	}

	if(0==lpszText) return IDOK;
	if(_T('\0')==lpszText[0]) return IDOK;

	protectMessage.EnterAplCriticalSection();

	if(isErr && global_err_wrapper!=0)
	{
		*global_err_wrapper += sPref;
		*global_err_wrapper += lpszText;
		g_bShow_aplMessageBox_WaitEnter = false;
		protectMessage.LeaveAplCriticalSection();
		return IDOK;
	}
	else if(global_mess_wrapper!=0)
	{
		*global_mess_wrapper += sPref;
		*global_mess_wrapper += lpszText;
		g_bShow_aplMessageBox_WaitEnter = false;
		protectMessage.LeaveAplCriticalSection();
		return IDOK;
	}

#ifdef __linux__
		printf(color_in);
#else
		SetConsoleTextAttribute(hConsole, color_in);
#endif
	printf(sPref);

	#ifdef _UNICODE
		CaplStringAdapter sa(lpszText);
		#if !defined(__linux__) && !defined(_MFC_VER)
			sa.m_bIsAnsiUtf8=consCP==65001; // кодовая страница
		#endif
		printf(LPCSTR(sa));
	#else
		printf(lpszText);
	#endif
#ifdef __linux__
		printf(color_out);
#else
		SetConsoleTextAttribute(hConsole, color_out);
#endif
		fflush(stdout);
	protectMessage.LeaveAplCriticalSection();
	return IDOK;
}

#ifndef _MFC_VER

int (*pMessageBoxGui)(LPCTSTR lpszText, UINT nType) = 0;

void SetMessageBoxInAggr(int (*MessageBoxGuiExt)(LPCTSTR lpszText, UINT nType))
{
	pMessageBoxGui = MessageBoxGuiExt;
}

bool CheckMessageBoxInAggr(){return pMessageBoxGui !=0;}

int aplMessageBox(LPCTSTR lpszText, UINT nType)
{
	if(pMessageBoxGui != 0)
		return pMessageBoxGui(lpszText, nType );

	return aplMessageBoxConsole(lpszText, nType );
}

#endif


// Возвращает оптимальный размер буфера для файловых операций (windows)
// iPercent - % памяти разрешенной  к использованию. (Всегда автоматически уменьшается до 40)
unsigned int aplGetOptimalBufSize(unsigned int iPercent)  
{
	unsigned int iOptBufSize=16*1024*1024;

#ifndef _MFC_VER
    (void)iPercent; // чтобы не ругался компилятор
	return iOptBufSize;

#else
    if(iPercent>40) iPercent=40;

	MEMORYSTATUSEX statex;
	memset(&statex,0,sizeof(MEMORYSTATUSEX));
	statex.dwLength = sizeof (statex);
	if(!GlobalMemoryStatusEx (&statex)) return iOptBufSize;

	unsigned int av_virt_mem =(unsigned int)( statex.ullAvailVirtual/(1024*1024)); // Виртуальная доступная процессу (в Мб)
	unsigned int av_phis_mem =(unsigned int)( statex.ullAvailPhys/(1024*1024)); // Физическая доступная компу (в Мб)

	if(av_phis_mem<av_virt_mem)	av_virt_mem=av_phis_mem;

	unsigned int maxmem= (av_virt_mem*iPercent)/100; // Верхний лимит в Мб.

	return maxmem*1024*1024;

#endif
}

#ifndef _MFC_VER
// Похоже, что Qt в порядке оптимизации оформляет код CaplCSV::StrToArray не как функцию, а как код в 
// месте вызова. В результате вызвать ее из другой dll или exe становится невозмжно
// Добавил функцию aplStrToArrayTest, чтобы указать компилятору оформлять CaplCSV::StrToArray как экспортируемую функцию
bool aplStrToArrayTest(LPCTSTR str, CStringArray &strings, char razd, bool bUseBreakStr)
{
	return CaplCSV::StrToArray(str, strings, razd, bUseBreakStr);
}
#endif
