// aplXML.cpp

#include "stdafx.h"
#include "aplXML.h"

#include <Wincrypt.h>
#pragma comment (lib, "Crypt32.lib")

#include <exception>

#include <aplAggr.h>

/* Что надо доделать:

1. Обработка системных символов
2. Доводка IgnoreSpacer()
	- Обработка комментариев
	- Учет строк
3. 
*/

CaplXMLNode::CaplXMLNode(LPCTSTR nodename, CaplXMLNode *nodeparent, CaplXMLFile *file)
{
	SetName(nodename);

	m_file=file;
	parent = nodeparent;
	if(nodeparent!=0)
	{
		parent->subnodes.Add(this);
		if(file==0) m_file=nodeparent->m_file;
	}
}

void CaplXMLNode::Clear()
{
	name=_T("");
	text=_T("");
	params.RemoveAll();
	while(subnodes.GetSize()>0)
	{
		delete subnodes[0];
	}
}

CaplXMLNode::~CaplXMLNode()
{
	while(subnodes.GetSize()>0)
	{
		delete subnodes[0]; 
	}

	if(parent!=0)
	{
		int i;
		for(i=0; i<parent->subnodes.GetSize(); i++)
		{
			if(parent->subnodes[i]==this)
			{
				parent->subnodes.RemoveAt(i);
				return;
			}
		}
	}
}

bool  CaplXMLNode::CheckParam(LPCTSTR paramname)
{
	int i;
	for (i = 0; i < params.GetSize(); i++)
	{
		if (0 == params[i].name.CompareNoCase(paramname))
			return true;
	}
	return false;
}

LPCTSTR  CaplXMLNode::GetParam(LPCTSTR paramname)
{
	int i;
	for(i=0;i<params.GetSize();i++)
	{
		if (0 == params[i].name.CompareNoCase(paramname))
		{
			if (m_file != NULL && m_file->m_bTrimStrValues)
			{
				return params[i].value.Trim();
			}
			else
			{
				return params[i].value;
			}
		}
	}
	return 0;
}

void CaplXMLNode::GetParam( LPCTSTR paramname, bool& bVal )
{
	bVal = GetBoolParam(paramname);	
}

void CaplXMLNode::GetParam( LPCTSTR paramname, int& iVal )
{
	iVal = GetIntParam(paramname);	
}

void CaplXMLNode::GetParam( LPCTSTR paramname, long& lVal )
{
	lVal = GetLongParam(paramname);	
}

void CaplXMLNode::GetParam( LPCTSTR paramname, double& dVal )
{
	dVal = GetDoubleParam(paramname);
}

void CaplXMLNode::GetParam( LPCTSTR paramname, float& fVal )
{
	fVal = (float)GetDoubleParam(paramname);
}

void CaplXMLNode::GetParam( LPCTSTR paramname, CString& sVal )
{
	sVal = GetParam(paramname);
}

void CaplXMLNode::SetParamIfNotEmpty(LPCTSTR paramname, LPCTSTR paramvalue, bool bNoFindExist)
{
	if (paramvalue != NULL && paramvalue[0] !=0)
	{
		SetParam(paramname, paramvalue, bNoFindExist);
	}
}

void CaplXMLNode::SetParamIfNotEmpty(LPCTSTR paramname, double paramvalue, bool bNoFindExist /*= false*/)
{
	if (paramvalue != 0)
	{
		SetParam(paramname, paramvalue, bNoFindExist);
	}
}

void CaplXMLNode::SetParamIfNotEmpty(LPCTSTR paramname, float paramvalue, bool bNoFindExist /*= false*/)
{
	if (paramvalue != 0)
	{
		SetParam(paramname, paramvalue, bNoFindExist);
	}
}

void CaplXMLNode::SetParamIfNotEmpty(LPCTSTR paramname, int paramvalue, bool bNoFindExist /*= false*/)
{
	if (paramvalue != 0)
	{
		SetParam(paramname, paramvalue, bNoFindExist);
	}
}

void CaplXMLNode::SetParamIfNotEmpty(LPCTSTR paramname, long paramvalue, bool bNoFindExist /*= false*/)
{
	if (paramvalue != 0)
	{
		SetParam(paramname, paramvalue, bNoFindExist);
	}
}

void CaplXMLNode::SetParamIfNotEmpty(LPCTSTR paramname, LPWSTR paramvalue, bool bNoFindExist /*= false*/)
{
	if (paramvalue != NULL && paramvalue[0] != 0)
	{
		SetParam(paramname, paramvalue, bNoFindExist);
	}
}

void CaplXMLNode::SetParam(LPCTSTR paramname, LPCTSTR paramvalue, bool bNoFindExist)
{
	if(bNoFindExist)
	{
		CaplXMLParam param(paramname,paramvalue);
		params.Add(param);
		return;
	}
	int i;
	for(i=0;i<params.GetSize();i++)
	{
		if(0==params[i].name.CompareNoCase(paramname))
		{
			params[i].value=paramvalue;
			return;
		}
	}
	CaplXMLParam param(paramname,paramvalue);
	params.Add(param);
}

void CaplXMLNode::SetParam(LPCTSTR paramname, LPWSTR  paramvalue, bool bNoFindExist)
{
	if(bNoFindExist)
	{
		params.Add(CaplXMLParam(paramname,paramvalue));
		return;
	}
	int i;
	for(i=0;i<params.GetSize();i++)
	{
		if(0==params[i].name.CompareNoCase(paramname))
		{
			params[i].value=paramvalue;
			return;
		}
	}
	params.Add(CaplXMLParam(paramname,paramvalue));
}


void CaplXMLNode::SetParam( LPCTSTR paramname, double paramvalue, bool bNoFindExist/*=false*/ )
{
	CString buf;
	buf.Format(_T("%.15f"),paramvalue);
	buf.TrimRight(_T("0"));
	buf.Replace(_T(","),_T("."));
	buf.TrimRight(_T("."));
	SetParam(paramname, LPCTSTR(buf), bNoFindExist);
}

