//---------------------------------------------------------------------------
#pragma hdrstop

//#include "stdafx.h"
#include <wx/msw/msvcrt.h>

#include "wxTools.h"
//#include <sstream>

#include <Math.h>
//#include <iostream>
//#include <locale>
//#include <string>
//#include <vector>
//#include <WinBase.h>
//#include <time.h>
#if defined( _VC )
	#include < ctime >
#endif
#if defined( _BORLAND )
	#include <vcl.h>
#endif

#include <tchar.h>
#include <wx/msgdlg.h>
#include <wx/stdpaths.h>
#include <wx/txtstrm.h>
#include <wx/filename.h>
#include <wx/zipstrm.h>
#include <sys/stat.h>
#include <wx/string.h>
#include <wx/intl.h>
#include <wx/textfile.h>
#include <wx/url.h>

//#include <openssl/md5.h>
#include <sstream>

#include "tcDebug.h"
//---------------------------------------------------------------------------
//#pragma package(smart_init)
//---------------------------------------------------------------------------
//#if defined( _WX )
//------------------------------------------------------------------------------
//Сохранить данные с веб сервера в массив байт
//накопление данных происходит в виде связанного списка, по приходу всего копируется в массив
unsigned char* getDataFromURL(const wxString url,unsigned int* size)
{
	RListOne* first=NULL,* next=NULL; //Элементы связанного списка
	unsigned int bufSize=512; //Размер буфера (и элемента в списке) 
	*size=0; //Размер принятого

	wxURL murl(url);
	if (murl.GetError() == wxURL_NOERR)
	{
		wxInputStream* is = murl.GetInputStream();
		if(is)
		{
			unsigned char* buf=new unsigned char[bufSize];
			first=new RListOne;
			first->next=NULL; first->size=0; first->data=new unsigned char[bufSize];
			next=first;
			for(;;)
			{
				is->Read(buf, bufSize);
				size_t n = is->LastRead();
				if ( n == 0 ) break;
				*size+=n;
				for(size_t i=0;i<n;i++) //Сейчас побайтно но лучше использовать "memcpy(newbuf, buf, sz)"
				{
					if(next->size==bufSize)
					{
						next->next=new RListOne;
						next=next->next;
						next->next=NULL; next->size=0; next->data=new unsigned char[bufSize];
					}
					next->data[next->size]=buf[i];
					next->size++;
				}
			}
			delete[] buf;
			delete is;
		};
	};
	//собираем данные в массив из связанного списка
	unsigned char* data=NULL;
	if(*size==0) return data;

	data=new unsigned char[*size];
	unsigned int pos=0;
	while(first!=NULL)
	{
		memcpy(&data[pos],first->data,first->size);
		pos+=first->size;

		next=first;
		first=first->next;
		delete[] next->data; //Освобождаем данные
		delete next; //Освобождаем элемент списка
	}
	return data;
}
//------------------------------------------------------------------------------
wxString getStringOnUrl(const wxString path)
{
	//size_t iRead=0;
	wxString res = _T("");
	wxURL url(path);
	((wxProtocol&)url.GetProtocol()).SetTimeout(100);
	//url->SetProxy(wxT("proxy.localdomain:80"));
	if (url.GetError() == wxURL_NOERR)
	{
		wxInputStream *in_stream = url.GetInputStream();
		if( in_stream )
		{
			size_t st=102400; //шаг увеличения буфера 100 килобайт
			size_t sz=st; //размер текущего буфера
			unsigned int pos=0;
			char* buf = new char[sz];
			do
			{
				//если собираемся прочитать байт в позизию больше че буфер то увеличиваем буфер
				if(pos>=sz)
				{
					char* newbuf = new char[sz+st];
					memcpy (newbuf, buf, sz);
					delete[] buf;
					buf=newbuf;
					sz+=st;
				}
				buf[pos]=in_stream->GetC();
				pos++;
			}
			while (in_stream->LastRead()>0);
			res = wxString::FromUTF8(buf,pos-1);
			delete[] buf;
			delete in_stream;
		}
	}
	//delete url;
	return res;
}
//------------------------------------------------------------------------------
//Сохранить с сервера url в файл path
bool getFileOnWebServer(const wxString url,const wxString path)
{
	bool res=true;
	wxURL murl(url);
	if (murl.GetError() == wxURL_NOERR)
	{
		wxInputStream* is = murl.GetInputStream();
		if(is)
		{
			wxFileName fn(path);
			if(!wxFileName::DirExists(fn.GetPath())) wxFileName::Mkdir(fn.GetPath());
			wxFile fOut(path, wxFile::write);
			if(fOut.IsOpened())
			{
				char buf[1000];
				for(;;)
				{
					is->Read(buf, 1000);
					size_t n = is->LastRead();
					if ( n == 0 ) break;
					fOut.Write(buf, n);
				}
			}else res=false;
			delete is;
		}else res=false;
	}else res=false;

	return res;
}
//------------------------------------------------------------------------------
//вычислить хеш md5 из строки
/*wxString md5String(wxString val)
{
	if(val==_T("")) return _T("");
	wxString result=_T("");
	unsigned char md5digest[MD5_DIGEST_LENGTH];
	wxCharBuffer cb=val.ToAscii();
	size_t len=strlen((const char*)cb);
	const unsigned char* buf=(const unsigned char*)(const char*)cb;
	MD5(buf,len, md5digest);
	for(unsigned int i=0;i<MD5_DIGEST_LENGTH;i++)
	{
		result.Printf(result+_T("%02x"),md5digest[i]);
	};
	return result;
}*/
//------------------------------------------------------------------------------
//Вернуть строку между 2мя заданными строками
wxString getSubString(wxString str, wxString bracket1, wxString bracket2)
{
	int pos1=str.Find(bracket1);
	int pos2=str.Find(bracket2);
	if((pos1!=wxNOT_FOUND)&&(pos2!=wxNOT_FOUND))
		return str.SubString(pos1+bracket1.Len(),pos2-1);
	return _T("");
}
//------------------------------------------------------------------------------
/*wxString getExecutablePath()
{
  wxString path=_T("");
  wxStandardPaths *StandardPaths = new wxStandardPaths();
  path = StandardPaths->GetExecutablePath();
  delete StandardPaths;
  return path;
  return "";
}*/
//------------------------------------------------------------------------------
wxString getAfterFirst(wxString str,wxString separator)
{
	int p=str.Find(separator);
	if(p!=wxNOT_FOUND)
	{
		return str.SubString(p+separator.Len(),str.Len()-1);
	}
	return _T("");
}
//------------------------------------------------------------------------------
wxString getAfterLast(wxString str,wxChar separator)
{
	return str.AfterLast(separator);
}
//------------------------------------------------------------------------------
wxString getBeforeFirst(wxString str,wxString separator)
{
	int p=str.Find(separator);
	if(p!=wxNOT_FOUND) return str.SubString(0,p-1);
	return _T("");
}
//------------------------------------------------------------------------------
wxString getBeforeLast(wxString str,wxChar separator)
{
	return str.BeforeLast(separator);
}
//------------------------------------------------------------------------------
//Заменить все вхождения строки find в sors на repl (wxString::Replace)
wxString replaceStrings(wxString sors,wxString find,wxString repl)
{
	if(find==repl) return sors;
	wxString rez=_T("");

	int p=sors.Find(find);
	while(p!=wxNOT_FOUND)
	{
		rez+=sors.SubString(0,p-1)+repl;
		sors=sors.SubString(p+find.Len(),sors.Len()-1);
		p=sors.Find(find);
	}
	rez+=sors;
	return rez;
}
//------------------------------------------------------------------------------
void replaseChars(wxString& str,wxChar oldCh,wxChar newCh)
{
	str.Replace(&oldCh,&newCh,true);
}
//------------------------------------------------------------------------------
wxString IntToStr(unsigned long i)
{
	wxString str=_T("");
	str.Printf(_T("%u"),i);
	return str;
}
//------------------------------------------------------------------------------
//Изять из строки только float в качестве разделителя точка
wxString getFloatString(wxString str)
{
	wxString res=_T("");
	for(unsigned int i=0;i<str.length();i++)
	{
		if(str[i]==_T('-') || str[i]==_T('0') ||str[i]==_T('1') ||str[i]==_T('2') ||str[i]==_T('3') ||str[i]==_T('4') ||str[i]==_T('5') ||str[i]==_T('6') ||str[i]==_T('7') ||str[i]==_T('8') || str[i]==_T('9') || str[i]==_T('.'))
			res+=str[i];
	}
	return res;
}
//------------------------------------------------------------------------------
wxString FloatToStr(float f, int numdigits)
{
    wxString Result;
    if (numdigits <= 0)
    {
      Result = Result.Format(_T("%f"),f);
    }
    else
    {
        wxString strNumFormat = wxString::Format(_T("%%0.%df"),numdigits);
        Result = Result.Format(strNumFormat,f);
    }
    return Result;
}
//------------------------------------------------------------------------------
//Из double в String
wxString DoubleToStr(double d, int numdigits)
{
	wxString Result;
	Result = Result.Format(_T("%.15f"),d);
	Result.Replace(wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT,wxLOCALE_CAT_NUMBER),wxT(".")); //Разделитель всегда точка
	if(numdigits>0)
	{
		size_t pos=Result.Find(_T('.'));
		if(pos!=wxNOT_FOUND)
		{
			Result=Result.Remove(pos+numdigits+1);
			for(size_t i=Result.length();i>pos;i--)
				if(Result[i-1]==_T('0'))
					Result=Result.Remove(i-1);
				else 
					break; //Обрезаем нули сконца
			if(Result.length()==pos+1) 
				Result=Result.Remove(pos); //Если в конце осталась точка
		}
	}else
	if(numdigits==0)
	{	size_t pos=Result.Find(_T('.'));
		Result=Result.Remove(pos);
	}
    return Result;
}
//------------------------------------------------------------------------------
//Сохранить UTF8 строку не больше 256 байт
void saveUTF8String(wxOutputStream *os, wxString str)
{
	const wxCharBuffer buff = str.mb_str(wxConvUTF8);
	size_t len0 = strlen(buff);
	unsigned char len1=0;
	if(len0<=256) len1=(unsigned char)len0;
	os->Write(&len1,1);
	if(len1>0) os->Write(buff,len1);
}
//------------------------------------------------------------------------------
//Сохранить UTF8 строку не больше 256 байт
void saveUTF8String(wxFFile *os, wxString str)
{
	const wxCharBuffer buff = str.mb_str(wxConvUTF8);
	size_t len0 = strlen(buff);
	unsigned char len1=0;
	if(len0<=256) len1=(unsigned char)len0;
	os->Write(&len1,1);
	if(len1>0) os->Write(buff,len1);
}
//------------------------------------------------------------------------------
//Сохранить UTF8 строку не больше 65535 байт
void saveUTF8String2(wxOutputStream *os, wxString str)
{
	const wxCharBuffer buff = str.mb_str(wxConvUTF8);
	size_t len0 = strlen(buff);
	unsigned short len1=0;
	if(len0<=65535) len1=(unsigned short)len0;
	os->Write(&len1,2);
	if(len1>0) os->Write(buff,len1);
}
//------------------------------------------------------------------------------
//Вырезать из строки под строку (Если разделитель не найден то вырезает всю строку)
wxString cutFirstSubStr(wxString &string,wxChar separator)
{
  wxString str=string.BeforeFirst(separator);
  string=string.AfterFirst(separator);
  return str;
}
//------------------------------------------------------------------------------
//выризать из строки под строку
wxString cutFirstSubStr(wxString &string,char ch)
{
  wxString str=string.BeforeFirst(ch);
  string=string.AfterFirst(ch);
  return str;
}
//------------------------------------------------------------------------------
int StrToInt(wxString str)
{
	long dblVal;
	str.ToLong(&dblVal);
	return dblVal;
}
//------------------------------------------------------------------------------
//Вырезать из строки только циферки и преобразовать
int StrFullToInt(wxString str)
{
	wxString tmp;
	for(int i=0;i<(int)str.length();i++)
	{
		if(str[i]=='-' ||str[i]=='.' || str[i]=='0' || str[i]=='1' || str[i]=='2' || str[i]=='3' || str[i]=='4' || str[i]=='5' || str[i]=='6' || str[i]=='7' || str[i]=='8' || str[i]=='9')
		{
			tmp+=str[i];
		}
	}
	long dblVal;
	tmp.ToLong(&dblVal);
	return dblVal;	
}
//------------------------------------------------------------------------------
unsigned int StrFullToUInt(wxString str)
{
	wxString tmp;
	for(int i=0;i<(int)str.length();i++)
	{
		if(str[i]=='.' || str[i]=='0' || str[i]=='1' || str[i]=='2' || str[i]=='3' || str[i]=='4' || str[i]=='5' || str[i]=='6' || str[i]=='7' || str[i]=='8' || str[i]=='9')
		{
			tmp+=str[i];
		}
	}
	unsigned long dblVal;
	tmp.ToULong(&dblVal);
	return dblVal;	
}
//------------------------------------------------------------------------------
double StrToDouble(wxString str)
{
	double val;
	str.ToDouble(&val);
	return val;
}
//******************************************************************************
wxHtmlOpeningStatus MyHtmlWindow::OnOpeningURL(wxHtmlURLType WXUNUSED(type),
                                               const wxString& WXUNUSED(url),
                                               wxString *WXUNUSED(redirect)) const
{
    return wxHTML_OPEN;
}
//------------------------------------------------------------------------------
void MyHtmlWindow::OnLinkClicked(const wxHtmlLinkInfo& link)
{
  wxLaunchDefaultBrowser(link.GetHref());
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//Задание для очереди заданий
TTask::TTask(int delay):wxThread(wxTHREAD_DETACHED)
{	critsectA=new wxCriticalSection();
	critsectR=new wxCriticalSection();
	listAnswer=new TSimpleList<RClientData*>(10,false);
	listRequest=new TSimpleList<RClientData*>(10,false);
	m_Cancel=false;
	m_delay=delay;
	//Стартуем поток
	if( Create() != wxTHREAD_NO_ERROR )
	{	//wxLogError(_T("Can’t create thread!"));		
	}else Run();
}
//------------------------------------------------------------------------------
void* TTask::Entry() //Функция потока для обработки заданий
{	RClientData* d;
	while(!m_Cancel)
	{
		d=getTask();
		if(d)
		{	//Определяем тип задания и отправляем на выполнение в основном это загрузка данных
			if(d->url!=_T("")) 
				d->data=getDataFromURL(d->url,&d->size);
			
			addAnswer(d);
		}
		Sleep(m_delay);
	}
	//Удаляем задания
	d=getTask();
	while(d!=NULL)
	{	delete d;
		d=getTask();
	}
	//Удаляем ответы на всяк случай если они никаму ненужны
	d=getAnswer();
	while(d!=NULL)
	{	delete d;
		d=getAnswer();
	}
	return NULL;
}
//------------------------------------------------------------------------------
wxString TAMas::getAt(wxString id)
{
	size_t i=0;
	while(m_id.Count()>i)
	{
		if(m_id.Item(i)==id) return m_val.Item(i);
		i++;
	}
	return _T("");
}
//------------------------------------------------------------------------------
void TAMas::setAt(wxString id,wxString val)
{
	m_id.Add(id);
	m_val.Add(val);
}
//------------------------------------------------------------------------------
void TAMas::Copy(TAMas& res)
{
	for(unsigned int i=0;i<res.m_id.Count();i++)
	{
		setAt(res.m_id.Item(i),res.m_val.Item(i));
	}
}
//------------------------------------------------------------------------------
//******************************************************************************
TIniFile::TIniFile(wxString path)
{
	this->path=path;
	first=NULL;
	if (wxFileExists(path))
	{
		wxTextFile* TextFile = new wxTextFile();
		TextFile->Open(path);
		if (TextFile->Eof())
		{
			TextFile->Close();
			delete TextFile;
		}else
		{
			wxString str;
			wxString section;
			str = TextFile->GetFirstLine();
			while(true)
			{
				if (str.Find('[')>=0)
				{
					section=str.AfterFirst('[').BeforeLast(']');
				}
				if (str.Find('=')>=0)
				{
					TIniStruct* inistr= new TIniStruct;
					inistr->next=NULL;
					inistr->section=section;
					inistr->ident=str.BeforeFirst('=');
					inistr->value=str.AfterFirst('=');
					if (first==NULL)
					{
						first=inistr;
						last=inistr;
					}else
					{
						last->next=inistr;
						last=inistr;
					}
				}
				if (TextFile->Eof()) break;
				str = TextFile->GetNextLine();
			}
			TextFile->Close();
			delete TextFile;
		}
	}
}
//------------------------------------------------------------------------------
TIniFile::~TIniFile()
{
  TIniStruct* inistrdel;
  TIniStruct* inistr = first;
  while (inistr!=NULL)
  {
    inistrdel=inistr;
    inistr=inistr->next;
    delete inistrdel;
  }
}
//------------------------------------------------------------------------------
wxString TIniFile::ReadString(wxString Section,wxString Ident,wxString Default)
{
  TIniStruct* inistr = first;
  while (inistr!=NULL)
  {
    if ((inistr->section==Section) && (inistr->ident==Ident))
    {
      return inistr->value;
    }
    inistr=inistr->next;
  }
  return Default;
}
//------------------------------------------------------------------------------
float TIniFile::ReadFloat(wxString Section,wxString Ident,float Default)
{
	wxString Result;
	Result = Result.Format(_T("%f"),Default);
	Result=ReadString(Section,Ident,Result);
	double dblVal;
	Result.Replace(wxT("."), wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT,wxLOCALE_CAT_NUMBER));
	if(Result.ToDouble(&dblVal)) return dblVal;
	else return 0;
}
//------------------------------------------------------------------------------
long TIniFile::ReadLong(wxString Section,wxString Ident,long Default)
{
	wxString Result;
	Result = Result.Format(_T("%d"),Default);
	Result=ReadString(Section,Ident,Result);
	long dblVal;
	Result.ToLong(&dblVal);
	return dblVal;
}
//------------------------------------------------------------------------------
unsigned long TIniFile::ReadULong(wxString Section,wxString Ident,unsigned long Default)
{
	wxString Result;
	Result = Result.Format(_T("%u"),Default);
	Result=ReadString(Section,Ident,Result);
	unsigned long dblVal;
	Result.ToULong(&dblVal);
	return dblVal;
}
//------------------------------------------------------------------------------
bool TIniFile::ReadBool(wxString Section,wxString Ident,bool Default)
{
	wxString Result;
	if(Default) Result = _T("1"); else Result = _T("0");
	Result=ReadString(Section,Ident,Result);
	if(Result==_T("1")) return true; else return false;
}
//------------------------------------------------------------------------------
void TIniFile::WriteString(wxString Section,wxString Ident,wxString Value)
{
	//ищем старое значение в заданной секции
	bool b=false;
	TIniStruct* lastSel = NULL; //Последний из тойже секции
	TIniStruct* inistr = first;
	while (inistr!=NULL)
	{
		if(inistr->section==Section)
		{
			lastSel=inistr;
			if(inistr->ident==Ident)
			{	inistr->value=Value;
				b=true;
				break;
			}
		}
		inistr=inistr->next;
	}

	//если не найденно то добавляем новое значение
	if (!b)
	{
		TIniStruct* inistr= new TIniStruct;
		inistr->next=NULL;
		inistr->section=Section;
		inistr->ident=Ident;
		inistr->value=Value;

		if (first==NULL)
		{
			first=inistr;
			last=inistr;
		}else
		{
			if(lastSel==NULL)
			{
				last->next=inistr;
				last=inistr;
			}
			else
			{
				inistr->next=lastSel->next;
				lastSel->next=inistr;
			}
		}
	}
}
//------------------------------------------------------------------------------
void TIniFile::WriteFloat(wxString Section,wxString Ident,float Value)
{
	wxString Result;
	Result = Result.Format(_T("%f"),Value);
	Result.Replace(wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT,wxLOCALE_CAT_NUMBER),wxT("."));
	WriteString(Section,Ident,Result);
}
//------------------------------------------------------------------------------
void TIniFile::WriteLong(wxString Section,wxString Ident,long Value)
{
	wxString Result;
	Result = Result.Format(_T("%d"),Value);
	WriteString(Section,Ident,Result);
}
//------------------------------------------------------------------------------
void TIniFile::WriteULong(wxString Section,wxString Ident,unsigned long Value)
{
	wxString Result;
	Result = Result.Format(_T("%u"),Value);
	WriteString(Section,Ident,Result);
}
//------------------------------------------------------------------------------
void TIniFile::WriteBool(wxString Section,wxString Ident,bool Value)
{
	wxString Result;
	if(Value) Result =_T("1"); else Result =_T("0");
	WriteString(Section,Ident,Result);
}
//------------------------------------------------------------------------------
//Сохранить из памяти в файл
void TIniFile::Save()
{
	wxTextFile* TextFile = new wxTextFile();
	if(!wxFileExists(path)) TextFile->Create(path);
	TextFile->Open(path);
	if(TextFile->IsOpened())
	{
		TextFile->Clear();
		wxString LastSection=_T("");
		TIniStruct* inistr = first;
		while (inistr!=NULL)
		{
			if (inistr->section!=LastSection)
			{
				TextFile->AddLine(_T("[")+inistr->section+_T("]"));
				LastSection=inistr->section;
			}
			TextFile->AddLine(inistr->ident+_T("=")+inistr->value);
			inistr=inistr->next;
		}
		TextFile->Write();
		TextFile->Close();
	}
	delete TextFile;
}
//******************************************************************************
TFileList::TFileList()
{
	size=20;
	List = new LStream*[size];
	count=0;
}
//------------------------------------------------------------------------------
TFileList::~TFileList()
{
	for(unsigned int i=0;i<count;i++)
	{
		delete List[i]->is;
		free(List[i]->data);
		delete List[i];
	}
	delete[] List;
}
//------------------------------------------------------------------------------
void TFileList::add(wxInputStream *is,wxString name)
{
	if(count>=size)return;

	LStream *ls=new LStream;
	ls->name=name;
	ls->size=(unsigned int)is->GetSize();
	ls->data=(char*)malloc(ls->size); //временно в поток потому что текстуру загружать в потоке отличном от контекста нельзя
	is->Read(ls->data,ls->size);
	ls->is=new wxMemoryInputStream(ls->data,ls->size);
	List[count]=ls;
	count++;
}
//------------------------------------------------------------------------------
wxInputStream* TFileList::get(wxString name)
{
	for(unsigned int i=0;i<count;i++)
	{
		if(List[i]->name.Lower()==name.Lower()) return List[i]->is;
	}
	return NULL;
}
//------------------------------------------------------------------------------
unsigned int TFileList::getCount()
{
	return count;
}
//------------------------------------------------------------------------------
LStream* TFileList::item(unsigned int i)
{
	return List[i];
}
//******************************************************************************
//------------------------------------------------------------------------------
//THTTPDownload сам вызовет delete если параметр wxTHREAD_DETACHED
THTTPDownload::THTTPDownload(wxEvtHandler *parent,wxString url,int userInt,wxString userStr,void* userData,MyFuncPtrType userFun):wxThread(wxTHREAD_DETACHED)
{
	m_Parent=parent;

	m_CD=new RClientData;
	m_CD->url=url;
	m_CD->clientInt=userInt;
	m_CD->clientStr=userStr;
	m_CD->clientData=userData;
	m_CD->clientFun=userFun;

	m_CD->size=0;
	m_CD->data=NULL;

	m_Cancel=false;

	//Стартуем поток
	if( Create() != wxTHREAD_NO_ERROR )
	{	//wxLogError(_T("Can’t create thread!"));		
	}else Run();
}
//------------------------------------------------------------------------------
THTTPDownload::~THTTPDownload()
{
}
//------------------------------------------------------------------------------
void* THTTPDownload::Entry()
{
	m_CD->data=getDataFromURL(m_CD->url,&m_CD->size);
	//если заданна пользовательская функция для обработки данных
	if(m_CD->clientFun!=NULL)
	{
		MyFuncPtrType my_func_ptr=m_CD->clientFun;
		(*my_func_ptr)(m_CD->size,m_CD->data,m_CD->clientData);
	}
	//Посылаем событие о окончании обработки данных
	if (m_Parent)
	{
		wxCommandEvent e(wxEVT_COMMAND_BUTTON_CLICKED,wxID_ANY);
		e.SetClientData(m_CD);
		m_Parent->AddPendingEvent(e); //Добавляем событие в очередь обработки
	}
	return NULL;
}
//------------------------------------------------------------------------------
//Отменить загрузку
/*void THTTPDownload::Cancel()
{
	m_Cancel=true;
}*/
//------------------------------------------------------------------------------
//#endif