void CaplXMLNode::SetParam( LPCTSTR paramname, float paramvalue, bool bNoFindExist/*=false*/ )
{
	CString buf;
	buf.Format(_T("%.5f"),paramvalue);
	buf.TrimRight(_T("0"));
	buf.Replace(_T(","),_T("."));
	buf.TrimRight(_T("."));
	SetParam(paramname, LPCTSTR(buf), bNoFindExist);
}

void CaplXMLNode::SetParam( LPCTSTR paramname, int paramvalue, bool bNoFindExist/*=false*/ )
{
	CString buf;
	buf.Format(_T("%i"), paramvalue);
	SetParam(paramname, LPCTSTR(buf), bNoFindExist);
}

void CaplXMLNode::SetParam( LPCTSTR paramname, long paramvalue, bool bNoFindExist/*=false*/ )
{
	CString buf;
	buf.Format(_T("%d"), paramvalue);
	SetParam(paramname, LPCTSTR(buf), bNoFindExist);
}

void CaplXMLNode::SetParam( LPCTSTR paramname, bool paramvalue, bool bNoFindExist/*=false*/ )
{
	CString buf;
	if(m_file!=0)//dmibor 15.07.10 - будем все-таки сохранять bool как true/false
	{
		if(paramvalue==true) buf=_T("true");
		else				 buf=_T("false");
	}
	else
	{
		buf.Format(_T("%i"), paramvalue);
	}
	SetParam(paramname, LPCTSTR(buf), bNoFindExist);
}

double CaplXMLNode::GetDoubleParam( LPCTSTR paramname )
{
	const TCHAR *buf = GetParam(paramname);
	if (buf == 0) return 0;
	return __atof(GetParam( paramname));	
}

int CaplXMLNode::GetIntParam( LPCTSTR paramname )
{
	const TCHAR *buf = GetParam(paramname);
	if (buf == 0) return 0;
	return _atoi(GetParam(paramname));
}

long CaplXMLNode::GetLongParam(LPCTSTR paramname)
{
	const TCHAR *buf = GetParam(paramname);
	if (buf == 0) return 0;
	return __atol(GetParam(paramname));
}

bool CaplXMLNode::GetBoolParam( LPCTSTR paramname )
{
	const TCHAR *buf = GetParam(paramname);
	if (buf == 0) return false;
	//если вдруг значение - это строчка, а не число 0/1
	CString sVal=buf;
	if(sVal.CompareNoCase(_T("true"))==0) return true;
	if(sVal.CompareNoCase(_T("false"))==0) return false;
	//если все-таки значение - число 0/1
	int i = _atoi(GetParam(paramname));
	return i ? true : false;
}
//**********************************************************
bool CaplXMLNode::Find(LPCTSTR query, APL_XML_NODES &resultnodes)
{
	resultnodes.RemoveAll();
	if(query==0) return false;
	if(query[0]==_T('\0')) return false;

	int i=0;

	while(query[i]==_T(' ')) i++;

	CString node_name;
	CStringArray aNodeNames;

	while(query[i]!=0)
	{
		TCHAR c=query[i];
		i++;
		if(c==_T(' ') || c==_T('/')) 
		{
			if(!node_name.IsEmpty())
				aNodeNames.Add(node_name);
			node_name=_T("");
		}
		else
		{
			node_name+=c;
		}
	}



	if(!node_name.IsEmpty())
		aNodeNames.Add(node_name);

	if(aNodeNames.GetSize()<=0) return false;

	APL_XML_NODES aRresultRootNodes, arr1;
	APL_XML_NODES *p1=&aRresultRootNodes, *p2=&arr1,*p3;


	if(!FindNodesBN(aNodeNames[0],aRresultRootNodes)) return false;
	
	int j,k;
	for(i=1;i<aNodeNames.GetSize(); i++)
	{
		for(j=0; j<p1->GetSize();j++) 
		{
			CaplXMLNode *node=p1->GetAt(j);
			for(k=0; k<node->subnodes.GetSize();k++)
			{
				if( (aNodeNames[i] == _T('*')) || 
					(node->subnodes[k]->name==aNodeNames[i]))
				{
					p2->Add(node->subnodes[k]);
				}
			}
		}
		p1->RemoveAll();
		p3=p1;	p1=p2; p2=p3;
	}

	resultnodes.Append(*p1);

	return true;
}

//**********************************************************
bool CaplXMLNode::FindNodesBN(LPCTSTR sNodeName, APL_XML_NODES &resultnodes, bool bRecursive)
{
	resultnodes.RemoveAll();
	
	if(sNodeName==0) return false;
	if(sNodeName[0]==_T('\0')) return false;

	return FindNodesBNinternal(sNodeName,resultnodes,bRecursive);
}
//**********************************************************
bool CaplXMLNode::FindNodesBNinternal(LPCTSTR sNodeName, APL_XML_NODES &resultnodes, bool bRecursive)
{
	if(name==sNodeName)
	{
		resultnodes.Add(this);
		if(!bRecursive) return true;
	}
	int i;
	for(i=0;i<subnodes.GetSize();i++)
	{
		subnodes[i]->FindNodesBNinternal(sNodeName,resultnodes,bRecursive);
	}
	return true;
}
//*************************************************************************

CaplXMLNode *CaplXMLNode::FindSingleNodeBN(LPCTSTR sNodeName)
{
	if(name==sNodeName) return (this);

	int i;
	for(i=0;i<subnodes.GetSize();i++)
	{
		CaplXMLNode *node=subnodes[i]->FindSingleNodeBN(sNodeName);
		if(node!=0) return node;
	}
	return 0;
}

void CaplXMLNode::SetName( LPCTSTR nodeName )
{
	name = nodeName;
}

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

//*************************************************************************
//*************************************************************************
//*************************************************************************
bool CaplXMLFile::aplXMLReadBuf::GetNode(CaplXMLNode &node)
{
	node.Clear();
	char c;

	bool bIgnoreQuotesInContent=false; 
	if(0!=m_pXMLFile)bIgnoreQuotesInContent=m_pXMLFile->m_bIgnoreQuotesInContent;
		
	try
	{
#ifdef _UNICODE
		CStringA node_name, node_text;
#else
		CStringA &node_name = node.name, &node_text=node.text;
#endif
		if(!IgnoreSpacer()) throw(1);
		//if(!GetChar(c)) throw(2);
		//if(c!=_T('<')) throw(3);
		
		// имя узла
		//if(!IgnoreSpacer()) throw(1);
		while(true)
		{
			if(!GetChar(c)) throw(4);
			if((c=='>')||(c=='/')||(c==' ')/*|| (c=='/n' || (c=='/r'*/ || (c=='\t')) break;
			node_name+=c;
		}
#ifdef _UNICODE
		CaplStringAdapter node_name_adapter(node_name);
		node.name = (LPWSTR)node_name_adapter;
#endif
		ChangePos(-1);
		if(!IgnoreSpacer()) throw(5);

		if(node_name[0]=='!') // Диррективы
		{
			//Считаем диррективы текстом
			bool bIsStr=false;
			int count_x=0;
			while(true)
			{
				if(!GetChar(c,true)) throw(11);
				
				if(c=='"') bIsStr=!bIsStr;
				else 
				{
					if(bIsStr==false)
					{
						if(c=='<') count_x++;
						else if(c=='>')
						{
							if(count_x==0)
								break;
							count_x--;
						}
					}
				}
				node_text+=c;
			}

			// перекодируем текст из UTF-8
			if (m_pXMLFile->m_bIsUtf8)
			{
				int len = MultiByteToWideChar(CP_UTF8, 0, node_text, -1, NULL, 0);

				if (m_stmp_len < (len + 1))
				{
					delete m_stmp;
					m_stmp = new WCHAR[len + 1];
					m_stmp_len = len + 1;
				}

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

				node.text = m_stmp;
			}
#ifdef _UNICODE
			else
				node.text = node_text;
#endif
		}
		else if (node_name[0] == _T('?') && node_name.CompareNoCase("?xml") != 0) // управляющие инструкции <? текст?>
		{
			//Считаем управляющие инструкции текстом
			bool bIsStr = false;
			int count_x = 0;
			while (true)
			{
				if (!GetChar(c, true)) throw(11);

				if (c == '"') bIsStr = !bIsStr;
				else
				{
					if (bIsStr == false)
					{
						if (c == '<') count_x++;
						else if (c == '>')
						{
							if (count_x == 0)
							{
								if (node_text.GetLength() > 0 && node_text[node_text.GetLength() - 1] == _T('?'))
								{
									// убираем в конце знак '?'
									node_text.ReleaseBufferSetLength(node_text.GetLength() - 1);
								}
								break;
							}
							count_x--;
						}
					}
				}
				node_text += c;
			}

			// перекодируем текст из UTF-8
			if (m_pXMLFile->m_bIsUtf8)
			{
				int len= MultiByteToWideChar(CP_UTF8, 0, node_text, -1, NULL, 0);

				if(m_stmp_len<(len + 1))
				{
					delete m_stmp;
					m_stmp=new WCHAR[len + 1];
					m_stmp_len=len + 1;
				}

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

				node.text = m_stmp;
			}
#ifdef _UNICODE
			else
				node.text = node_text;
#endif
		}
		else
		{
			// параметры или концовка

			bool bEndNode=false;
			bool bEndNodeBegin=false;

			while (!bEndNodeBegin)
			{
				if(!IgnoreSpacer()) throw(15);
				if(!GetChar(c)) throw(6);

				if(c=='/')
				{
					if(!GetChar(c)) throw(7);
					if(c=='>') return true;
					else 
						throw(8);
				}
				if(c=='>') break;
				if(c=='?') 
				{
					if(node_name[0]=='?' && buf[pos]=='>')
					{
						ChangePos(1);
						//bEndNode=true;
						return true;
					}
				}
				
				// чтение имени параметра
				m_param="";
				m_value="";
				m_param+=c;
				bool b_findequ=true;
				
				while(true)
				{
					if(!GetChar(c)) throw(9);
					if((c==' ')|| (c=='\t') /*|| (c=='/n' || (c=='/r' */) break;
					if(c=='=') {b_findequ=false; break;}
					char cExt;
					ConvertSpecSymbolFromBuf(c, cExt);
					m_param += c ;
					if ( cExt != '\0' ) m_param += cExt;
				}
				if(b_findequ)
				{
					while(GetChar(c))
					{
						if(c=='=') break;
					}
				}
				// Чтение значения параметра
				bool bIsStr=false;
				while(true)
				{
					if(!GetChar(c,bIsStr)) throw(10);

					if(c=='"') bIsStr=!bIsStr;
					else 
					{
						char cExt;
						ConvertSpecSymbolFromBuf(c, cExt );
						if(bIsStr) 
						{	
							m_value+= c ;
							if ( cExt != '\0' ) m_value += cExt;
						}
						else
						{
							if((c==' ')  || (c=='\t')) break;
							else if(c=='>') 
							{
								bEndNodeBegin=true;
								break;
							}
							else if(c=='/') 
							{
								if(buf[pos]=='>')
								{
									ChangePos(1);
									bEndNode=true;
									break;
								}
							}
							else if(c=='?') 
							{
								if(node_name[0]=='?' && buf[pos]=='>')
								{
									ChangePos(1);
									bEndNode=true;
									break;
								}
								else m_value+=c;
							}
							else m_value+=c;
						}
					}
				}
				if (m_param.IsEmpty() == false)
				{
					if (m_pXMLFile->m_bIsUtf8)
					{
						int len= MultiByteToWideChar(CP_UTF8, 0, m_value, -1, NULL, 0);
						
						if(m_stmp_len<(len + 1))
						{
							delete m_stmp;
							m_stmp=new WCHAR[len + 1];
							m_stmp_len=len + 1;
						}
					
						MultiByteToWideChar(CP_UTF8, 0, m_value, -1, m_stmp, len);
						m_stmp[len]= _T('\0');
#ifdef _UNICODE
						CStringW paramW = m_param;
						node.SetParam(paramW, m_stmp);
#else
						node.SetParam(m_param, m_stmp);
#endif
					}
					else
					{
#ifdef _UNICODE
						CStringW paramW = m_param;
						CStringW valueW = m_value;
						node.SetParam(paramW, valueW);
#else
						node.SetParam(m_param, m_value);
#endif
					}
				}	

				if (bEndNode) return true;
			}
		
			// Чтение содержимого узла
			bool bIsStr=false;
			while(true)
			{
				if(!GetChar(c,bIsStr)) throw(11);

				if((c=='"') && (bIgnoreQuotesInContent==false)) {bIsStr=!bIsStr; node_text+=c;}
				else 
				{
					if(bIsStr) node_text+=c;
					else
					{
						//if(/*(c=='/n') || (c=='/r') || */(c=='/t')) continue;
						if(c=='<')
						{
							if(buf[pos]=='/')
							{
								// Закрытие тега
								if(!GetChar(c)) throw(12);
								m_end_tag_name="";
								while(true)
								{
									if(!GetChar(c)) throw(13);
									if(c=='>') break;
									if(c==' ') continue;
									m_end_tag_name+=c;
								}
								if(0==node_name.CompareNoCase(m_end_tag_name))
								{
									break;
								}
								else
								{
									throw(14);
								}
							}
							else
							{
								// новый тег
								CaplXMLNode *subnode=new CaplXMLNode;
								if(!GetNode(*subnode))
								{
									delete subnode;
									throw(-1);
								}
								subnode->parent=&node;
								subnode->m_file = m_pXMLFile;
								node.subnodes.Add(subnode);
							}
						}
						else 
						{
							char cExt;
							ConvertSpecSymbolFromBuf(c, cExt);
							node_text += c ;
							if ( cExt != '\0' ) node_text += cExt;

						}
					}
				}
			}

			// перекодируем текст из UTF-8
			if (m_pXMLFile->m_bIsUtf8)
			{
				int len= MultiByteToWideChar(CP_UTF8, 0, node_text, -1, NULL, 0);

				if(m_stmp_len<(len + 1))
				{
					delete m_stmp;
					m_stmp=new WCHAR[len + 1];
					m_stmp_len=len + 1;
				}

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

				node.text = m_stmp;
			}
			else
				node.text = node_text;

			// Очистка строки, если она фактичкски пустая
			bool isEmpty=true;
			int i=0;
			while(i<node.text.GetLength())
			{
				TCHAR c=node.text[i];
				i++;
				if(c==_T(' ')) continue;
				if(c==_T('\t')) continue;
				if(c==_T('\n')) continue;
				if(c==_T('\r')) continue;
				
				isEmpty=false; break;
			}
			if(isEmpty)node.text=_T("");
		}
	}
	catch(int err)
	{
		if(err>0)
		{
			CString err_txt;
			switch(err)
			{
			case 14: err_txt=APL_T("Не найден завершающий тег для \"") +node.name+_T("\"");
				break;
			}
			CString msg, sFile;
			if (m_pXMLFile)
				sFile = m_pXMLFile->filename;
			if (sFile.IsEmpty())
			{
				msg.Format(APL_T("Ошибка при чтении файла\r\nOшибка: %i\r\nСимвол:%i Строка:%i Колонка:%i\r\n%s"),
					err,pos,line,pos_in_line,LPCTSTR(err_txt));
			}
			else
			{
				msg.Format(APL_T("Ошибка при чтении файла %s\r\nOшибка: %i\r\nСимвол:%i Строка:%i Колонка:%i\r\n%s"),
					sFile,err,pos,line,pos_in_line,LPCTSTR(err_txt));
			}
			if(m_pXMLFile->m_bNoMessageBox) m_pXMLFile->m_sErrMessage=msg; else AfxMessageBox(msg,MB_OK|MB_ICONSTOP);		
		}
		return false;
	}
	return true;
}

bool CaplXMLFile::aplXMLReadBuf::ConvertSpecSymbolFromBuf(char &c, char& cExt )
{
	cExt = '\0';

	if ( c == '\n' && pos > 1 && buf[ pos - 2 ] == '\r'  )
	{
		c = '\r';
		cExt = '\n';

		return true;
	}

	if (c != '&') return true;

	int i;
	CStringA tmp;
	tmp.Empty();
	bool found=false;

	for(i=0;i<20;i++) //считаем что больше 20 сисмволов в служебном не будет
	{
		char c=buf[pos+i];
		if (c == ';') {found = true; break;}
		tmp += c;
	}

	if(!found) return false;

	if (tmp[0]=='#')
	{
		unsigned short ci=0;
		if( (tmp[1]=='x') || (tmp[1]=='X'))
		{
			/// \r\n = #xA
			if ( tmp[2] == 'A' && tmp[3]== '\0' )
			{
				i++;
				pos +=i;
				c = '\n';
				return true;
			}
			else if ( tmp[2] == 'D' && tmp[3]== '\0' )
			{
				i++;
				pos +=i;
				c = '\r';
				return true;
			}

			// Берем только 2 символа

			int i,k=tmp.GetLength();
			for(i=2;i<k;i++)
			{
				TCHAR c1=tmp[i];
				if(c1<58) c1-=48;
				else if(c1<65) c1-=(65-0xa);
				else c1-=(97-0xa);

				unsigned short c11=c1;
				ci<<=4;
				ci|=(c11&0xf);
			}

			/*char c1=tmp[2]; char c2=tmp[3];

			if(c1<58) c1-=48;
			else if(c1<65) c1-=(65-0xa);
			else c1-=(97-0xa);

			if(c2<58) c2-=48;
			else if(c2<65) c2-=(65-0xa);
			else c2-=(97-0xa);

			c=c1*16+c2;*/
		}
		else
		{
			ci=atoi(LPCSTR(tmp)+1);
		}

		if(ci<256) c=(char)ci;
		else
		{
#if _MSC_VER >= 1400
			WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)&ci, 1 ,&c, 1, 0, 0);
#else
			WideCharToMultiByte(CP_ACP, 0, &ci, 1 ,&c, 1, 0, 0);
#endif
		}
	}
	else
	{
		if(tmp=="amp")			c='&';
		else if(tmp=="apos")	c='\'';
		else if(tmp=="lt")		c='<';
		else if(tmp=="gt")		c='>';
		else if(tmp=="quot")	c='"';
		else return false;
	}

	i++;
	pos +=i;

	return true;
}
//******************************************************************************
bool CaplXMLFile::aplXMLReadBuf::Init(LPCTSTR inBuf)
{
	if(!inBuf) return false;

	if(buf!=0) { delete buf; buf=0;}
	len= 0; pos= 0; line= 0; pos_in_line= 0;

	long iLenght = _strlen(inBuf);
	buf=new char[iLenght+1];

#ifdef _UNICODE
	wcstombs(buf, inBuf, iLenght);
	buf[iLenght] = '\0';
#else
	memcpy(buf,inBuf,iLenght+1);
#endif

	len=iLenght;
	return true;
}
//******************************************************************************
bool CaplXMLFile::aplXMLReadBuf::ReadFile(LPCTSTR path, bool bDenyWrite/*=true*/, bool bShowErrors /*= true*/)
{
	if(buf!=0) { delete buf; buf=0;}
	pos=0;
	line=0;pos_in_line=0;

	CFile file;

	UINT shareFlag = CFile::shareDenyWrite;
	if (bDenyWrite == false)
		shareFlag = CFile::shareDenyNone;
	if (!file.Open(path, CFile::modeRead | shareFlag))
	{
		CString msg;
		msg.Format(APL_T("Ошибка открытия файла!\nФайл: %s"), path);

		if (bShowErrors)
		{
			if(m_pXMLFile->m_bNoMessageBox) m_pXMLFile->m_sErrMessage=msg; else AfxMessageBox(msg,MB_OK|MB_ICONSTOP);		
		}
		else
		{
			TRACE(msg);
			//CaplStringAdapter sMsgAdapter(msg);
			//throw (std::exception(LPCSTR(sMsgAdapter)));
		}

		return false;
	}

	DWORD flen=(DWORD)file.GetLength();
	if(flen < 5)
	{
		file.Close();
		CString msg = APL_T("Это не  файл XML !!!\n\n");
		msg += path;

		if (bShowErrors)
		{
			if(m_pXMLFile->m_bNoMessageBox) m_pXMLFile->m_sErrMessage=msg; else AfxMessageBox(msg,MB_OK|MB_ICONSTOP);
		}
		else
		{
			TRACE(msg);
			//CaplStringAdapter sMsgAdapter(msg);
			//throw (std::exception(LPCSTR(sMsgAdapter)));
		}

		return false;
	}
	
	buf = new char[flen];
#if _MSC_VER < 1310 // 6 студия
	if (!file.ReadHuge(buf,flen))
#else	// 7 и выше
	if (!file.Read(buf, flen))
#endif
	{
		file.Close();
		CString msg = APL_T("Ошибка чтения из файла!!!\n\n");
		msg += path;
		if (bShowErrors)
		{
			if(m_pXMLFile->m_bNoMessageBox) m_pXMLFile->m_sErrMessage=msg; else AfxMessageBox(msg,MB_OK|MB_ICONSTOP);
		}
		else
		{
			TRACE(msg);
			//CaplStringAdapter sMsgAdapter(msg);
			//throw (std::exception(LPCSTR(sMsgAdapter)));
		}

		return false;
	}
	file.Close();
	len = flen;
	return true;
};

//******************************************************
//bool aplXMLReadBuf::GetChar( char &c,bool IsStr, bool bIgnoreNewLine)
bool CaplXMLFile::aplXMLReadBuf::GetChar( char &c, bool IsStr)
{
	c='\0';

	while(true)
	{
		c=buf[pos];
		pos++;
		if(c=='\r') continue;
		// \n разрешаем только в строчках
		if(c=='\n'  && IsStr==false) {pos_in_line++; continue;}
		return true;
	}

	
	/*start:
	if(pos>=len) return false;
	c=buf[pos];
	if(c=='\r') {pos++; goto start;}

	// 25.03.2007 ЯАИ Теперь комментарии считаем нормальными элементами
	// а внутри <...> комментариев недопускаем
	/*if(!IsStr)
	{
		if(c=='\n') {line ++; pos_in_line=0; pos++; goto start;}

		// Обработка комментариев
		// 25.03.2007 ЯАИ Теперь комментарии считаем нормальными элементами
		// а внутри <...> комментариев недопускаем
		if(c=='<')
		{
			if(len-pos>5)
			{
				if(buf[pos+1]=='!')
				{
					if((buf[pos+2]=='-')&&(buf[pos+3]=='-'))
					{
						// Далее комментарий
						int i=pos+3;
						while(true)
						{
							i++; pos_in_line++;
							char c1=buf[i];
							if(bIgnoreNewLine)
							{
								if(c=='\r') continue;
								if(c=='\n') {line ++; pos_in_line=0; continue;}
							}
							if(c1=='-')
							{
								if(len-pos<3) return false;
								if((buf[i+1]=='-') && (buf[i+2]=='>'))
								{
									pos=i+3;
									goto start;
									//c=buf[pos];
									//return true;
								}
							}
						}
					}
				}
			}
		}
	}
	pos++;
	pos_in_line++;*/

	return true;
};

//******************************************************
bool CaplXMLFile::aplXMLReadBuf::IgnoreSpacer()
{
	while(true)
	{
		if(pos>=len) return false;
		char c= buf[pos];
		pos++; pos_in_line=0;
		if((c==' ') || (c=='\r') || (c=='\t')) continue;
		if(c=='\n') {line ++; pos_in_line=0; continue;}
		pos--;
		return true;
	}
};

CaplXMLFile::aplXMLReadBuf::aplXMLReadBuf( CaplXMLFile* xmlFile )
{
	m_pXMLFile = xmlFile;
	buf = 0;
	len = 0;
	pos = 0;
	line = 0;
	pos_in_line = 0;

	m_param.GetBuffer(1024);
	m_param.ReleaseBuffer(1024);

	m_value.GetBuffer(1024);
	m_value.ReleaseBuffer(1024);

	m_stmp=new WCHAR[1024];
	m_stmp_len=1024;

}

CaplXMLFile::aplXMLReadBuf::~aplXMLReadBuf()
 { 
	if(buf != 0) delete [] buf;
	if(m_stmp!=0)delete [] m_stmp;
}

//******************************************************
//******************************************************
//******************************************************
#define APL_CHARSET_WIN1251	_T("windows-1251")
#define APL_CHARSET_UTF8	_T("utf-8")

CaplXMLFile::CaplXMLFile(bool bIsUtf8/* = true*/, bool bSaveBOM /*= true*/)
{
	filecharset = APL_CHARSET_WIN1251;
	m_bIsUtf8 = bIsUtf8;
	m_bSaveBOM = bSaveBOM;
	m_bIsFileOpen = false;
	m_bIgnoreQuotesInContent=true;
	m_bNoMessageBox=false;
	m_bTrimStrValues = false;
	root.m_file = this;

}
//*****************************************************
bool CaplXMLFile::LoadFromFile(LPCTSTR path,bool bDenyWrite/*=TRUE*/, bool bShowErrors /*= true*/)
{
	if(path==0) return false;

	aplXMLReadBuf xml(this);
	if (!xml.ReadFile(path, bDenyWrite, bShowErrors))
		return false;
	
	return Read(&xml);
}
//*************************************************************************
bool CaplXMLFile::LoadFromBuffer(LPCTSTR buffer)
{
	if(!buffer) return false;

	aplXMLReadBuf xml(this);
	xml.Init(buffer);

	return Read(&xml);
}
//*************************************************************************
bool CaplXMLFile::Read(aplXMLReadBuf* xmlBufer)
{	
	char c;
	if(!xmlBufer->GetChar(c)) return false;
	if(c!='<')
	{
		if(((BYTE)(c))!=0xEF) return false;
		if(!xmlBufer->GetChar(c)) return false;
		if(((BYTE)(c))!=0xBB) return false;
		if(!xmlBufer->GetChar(c)) return false;
		if(((BYTE)(c))!=0xBF) return false;
		if(!xmlBufer->GetChar(c)) return false;

		m_bIsUtf8 = true;

		if(c!='<') return false;
	}

	CaplXMLNode *node=new CaplXMLNode;
	xmlBufer->GetNode(*node);

	// этот узел нам нужен был только для справки
	if(node->name == _T("?xml"))
	{
		xmlversion = node->GetParam(_T("version"));
		filecharset = node->GetParam(_T("encoding"));

		root.Clear();			

		if(0==filecharset.CompareNoCase(APL_CHARSET_UTF8)) m_bIsUtf8=true;
		else m_bIsUtf8=false;		

		delete node; node=0;
	}
	// а если это не "?xml", то считаем за первый элемент
	else
	{
		node->parent=&root;
		node->m_file = root.m_file;
		root.subnodes.Add(node);

		m_bIsUtf8=true;
	}

	while(true)
	{
		xmlBufer->IgnoreSpacer();
		if (xmlBufer->pos>=xmlBufer->len) break;
		if (!xmlBufer->GetChar(c)) return false;
		if (c != _T('<'))   return false;

		CaplXMLNode *subnode=new CaplXMLNode;
		if(!xmlBufer->GetNode(*subnode))
		{
			delete subnode;
			return false;
		}
		subnode->parent=&root;
		subnode->m_file = root.m_file;
		root.subnodes.Add(subnode);
	}

	m_bIsFileOpen= true;
	return true; 
}

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

bool CaplXMLFile::SaveToFile(LPCTSTR path, bool bSaveXmlVer, bool bShowErrors /*= true*/, bool bUTF8)
{
	if (path == 0) return false;

	CStdioFile file;
	if (!file.Open(path, CFile::modeWrite | CFile::typeText))
	{
		if(!file.Open(path, CFile::modeCreate | CFile::modeWrite|CFile::typeText))
		{

			CString msg = APL_T("Ошибка создания файла !!!\n\n");
			msg += path;
			if (bShowErrors)
			{
				if(m_bNoMessageBox) m_sErrMessage=msg; else AfxMessageBox(msg,MB_OK|MB_ICONSTOP);
			}
			else
			{
				CaplStringAdapter sMsgAdapter(msg);
				throw (std::exception(LPCSTR(sMsgAdapter)));
			}

			return false;
		}
	}
	else
	{
		file.SetLength(0);
		file.SeekToBegin();
	}
	
	CString  buffer;
	buffer.GetBuffer(1024*1024);
	buffer.ReleaseBuffer(1024*1024);
	if(bUTF8)
		filecharset = APL_CHARSET_UTF8;
	else
		filecharset = APL_CHARSET_WIN1251;

	if (!SaveToBuffer(buffer,bSaveXmlVer,&file))
	{
		file.Close();
		return false;
	}

	if(bUTF8 && m_bSaveBOM && 0==file.GetLength())
	{
		BYTE cBOM[3] = { 0xEF, 0xBB, 0xBF };
		BYTE* pBOM = cBOM;

		file.Write(pBOM, 3);
		file.Flush();
		//file.WriteString(_T("\xEF\xBB\xBF"));
	}
		
	file.WriteString(buffer);
	file.Flush();
	file.Close();
	return true;
}

bool CaplXMLFile::SaveToFileAsParams(LPCTSTR path, bool bSaveXmlVer)
{
	if(path==0) return false;

	CStdioFile file;
	if(!file.Open(path, CFile::modeWrite|CFile::typeText))
	{
		if(!file.Open(path,CFile::modeCreate|CFile::modeWrite|CFile::typeText))
		{

			CString msg=APL_T("Ошибка создания файла !!!\n\n");
			msg+=path;
			if(m_bNoMessageBox) m_sErrMessage=msg; else AfxMessageBox(msg,MB_OK|MB_ICONSTOP);
			return false;
		}
	}
	else
	{
		file.SetLength(0);
		file.SeekToBegin();
	}

	CString  buffer;
	buffer.GetBuffer(1024*1024);
	buffer.ReleaseBuffer(1024*1024);
	if(!SaveToBufferAsParams(buffer,true,&file))
	{
		file.Close();
		return false;
	}

	file.WriteString(buffer);
	file.Flush();
	file.Close();
	return true;
}

//******************************************************************************
bool CaplXMLFile::SaveToBuffer(CString &buffer, bool bSaveXmlVer, CStdioFile *file)
{
	buffer=_T("");

	//filecharset="windows-1251";

	if(bSaveXmlVer)
	{
		CString buf;
		if( filecharset.IsEmpty()) 
			filecharset=APL_CHARSET_WIN1251;
			//filecharset="utf-8";

		if(xmlversion.IsEmpty())
			xmlversion=_T("1.0");

		buf.Format(_T("<?xml version=\"%s\" encoding=\"%s\"?>\n"),LPCTSTR(xmlversion),LPCTSTR(filecharset));
		
		buffer+=buf;
		
		if(!additionalheader.IsEmpty())
			buffer+=additionalheader;
		if(!sReportName.IsEmpty())
		{
			buf=_T("<!-- [") + sReportName + _T("] created with aplXML ver. 2.0-->\n");		
			buffer+=buf;
		}
	}

	if(!SaveNode(buffer, root, 0, file)) return false;

	if (filecharset.CompareNoCase(APL_CHARSET_UTF8)==0)
	{
		ConvertToUTF8(buffer);
	}

	return true;
}

bool CaplXMLFile::SaveToBufferAsParams(CString &buffer, bool bSaveXmlVer, CStdioFile *file)
{
	buffer=_T("");

	if(!SaveNodeParams(buffer, root, 0, file)) return false;

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

bool CaplXMLFile::SaveNodeParams(CString &buffer, CaplXMLNode &node, int level, CStdioFile *file)
{
	int i;
	CString buf;
	buf=_T("");
	
	if(file)
	{
		if(buffer.GetLength()>64*1024)
		{
			file->WriteString(buffer);
			file->Flush();
			buffer = _T("");
		}
	}

	if(node.name.IsEmpty()) // C пустым именем  - это фиктивный node в CaplXMLFile
	{
		for(i=0;i<node.subnodes.GetSize();i++)
		{
			if(i!=0 && level>=0) buffer+=_T("\n");
			if(!SaveNodeParams(buffer, *(node.subnodes[i]), level, file)) return false;
		}
		return true;
	}
	buf=_T("");

	//параметры
	CString str;
	for(i=0;i<node.params.GetSize();i++)
	{
		//		if(node.params[i].value.IsEmpty()) continue;
		str = _T("");
		int ip;
		CaplXMLNode *pParent = node.parent;
		CArray<CaplXMLNode*, CaplXMLNode*> parents;
		while(pParent)
		{
			parents.InsertAt(0, pParent);
			pParent = pParent->parent;
			if(pParent == &root) pParent = NULL;
		}

		for(ip=0; ip<parents.GetSize(); ip++)
		{
			ReplaceSpecSymbol(parents[ip]->name+_T("_"), str, true);
		}
		ReplaceSpecSymbol(node.name+_T("_"), str, true);
		ReplaceSpecSymbol(node.params[i].name, str, true);
		str+=_T("=\""); 
		ReplaceSpecSymbol(node.params[i].value, str, true);
		str+=_T("\"\n");
		buf+=str;
	}

	buffer+=buf;

	for(i=0;i<node.subnodes.GetSize();i++)
	{
		if(level>=0) buffer+=_T("\n");
		if(!SaveNodeParams(buffer, *(node.subnodes[i]), level+1, file)) return false;
	}


	return true;
}

bool CaplXMLFile::SaveNode(CString &buffer, CaplXMLNode &node, int level, CStdioFile *file)
{
	int i;
	CString buf;
	buf=_T("");

	if(file)
	{
		//if(buffer.GetLength()>1024*1024)
		if(buffer.GetLength()>64*1024)
		{
			if (filecharset.CompareNoCase(APL_CHARSET_UTF8)==0) 
			{
				if(m_bSaveBOM && 0==file->GetLength()) 
				{
					BYTE cBOM[3] = { 0xEF, 0xBB, 0xBF };
					BYTE* pBOM = cBOM;

					file->Write(pBOM, 3);
					file->Flush();
					//file->WriteString(_T("\xEF\xBB\xBF"));
				}
				ConvertToUTF8(buffer);
			}
			file->WriteString(buffer);
			file->Flush();
			buffer= _T("");
		}
	}

	if(node.name.IsEmpty()) // C пустым именем  - это фиктивный node в CaplXMLFile
	{
		for(i=0;i<node.subnodes.GetSize();i++)
		{
			if(i!=0 && level>=0) buffer+=_T("\n");
			if(!SaveNode(buffer, *(node.subnodes[i]), level, file)) return false;
		}
		return true;
	}
	buf=_T("");
	if(level>=0)
	{
		for(i=0;i<level;i++) buf+=_T(" ");
	}
	buf+=_T("<"); buf+=node.name;

	if(node.name[0]==_T('!'))
	{
		buf+=_T(" ");
		buf+=node.text;
		buf+=_T(">");
		buffer+=buf;
		return true;
	}

	//параметры
	for(i=0;i<node.params.GetSize();i++)
	{
		//		if(node.params[i].value.IsEmpty()) continue;

		buf+=_T(" "); 
		ReplaceSpecSymbol(node.params[i].name,buf,true);
		buf+=_T("=\""); 
		ReplaceSpecSymbol(node.params[i].value,buf,true);
		buf+=_T('\"');
	}

	if((node.subnodes.GetSize()<1)&&(node.text==_T("")))
	{
		if(node.name[0]==_T('?')) buf+=_T('?');
		else buf+=_T('/');
		buf+=_T(">");
	}
	else if((node.subnodes.GetSize()<1)&&(node.name[0]==_T('?')))
	{
		buf+=_T("?>");
		ReplaceSpecSymbol(node.text,buf,true);
	}
	else
	{
		buf+=_T(">");
		buffer+=buf;

		if(node.text.IsEmpty() == false)
		{
			if(level>=0)
			{
				for(i=0;i<=level;i++) buf+=_T(" ");
			}
			ReplaceSpecSymbol(node.text,buffer,true);
		}

		for(i=0;i<node.subnodes.GetSize();i++)
		{
			if(level>=0) buffer+=_T("\n");
			if(!SaveNode(buffer, *(node.subnodes[i]), level+1, file)) return false;
		}

		buf=_T("");

		if(level>=0)
		{
			if(i>0){buf=_T("\n");for(i=0;i<level;i++) buf+=_T(" ");}
		}

		buf+=_T("</"); buf+=node.name; buf+=_T(">");
	}

	buffer+=buf;
	return true;
}


//******************************************************************************
bool CaplXMLFile::ConvertToUTF8(CString &str)
{
	LPSTR destStr= NULL;

	BSTR bstrStr= str.AllocSysString();
	
	int lWlen= SysStringLen(bstrStr);
	int lSlen= str.GetLength();
	int len=WideCharToMultiByte(CP_UTF8, 0, bstrStr, lWlen, NULL, 0, NULL, NULL);
	int size=len+1;
	//size= ((len/(64*1024))+1)*(64*1024);
	destStr= new char[size];
	if(len)
	{
		WideCharToMultiByte(CP_UTF8, 0, bstrStr, lWlen, destStr, len, NULL, NULL);
		destStr[len]= _T('\0');
	}
	else
	{
		switch(GetLastError()) 
		{
		case ERROR_INSUFFICIENT_BUFFER:
			TRACE("ERROR_INSUFFICIENT_BUFFER\n");
			break;
		case ERROR_INVALID_FLAGS:
			TRACE("ERROR_INVALID_FLAGS\n");
			break;
		case ERROR_INVALID_PARAMETER:
			TRACE("ERROR_INVALID_PARAMETER\n");
			break;
		}
	}
	
	if(bstrStr) SysFreeString(bstrStr);

	str = destStr;
	delete [] destStr;

	return true;
}


//*****************************************************
bool CaplXMLFile::ReplaceSpecSymbol(CString &in, CString &out, bool bAppendOut)
{
	
	if(!bAppendOut)
	{
		out.GetBuffer(in.GetLength()+20);
		out=_T("");
	}
	int i;
	for(i=0;i<in.GetLength();i++)
	{
		TCHAR c=in[i];
		switch (c)
		{
			case _T('<'):	 out+=_T("&lt;");	break;
			case _T('>'):	 out+=_T("&gt;");	break;
			case _T('"'):	 out+=_T("&quot;"); break;
			case _T('&'):	 out+=_T("&amp;");	break;
			case _T('\''):	 out+=_T("&apos;"); break;
			case _T('\r'):	 out += _T("&#xD;"); break;
			case _T('\n'):	 out += _T("&#xA;"); break;
//  [5/28/2018 lobanov]
//  Заменено на честную обработку двумя строчками выше
/*
			case _T('\r'):
			{
				if ( in[ i + 1 ] == _T('\n') )
				{
					i++;
					out += _T("&#xA;");
				}
			}
			break;
*/
			default:	 out+=c;
		}
	}
	return true;
}
//********************************************************************************
//********************************************************************************
//********************************************************************************
bool CaplXMLFile::EncodeToBase64( unsigned char const* BYTEs_to_encode, unsigned int in_len, CString &out_string )
{
	out_string=_T("");
	if(0==in_len || 0==BYTEs_to_encode) return false;

	int buflen= 1000+(int)(0.5+(((double)in_len)*8/6));
	
	DWORD nLenOut= 0;
	BOOL fRet= CryptBinaryToString(	(const BYTE*)BYTEs_to_encode, in_len, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCR,	0, &nLenOut	);
	if (!fRet)	return false;
	nLenOut++;
	TCHAR *buf=new TCHAR[nLenOut];
	fRet= CryptBinaryToString(	(const BYTE*)BYTEs_to_encode, in_len, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCR,	buf, &nLenOut);
	if (!fRet){delete buf; return false;}

	buf[nLenOut]=_T('\0');
	out_string=buf;
	delete buf;
	return true;
}

bool CaplXMLFile::DecodeFromBase64(LPCTSTR encoded_string, unsigned char const *buf, unsigned int &buf_len)
{
	DWORD nLenOut=buf_len;
	buf_len=0;
	if(0==encoded_string || _T('\0')==encoded_string[0]) return false;
	BOOL fRet= CryptStringToBinary(encoded_string, 0,CRYPT_STRING_BASE64, (BYTE*)buf, &nLenOut, NULL,	NULL);
	if (!fRet)return false;
	buf_len=nLenOut;
	return true;
}