//------------------------------------------------------------------------------
#include "tiptopbd.h"
#include "wxTools.h"
#include "mathTools.h"
#include "tcDebug.h"
#include <wx/wfstream.h>
#include <math.h>

//------------------------------------------------------------------------------
//Преобразовать в строку в соответствии стипом
wxString getSQLValue(wxString t,wxString v)
{
	if( t==_T("object") )
	{
		if (v==_T("-1") || v==_T("")) v=_T("NULL");
	}
	else if( t==_T("i4") )
	{
		if( v==_T("") ) v=_T("NULL");
	}
	else if( t==_T("f8") )
	{
		if( v==_T("") ) v=_T("NULL");
		replaseChars(v,_T(','),_T('.')); //Разделитель целой и дробной части точка
	}
	else if(t==_T("b"))
	{
		if(v==_T("")) v=_T("NULL");
		else if(v==_T("1") || v==_T("t")) v=_T("true");
		else if(v==_T("0") || v==_T("f")) v=_T("false");
	}
	else if(t==_T("string"))
	{
		if(v==_T(""))
		{
			v=_T("NULL");
		}
		else
		{
			v=replaceStrings(v,_T("'"),_T("\\'")); //так как в SQL строку вставляется
			v=_T("'")+v+_T("'");
		}
	}
	else
		v=_T("'")+v+_T("'");
	return v;
}
//------------------------------------------------------------------------------
//Сохранить UTF8 строку не больше 256 байт
void saveUTF8StringBD(TiptopStream *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 байт
wxString loadUTF8StringBD(TiptopStream *is)
{
	char c;
	is->Read(&c,1);
	char* buf = new char[c];
	is->Read(buf,c);
	wxString str=wxString::FromUTF8(buf,c);
	delete[] buf;
	return str;
}
//------------------------------------------------------------------------------
/*TiptopIndexFreeSpase::TiptopIndexFreeSpase(TiptopTable* table)
{
m_table=table;
m_count=0;
//пытаемся открыть файл для чтения
m_fis=new wxFileInputStream(m_table->m_bd->m_path+IntToStr(m_table->m_id)+_T("_0"));
m_fos=new wxFileOutputStream(m_table->m_bd->m_path+IntToStr(m_table->m_id)+_T("_0"));
}
//------------------------------------------------------------------------------
TiptopIndexFreeSpase::~TiptopIndexFreeSpase()
{
delete m_fis;
delete m_fos;
}*/
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//Самый простой тип индекса просто список упоряд по возрастанию
TiptopIndex::TiptopIndex(TiptopField* fl)
{
	/*
	m_count=0;
	m_fl=fl; //Поле(столбец) таблицы
	wxString file=m_fl->m_tb->m_bd->m_path+IntToStr(m_fl->m_tb->m_id)+_T('_')+IntToStr(m_fl->m_id)+_T(".i");

	if(wxFileExists(file))
	{
	m_file=new wxFFile(file,_T("a+b"));
	m_file->Seek(0,wxFromStart);
	uint4 i=0;
	m_file->Read(&i,4); //id файла
	m_file->Read(&i,4); //Версия
	m_file->Read(&m_type,4); //Под версия
	m_file->Read(&m_count,4);
	}else
	{
	m_file=new wxFFile(file,_T("a+b"));
	m_file->Seek(0,wxFromStart);
	uint4 i=0;
	m_file->Write(&file_id,4); //id файла
	m_file->Write(&file_version,4); //Версия
	m_type=1;
	m_file->Write(&m_type,4); //Под версия
	m_file->Write(&i,4); //Количество
	}
	*/
}
//------------------------------------------------------------------------------
TiptopIndex::~TiptopIndex()
{
	delete m_file;
}
//------------------------------------------------------------------------------
//Добавить позицию в список (значение храниться в файле таблицы)
void TiptopIndex::add(uint4 pos,uint4 id)
{
	uint4 p;
	int4 num;
	getPos(id,p,num); //Узнаём номер элемента совподающего с текущем либо меньшим
	//Вставляем следующем элементом за num
	m_file->Seek(16+(num*4)+4+1,wxFromStart);
	for(uint4 i=num+1; i<m_count; i++) //Сдвигаем элементы
	{
		uint4 buf;
		m_file->Read(&buf,4); //надо проверить
		m_file->Write(&buf,4);
	}
	m_file->Seek(16+(num*4)+4+1,wxFromStart);
	m_file->Write(&pos,4);
	m_count++;
	m_file->Seek(12,wxFromStart);
	m_file->Write(&m_count,4);
}
//------------------------------------------------------------------------------
//Поиск позиции по значению в списке (Список по возрастанию)
//return точное совпадение то true вернулась меньшая запись то false
//val - Значение которое мы ищем
//pos - позиция с начала файла Указывает на найденное значение либо на меньшее (если меньшее return false)
bool TiptopIndex::getPos(uint4 val,uint4& pos,int4& num)
{
	/*
	if(m_count==0) //В списке ещё нет ни одного элемента
	{
	pos=0; num=-1;
	return false;
	}else
	if(m_count==1) //в списке 1 элемент
	{
	m_file->Seek(16+1,wxFromStart); //Курсор на 1й элемент
	m_file->Read(&pos,4);
	if(!m_fl->m_tb->ReadRecord(pos)) {pos=0; return false;}
	//Прочитали теперь проверяем
	if(m_fl->getUint4Val() < val) l = center; else r = center;

	}else
	{
	//Ищём среди сортированного списка значений делением на 2
	unsigned int l = 0;
	unsigned int r = m_count-1;
	unsigned int center=0;
	while (r-l>1)
	{
	center = (l+r) / 2;
	//Читаем запись по центру
	m_file->Seek(16+center,wxFromStart);
	unsigned int pos;
	m_file->Read(&pos,4);
	if(!m_fl->m_tb->ReadRecord(pos)) {pos=0; return false;}
	//Прочитали теперь проверяем
	if(m_fl->getUint4Val() < val) l = center; else r = center;
	}
	num=center;
	if(m_fl->getUint4Val()==val) return true; else return false;
	}
	*/
	return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
TiptopField::TiptopField(TiptopTable* tb)
{
	m_tb=tb;
	m_size=0;
	m_value=NULL;
	m_idx=NULL;
	m_NULL=false;
}
//------------------------------------------------------------------------------
TiptopField::~TiptopField()
{
	if(m_value!=NULL) free(m_value);
	if(m_idx!=NULL) delete m_idx;
}
//------------------------------------------------------------------------------
//value - данные (не забирает во владения)
//size - Размер
void TiptopField::setValue(void* value,uint4 size)
{
	if(m_value!=NULL)
	{
		free(m_value);
		m_value=NULL;
	}
	m_size=size;
	if(size==0) return;
	m_value=malloc(m_size);
	//copy data
	for(uint4 i=0; i<m_size; i++) ((char*)m_value)[i]=((char*)value)[i];
}
//------------------------------------------------------------------------------
//Читаем из потока
void TiptopField::setValue(TiptopStream* is,uint4 size)
{
	if(m_value!=NULL)
	{
		free(m_value);
		m_value=NULL;
	}
	m_size=size;
	if(size==0) return;
	m_value=malloc(m_size);
	//read data
	is->Read(m_value,size);
}
//------------------------------------------------------------------------------
void TiptopField::setValue(int value)
{
	setValue(&value,4);
}
//------------------------------------------------------------------------------
void TiptopField::setValue(bool value)
{
	setValue(&value,1);
}
//------------------------------------------------------------------------------
void TiptopField::setValue(double value)
{
	setValue(&value,8);
}
//------------------------------------------------------------------------------
void TiptopField::setValue(wxString value)
{
	const wxCharBuffer buff = value.mb_str(wxConvUTF8);
	setValue((void*)buff.data(),strlen(buff));
}
//------------------------------------------------------------------------------
uint4 TiptopField::getUint4Val()
{
	return *((uint4*)m_value);
}
//------------------------------------------------------------------------------
int4 TiptopField::getIntVal()
{
	return *((int4*)m_value);
}
//------------------------------------------------------------------------------
double TiptopField::getDblVal()
{
	return *((double*)m_value);
}
//------------------------------------------------------------------------------
//Клонировать узел
TiptopField* TiptopField::cloneNode()
{
	TiptopField* f=new TiptopField(NULL);
	f->m_type_id=m_type_id;
	f->m_size=m_size;
	f->m_value=malloc(m_size);
	memcpy(f->m_value,m_value,m_size);
	return f;
}
//------------------------------------------------------------------------------
//Размер данных с учётом размера под количество байт
uint4 TiptopField::getAllSize()
{
	uint4 size=m_size;
	switch(m_type_id)
	{
	case BD_UTF8_1:
		size+=1;
		break;
	case BD_UTF8_2:
		size+=2;
		break;
	case BD_GPS_8:
		size+=4;
		break;
	case BD_GPSS_8:
		size+=4;
		break;
	case BD_BLOB_4:
		size+=4;
		break;
	}
	return size;
}
//------------------------------------------------------------------------------
bool TiptopField::getBoolVal()
{
	return *((bool*)m_value);
}
//------------------------------------------------------------------------------
//Преобразовать данные в строку
wxString TiptopField::getStrVal()
{
	if(m_value==NULL)
		return _T(""); //Этого недолжно происходить

	if(m_type_id==BD_BOOL)
	{
		if(*((uint1*)m_value)==0) return _T("false");
		else return _T("true");
	}
	else if(m_type_id==BD_UINT1)
		return IntToStr(*((uint1*)m_value));
	else if(m_type_id==BD_UINT2)
		return IntToStr(*((uint2*)m_value));
	else if(m_type_id==BD_UINT4)
		return IntToStr(*((uint4*)m_value));
	else if(m_type_id==BD_INT4)
		return IntToStr(*((int4*)m_value));
	else if(m_type_id==BD_FLOAT4)
		return FloatToStr(*((float*)m_value),-1);
	else if(m_type_id==BD_FLOAT8)
		return DoubleToStr(*((double*)m_value),-1);
	else if(m_type_id==BD_UTF8_1 || m_type_id==BD_UTF8_2)
	{
		return wxString::FromUTF8((char*)m_value, m_size);
	}
	return _T("");
}
//------------------------------------------------------------------------------
//Преобразовать данные в строку в соответствии с типом для вставки в SQL
wxString TiptopField::getStrSQLVal()
{
	wxString val=getStrVal();
	switch(m_type_id)
	{
	case BD_BOOL:
	case BD_UINT1:
	case BD_UINT2:
	case BD_UINT4:
	case BD_UINT8:
	case BD_INT1:
	case BD_INT2:
	case BD_INT4:
	case BD_INT8:
	case BD_FLOAT4:
	case BD_FLOAT8:
		if(val==_T("")) val=_T("NULL");
		replaseChars(val,_T(','),_T('.')); //Разделитель целой и дробной части точка
		break;
	case BD_UTF8_1:
	case BD_UTF8_2:
		if(val==_T(""))
		{
			val=_T("NULL");
		}
		else
		{
			//$v=str_replace('\'','\\\'',$v); //так как в SQL строку вставляется
			val=_T("'")+val+_T("'");
		}
		break;
	default:
		val=_T("'")+val+_T("'");
		break;
	}
	return val;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//Конструктор таблицы базы данных
//bd - База данных
//id - id таблицы
TiptopTable::TiptopTable(TiptopBD* bd,uint4 id)
{
	m_bd=bd; //База данных
	m_id=id; //id таблицы (Либо запроса если таблица используется для передачи данных)
	m_Null=NULL;//байты для чтения NULL значений
	m_NullSize=0;//количество байт для NULL, = cell(count fields/8)
	m_Type=0; //По умолчанию плотная таблица

	//m_is=NULL;
	//m_os=NULL;

	//m_fos=NULL;
	m_file=NULL;
	m_stream=new TiptopStream();

	//m_maxid=0; //максимальный id для поля (Теперь по позиции)
	//m_ok=true;

	fields=new TSimpleList<TiptopField*>(10,true);
	m_count=0; //Кол-во записей


	m_IndexFreeSize=new TiptopIndexFreeSize();
}
//------------------------------------------------------------------------------
TiptopTable::~TiptopTable()
{
	delete fields;
	if(m_file!=NULL) delete m_file; //TODO удалить через потоки надо делать

	//if(m_is!=NULL) delete m_is;
	//if(m_osOwner && m_os!=NULL) delete m_os;
	delete m_stream;

	if(m_Null!=NULL)delete m_Null;

	delete m_IndexFreeSize;
}
//------------------------------------------------------------------------------
//Добавить поле к таблице становится владельцем
bool TiptopTable::AddField(TiptopField* fl)
{
	if(fields->count()>255) return false;
	fields->add(fl);
	m_NullSize=ceil(fields->count()/8.0f); //подсчитываем кол-во байтов нужных для NULL значений
	if(m_Null!=NULL) delete m_Null;
	m_Null=new uint1[m_NullSize];//байты для чтения NULL значений
	for(uint1 i=0; i<m_NullSize; i++) m_Null[i]=0;
	return true;
}
//------------------------------------------------------------------------------
//Добавить поле к таблице становится владельцем
TiptopField* TiptopTable::AddField(wxString name,uint1 type_id)
{
	TiptopField* fl=new TiptopField(this);
	fl->m_name=name;
	fl->m_type_id=type_id;
	AddField(fl);
	return fl;
}
//------------------------------------------------------------------------------
//Добавить поле к таблице + преобразование типа из текста
TiptopField* TiptopTable::AddField(wxString name,wxString type)
{
	uint1 type_id=0;
	//Типы данных из metadata.xml
	if(type==_T("b")) type_id=BD_BOOL;
	if(type==_T("i1")) type_id=BD_INT1;
	if(type==_T("i4") || type==_T("object")) type_id=BD_INT4;
	if(type==_T("i8")) type_id=BD_INT8;
	if(type==_T("f4")) type_id=BD_FLOAT4;
	if(type==_T("f8")) type_id=BD_FLOAT8;
	if(type==_T("string")) type_id=BD_UTF8_2; //max 65535 символов

	return AddField(name,type_id);
}
//------------------------------------------------------------------------------
//Прочитать таблицу из текущего открытого потока
bool TiptopTable::OpenTable()
{
	//if(!is->IsOk()) return false;

	uint4 i;
	uint2 s;
	uint1 count;
	//*****читаем заголовок таблицы*****
	m_stream->Read(&s,2);
	if(s!=file_id) return false; //id файла
	m_stream->Read(&s,2);
	if(s!=file_version) return false; //версия файла
	m_stream->Read(&m_id,4); //id файла (или запроса данных)
	m_stream->Read(&m_Type,1); //Тип таблицы 0-"плотная" или 1-"жидкая"
	m_name=loadUTF8StringBD(m_stream); //название таблицы
	m_stream->Read(&count,1); //количество полей (столбцов)
	for(i=0; i<count; i++)
	{
		TiptopField* tf=new TiptopField(this);
		tf->m_name=loadUTF8StringBD(m_stream); //название поля
		m_stream->Read(&tf->m_type_id,1);//id типа поля
		fields->add(tf);
	}
	m_NullSize=ceil(fields->count()/8.0f); //подсчитываем кол-во байтов нужных для NULL значений
	m_Null=new uint1[m_NullSize];//байты для чтения NULL значений

	return true;

	/*
	wxString path=m_bd->m_path+IntToStr(m_id)+_T(".t"); //по id ищем файл в этой же папке и считываем настройки
	if(wxFileExists(path))
	{
	m_is=new wxFileInputStream(path);
	//wxFileOutputStream* m_fos=new wxFileOutputStream(path);
	//m_file=new wxFFile(path,_T("a+b"));
	//m_file->Seek(0,wxFromStart);

	OpenTable(m_is);

	}else return false;
	return true;
	*/
}
//------------------------------------------------------------------------------
//Прочитать из файла (создаётся поток файл всегда открыт)
bool TiptopTable::OpenTable(wxString path)
{
	if(!wxFileExists(path)) return false;
	wxFileInputStream* is=new wxFileInputStream(path);

	//Читаем в память (TODO надо сделать работу с файлом)
	m_stream->Clear();
	m_stream->Write(is);
	delete is;
	return OpenTable();
}
//------------------------------------------------------------------------------
//Прочитать заголовок таблицы из потока
///\param is - Откуда читаем данне (не становится владельцем)
/*bool TiptopTable::OpenTable(wxInputStream* is)
{
if(!is->IsOk()) return false;

uint4 i; uint2 s;
uint1 count;
//*****читаем заголовок таблицы*****
is->Read(&s,2); if(s!=file_id) return false; //id файла
is->Read(&s,2); if(s!=file_version) return false; //версия файла
is->Read(&m_id,4); //id файла (или запроса данных)
is->Read(&m_Type,1); //Тип таблицы 0-"плотная" или 1-"жидкая"
m_name=loadUTF8String(is); //название таблицы
is->Read(&count,1); //количество полей (столбцов)
for(i=0;i<count;i++)
{
TiptopField* tf=new TiptopField(this);
tf->m_name=loadUTF8String(is); //название поля
is->Read(&tf->m_type_id,1);//id типа поля
fields->add(tf);
}
m_NullSize=ceil(fields->count()/8.0f); //подсчитываем кол-во байтов нужных для NULL значений
m_Null=new uint1[m_NullSize];//байты для чтения NULL значений

return true;
}*/
//------------------------------------------------------------------------------
//Прочитать таблицу из потока в оперативную память
///\param is - Содержит заголовок и данные (не становится вледельцем)
bool TiptopTable::ReadTableToMem(wxInputStream* is)
{
	if(is==NULL) return false;

	m_stream->Clear(); //Очищяем память
	m_stream->SeekWrite(0);
	m_stream->Write(is);

	return OpenTable(); //Читаем заголовок
}
//------------------------------------------------------------------------------
//Переместить курсор чтения на первую запись (чтоб потом можно было последовательно прочитать всю таблицу)
bool TiptopTable::SeekToStart()
{
	if(m_Pos.count()<1) return false;
	m_stream->SeekRead(m_Pos[0]);
	return true;
}
//------------------------------------------------------------------------------
/*bool TiptopTable::SetOutputStream(wxMemoryOutputStream* os, bool owner)
{
if(m_osOwner && m_os!=NULL) delete m_os;
m_os=os;
m_osOwner=owner;
return true;
}*/
//------------------------------------------------------------------------------
//Найти и прочитать запись с заданным значением (использую для поска по идентификатору поля)
//field - Название поля
//val - Значение поля
bool TiptopTable::ReadRecord(wxString field, uint4 val)
{
	TiptopField* fl=getField(field);
	if(fl && fl->m_idx)
	{
		//uint4 pos=fl->m_idx->getPos(val);
		uint4 pos;
		int4 num;
		return fl->m_idx->getPos(val,pos,num);
		//return ReadRecord(pos);
	}
	return false;
}
//------------------------------------------------------------------------------
//is - Уже установлен на позицию с которой надо читать запись
bool TiptopTable::ReadRecord(TiptopStream* is)
{
	//if(is==NULL || !is->CanRead() || is->Eof()) return false;
	uint4 size=0; //Сколько байт прочитали из потока
	uint4 pos=0; //Позиция начала записи

	pos=is->TellRead();
	if(pos>=is->GetSize()) return false;

	is->Read(m_Null,m_NullSize);
	size+=m_NullSize;//is->LastRead(); //Размер под NULL
	for(uint4 i=0; i<fields->count(); i++)
	{
		if(testBit(m_Null,i)) //Если есть данные то читаем
		{
			TiptopField* fl=fields->get(i);

			if(fl->m_type_id==BD_UINT1 || fl->m_type_id==BD_BOOL) //0
			{
				fl->setValue(is,1);
				size+=1;
			}
			else if(fl->m_type_id==BD_UINT4) //3
			{
				fl->setValue(is,4);
				size+=4;
			}
			else if(fl->m_type_id==BD_INT4) //13
			{
				fl->setValue(is,4);
				size+=4;
			}
			else if(fl->m_type_id==BD_INT8) //17
			{
				fl->setValue(is,8);
				size+=8;
			}
			else if(fl->m_type_id==BD_FLOAT4) //20
			{
				fl->setValue(is,4);
				size+=4;
			}
			else if(fl->m_type_id==BD_FLOAT8) //22
			{
				fl->setValue(is,8);
				size+=8;
			}
			else if(fl->m_type_id==BD_UTF8_1) //Не более 255 байт
			{
				uint1 i;
				is->Read(&i,1);
				fl->setValue(is,i);
				size+=1+i;
			}
			else if(fl->m_type_id==BD_UTF8_2) //Не более 65535 байт
			{
				uint2 i;
				is->Read(&i,2);
				fl->setValue(is,i);
				size+=2+i;
			}
			else if(fl->m_type_id==BD_GPS_8) //131
			{
				uint4 i;
				is->Read(&i,4);
				fl->setValue(is,i);
				size+=4+i;
			}
			else if(fl->m_type_id==BD_GPSS_8) //132 как двоичные данные
			{
				uint4 i;
				is->Read(&i,4);
				fl->setValue(is,i);
				size+=4+i;
			}
			else if(fl->m_type_id==BD_BLOB_4) //143
			{
				uint4 i;
				is->Read(&i,4);
				fl->setValue(is,i);
				size+=4+i;
			}
			else
				return false;
		}
		else
			fields->get(i)->setValue((void*)NULL,0); //NULL запись
	}

	if(size>0)
	{
		m_RecSize = size;
		m_RecPos = pos;
		return true;
	}
	else
		return false;
}
//------------------------------------------------------------------------------
//Перемещяем курсор в заданную позицию и читаем запись
//pos - позиция в файле если -1 то не смещяемся
bool TiptopTable::ReadRecord(int pos)
{
	if(pos>0)
		m_stream->SeekRead(pos);
	return ReadRecord(m_stream);
}
//------------------------------------------------------------------------------
//Прочитать следующую запись из таблицы
//savePos - Сохранять ли позицию записи при чтении
bool TiptopTable::ReadNextRecord(bool savePos)
{
	if(m_Type!=0) return false; //Пока только для плотной таблицы те. записи идут подподрят

	wxFileOffset ff=m_stream->TellRead(); //Начальная позиция записи

	if(ReadRecord(-1))
	{
		if(savePos) m_Pos.add(ff); //Для плотной таблицы нет индекса поэтому пишем позицию записи в файле
		m_count++; //Количество записей
		return true;
	}
	return false;
}
//------------------------------------------------------------------------------
//Прочитать запись по её порядковому номеру (Если списка позиций нет то последовательно читать с начала (TODO и создаёт этот список))
//num - Номер записи нумерация с 0
bool TiptopTable::ReadRecordByNum(uint4 num)
{
	if(num<m_Pos.count())
		return ReadRecord(m_Pos[num]);
	return false;
}
//------------------------------------------------------------------------------
//Записать заголовок таблицы в поток m_os
bool TiptopTable::WriteHeader()
{
	//if(m_os==NULL || !m_os->IsOk()) return false;
	m_stream->Write(&file_id,2);
	m_stream->Write(&file_version,2);
	m_stream->Write(&m_id,4);
	uint1 t=0;
	m_stream->Write(&t,1); //Протная таблица
	saveUTF8StringBD(m_stream,m_name); //max 256 байт
	t=fields->count();
	m_stream->Write(&t,1); //Не больше 256 столбцов
	for(uint4 i=0; i<fields->count(); i++)
	{
		saveUTF8StringBD(m_stream,fields->get(i)->m_name);
		m_stream->Write(&fields->get(i)->m_type_id,1);
	}
	return true;
}
//------------------------------------------------------------------------------
//Записать 1 запись в m_os из полей
//savePos - сохранять позицию записи
//findPos - Искать свободное место в потоке, если нет то надо установить курсор в нужное место заранее.
bool TiptopTable::WriteRecord(bool savePos, bool findPos)
{
	uint4 newSize=0;
	uint4 newPos=0;

	if(findPos) //Потому что некоторые потоки не умеют выполнять Seek
	{
		//подсчитываем размер (потом сделать автоматический подсчёт при обновлении полей)
		newSize=m_NullSize;
		for(uint4 i=0; i<fields->count(); i++)
		{
			newSize+=fields->get(i)->getAllSize();
		}

		newPos = findFreeSize(newSize); //Находим пустое место куда можно записать данные заданного размера (обычно в конец потока)
		m_stream->SeekWrite(newPos);
	}
	if(savePos) addRecPos(m_stream->TellWrite()); //Сохраняем адрес в потоке для текущей записи

	if(!WriteRecord()) return false;

	//закончили запись в поток отмечаем что место не пустое в индексе пустых мест
	if(findPos)
		m_IndexFreeSize->upd(newPos,newSize);

	return true;
}
//------------------------------------------------------------------------------
//Записать запись из полей в текущее положение потока m_os
bool TiptopTable::WriteRecord()
{
	//if(m_os==NULL || !m_os->IsOk() || m_Null==NULL) return false;

	//Записываем NULL значения
	for(uint1 i=0; i<m_NullSize; i++) m_Null[i]=0;
	for(uint4 i=0; i<fields->count(); i++)
	{
		if(fields->get(i)->m_value==NULL || fields->get(i)->m_size==0)
			setBit(m_Null,i,false);
		else
			setBit(m_Null,i,true);
	}

	m_stream->Write(m_Null,m_NullSize);
	//if(m_stream->LastWrite()!=m_NullSize) return false;

	//Записываем сами данные
	for(uint4 i=0; i<fields->count(); i++)
	{
		if(fields->get(i)->m_value!=NULL && fields->get(i)->m_size!=0)
		{
			switch(fields->get(i)->m_type_id) //Так как у разных типов своя структура.
			{
			case BD_UTF8_1:
			{
				uint1 ch=fields->get(i)->m_size;
				m_stream->Write(&ch,1);
				m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
			}
			break;
			case BD_UTF8_2:
			{
				uint2 ch=fields->get(i)->m_size;
				m_stream->Write(&ch,2);
				m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
			}
			break;
			case BD_BLOB_4:
			{
				uint4 ch=fields->get(i)->m_size;
				m_stream->Write(&ch,4);
				m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
			}
			break;
			default: //Для типов которым ненадо записывать размер
				m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
			}
		}
	}
	return true;
}
//------------------------------------------------------------------------------
//Обновить(удалить потом записать) 1 запись перед удалением запись должна быть прочитана для выяснения её позиции и размера.
bool TiptopTable::UpdateRecord()
{

	//if(m_os==NULL || !m_os->IsOk() || m_Null==NULL) return false;

	//if(m_noindex) return false; //Проверяем есть ли индекс позиций для всей таблицы

	//Подсчитываем размер новой записи если она равна старой то записываем в туже позицию
	uint4 newPos=m_RecPos;
	uint4 newSize=m_NullSize;
	for(uint4 i=0; i<fields->count(); i++)
	{
		newSize+=fields->get(i)->getAllSize();
	}

	if(newSize==m_RecSize) //Позиция и размер записи не меняется
	{
		m_stream->SeekWrite(m_RecPos);
		WriteRecord();
	}
	else
	{
		m_IndexFreeSize->add(m_RecPos, m_RecSize); //Добавляем пустое место в список
		m_Type=1; //Ставим признак не плотной таблицы
		newPos = findFreeSize(newSize); //Находим пустое место куда можно записать данные заданного размера (обычно в конец потока)
		m_stream->SeekWrite(newPos);

		if(WriteRecord())
		{
			unsigned int p;
			if(m_Pos.Position(m_RecPos,p))
				m_Pos.get(p)=newPos; //Обновляем позицию записи
			m_IndexFreeSize->upd(newPos,newSize); //Обновляем размер
		}
	}
	return true;
	// return false;
}
//------------------------------------------------------------------------------
bool TiptopTable::delRecPos(uint8 pos) //Удалить позицию записи в памяти
{
	unsigned int p;
	if(m_Pos.Position(pos,p))
	{
		m_Pos.del(p);
		return true;
	}
	else
		return false;
	//return false;
};
//------------------------------------------------------------------------------
/*bool TiptopTable::isOk()
{	return m_ok;
}*/
//------------------------------------------------------------------------------
//получить поле по имени
TiptopField* TiptopTable::getField(wxString name)
{
	name.Trim(true);
	name.Trim(false); //удалить пробелы с права и с лева
	for(uint4 i=0; i<fields->count(); i++)
		if(fields->get(i)->m_name==name) return fields->get(i);
	return NULL;
}
//------------------------------------------------------------------------------
//Добавить записи из заданной в текущую (поля должны совпадать по имени и по типу)
//Начинает читать со следующей позиции из tb без сохранения позиции
//tb - Таблица с которой читаем записи
//spw - сохранять ли позицию при записи
//spr - сохранять ли позицию при чтении
bool TiptopTable::AppendTable(TiptopTable* tb, bool spw, bool spr)
{
	if(tb==NULL) return false;
	bool result=true;

	int* fi0=new int[fields->count()]; //Позиции полей в локальной таблице
	int* fi1=new int[fields->count()]; //Позиции полей в присваиваемой таблице
	int pos=0; //Сколько нашли полей
	for(unsigned int i=0; i<fields->count(); i++)
	{
		for(unsigned int j=0; j<tb->fields->count(); j++)
		{
			if(fields->get(i)->m_name==tb->fields->get(j)->m_name && fields->get(i)->m_type_id == tb->fields->get(j)->m_type_id)
			{
				fi0[pos]=i;
				fi1[pos]=j;
				pos++;
				break;
			}
		}
	}
	//Обнулим чтоб в данные не попал мусор
	for(unsigned int i=0; i<fields->count(); i++)
		fields->get(i)->setValue((void*)NULL,0);
	//Ищем соответствующие поля
	if(pos>0)
	{
		while(tb->ReadNextRecord(spr)) //При считывании сохраняем позицию записи
		{
			for(int i=0; i<pos; i++)
			{
				fields->get(fi0[i])->setValue(tb->fields->get(fi1[i])->m_value,tb->fields->get(fi1[i])->m_size);
			}
			if(!WriteRecord(spw,true))
			{
				result=false;
				break;
			}
		}
	}
	delete fi0;
	delete fi1;
	return result;
}
//------------------------------------------------------------------------------
//Обновить записи из заданной в текущую (поля должны совпадать по имени и по типу)
//tb - Таблица с которой читаем записи
bool TiptopTable::UpdateTable(TiptopTable* tb)
{
	if(tb==NULL) return false;
	bool result=true;

	//строим табличку соответствия номеров одинаковых полей
	int* fi0=new int[fields->count()]; //Позиции полей в локальной таблице
	int* fi1=new int[fields->count()]; //Позиции полей в присваиваемой таблице
	unsigned int cnt=0; //Сколько нашли одинаковых полей
	for(unsigned int i=0; i<fields->count(); i++)
	{
		for(unsigned int j=0; j<tb->fields->count(); j++)
		{
			if(fields->get(i)->m_name==tb->fields->get(j)->m_name && fields->get(i)->m_type_id == tb->fields->get(j)->m_type_id)
			{
				fi0[cnt]=i;
				fi1[cnt]=j;
				cnt++;
				break;
			}
		}
	}

	//Обновляем записи (пока по простому по равенству с полем id потом надо передавать условия)
	if(cnt>0)
	{
		buildPosIndex(); //Строим индекс если ещё не создан

		tb->SeekToStart();
		while(tb->ReadNextRecord(true)) //При считывании сохраняем позицию записи
		{
			//Ищем и читаем локальную запись
			uint4 pos,num;
			if(FindFirstRecord(tb->getField(_T("id")),pos,num))
			{
				//Переписываем поля
				for(unsigned int i=0; i<cnt; i++)
				{
					fields->get(fi0[i])->setValue(tb->fields->get(fi1[i])->m_value,tb->fields->get(fi1[i])->m_size);
				}
				//Записываем в память
				if(!UpdateRecord())
				{
					result=false;
					break;
				}
			}
		}
	}

	delete fi0;
	delete fi1;
	return result;
}
//------------------------------------------------------------------------------
//Построить индекс позиций прочитав таблицу он начала до конца (только для плотной таблицы)
bool TiptopTable::buildPosIndex()
{
	if(m_Type!=0) return false; //только для плотной таблицы
	if(m_Pos.count()>0) ReadRecord(m_Pos.get(m_Pos.count()-1)); //Если есть хоть 1 позиция в списке позиций то перемещяемся на неё
	while(ReadNextRecord(true)) {} //В цикле читаем оставшиеся и запоминаем позиции
	return true;
}
//------------------------------------------------------------------------------
//создать таблицу по SQL запросу
bool TiptopTable::CreateTable(wxString SQL)
{
	m_name=getAfterLast(getBeforeFirst(SQL,_T("(")),' ');
	if(m_bd!=NULL && m_bd->TableExist(m_name)) return false;
	//добавляем поля по умолчанию id uint4,seq uint4,del uint1
	TiptopField* field;
	field=new TiptopField(this);
	//field->m_id=++m_maxid; //id поля
	field->m_name=_T("id"); //название поля
	field->m_type_id=BD_UINT4; //id типа данных
	field->m_NULL=false; //Может ли быть NULL
	field->m_index=true; //Нуждается ли в индексации
	field->m_idx=new TiptopIndex(field); //Индекс поля (пока только для поля id)
	fields->add(field);

	field=new TiptopField(this);
	//field->m_id=++m_maxid; //id поля
	field->m_name=_T("seq"); //название поля
	field->m_type_id=BD_UINT4; //id типа данных
	field->m_NULL=false;
	fields->add(field);

	field=new TiptopField(this);
	//field->m_id=++m_maxid; //id поля
	field->m_name=_T("del"); //название поля
	field->m_type_id=BD_UINT1; //id типа данных
	field->m_NULL=false;
	fields->add(field);

	//перебираем поля в SQL запросе и добавляем к списку
	//CREATE TABLE tablename(id uint1,data uint2,data3 double);
	wxString fields=getBeforeLast(getAfterFirst(SQL,_T("(")),')')+_T(",");
	while (fields!=_T(""))
	{
		wxString sField=getBeforeFirst(fields,_T(","));
		sField.Trim(true);
		sField.Trim(false); //удалить пробелы с права и с лева
		wxString sName=getBeforeFirst(sField,_T(" "));
		wxString sType=getAfterLast(sField,' ');

		field=new TiptopField(this);
		field->m_type_id=0;
		field->m_NULL=true;
		field->m_index=false;
		field->m_name=sName; //название поля
		if(sType.Lower()==_T("uint1"))
			field->m_type_id=BD_UINT1; //id типа данных
		else if(sType.Lower()==_T("uint2"))
			field->m_type_id=BD_UINT2; //id типа данных
		else if(sType.Lower()==_T("uint4"))
			field->m_type_id=BD_UINT4; //id типа данных
		else if(sType.Lower()==_T("utf8_1"))
			field->m_type_id=BD_UTF8_1; //id типа данных
		else if(sType.Lower()==_T("gps_8"))
			field->m_type_id=BD_GPS_8; //id типа данных
		else if(sType.Lower()==_T("gpss_8"))
			field->m_type_id=BD_GPSS_8; //id типа данных

		if(field->m_type_id!=0)
		{
			//field->m_id=++m_maxid; //id поля
			this->fields->add(field);
		}
		else
			delete field;

		fields=getAfterFirst(fields,_T(","));
	}
	//байты для чтения NULL значений
	m_NullSize=ceil(this->fields->count()/8.0f);
	m_Null=new uint1[m_NullSize];

	//создаём файл таблицы по собранным данным
	m_file=new wxFFile(m_bd->m_path+IntToStr(m_id)+_T(".t"),_T("a+b"));
	m_file->Seek(0,wxFromStart);

	uint1 i=0,t=0;
	m_file->Write(&file_id,4);
	m_file->Write(&file_version,4);
	m_file->Write(&t,1); //тип таблицы 0=системный, 1=пользовательский,...
	m_file->Write(&i,4); //резерв
	saveUTF8String(m_file,m_name);
	m_file->Write(&i,4); //время последнего обновления таблицы секунд с 2000 года
	m_file->Write(&i,4); //резерв
	//m_file->Write(&m_maxid,4); //max id поля
	uint4 count=this->fields->count();
	m_file->Write(&count,4);
	for(uint4 i=0; i<count; i++)
	{
		TiptopField* tf=this->fields->get(i);

		//m_file->Write(&tf->m_id,4); //id поля
		saveUTF8String(m_file,tf->m_name); //название
		m_file->Write(&tf->m_type_id,4); //id типа данных
		m_file->Write(&tf->m_NULL,1); //Может ли быть пустым
		m_file->Read(&tf->m_index,1); //Нужен ли индекс для этого поля (резерв)
	}
	return true;
}
//------------------------------------------------------------------------------
//добавить запись в таблицу а если есть индексы то и в индексы
//INSERT INTO test(id,seq,del,name)values(1,2,0,\"Проба"" текста\");
bool TiptopTable::InsertInto(wxString SQL)
{
	for(uint4 i=0; i<fields->count(); i++) fields->get(i)->setValue((void*)NULL,0); //обнуляем список столбцов

	wxString sFields=getBeforeFirst(getAfterFirst(SQL,_T("(")),_T(")"))+_T(","); //название полей через запятую
	wxString sValues=getBeforeLast(getAfterFirst(getAfterFirst(SQL,_T(")")),_T("(")),')')+_T(","); //значения через запятую
	while(sFields!=_T(""))
	{
		wxString name=cutFirstSubStr(sFields,_T(','));
		wxString value=_T("");
		TiptopField* fld=getField(name);
		if(fld==NULL) return false;
		//кроме текстового поля значения выбираются до запятой
		if(fld->m_type_id==BD_UTF8_1)
		{
			value=cutFirstSubStr(sValues,_T(',')); //TODO изменить только для тестирования (сейчас в тексте не должно быть запятых)
			value=_T("_")+value.SubString(1,value.length()-2); //1й символ размер строки

			wxCharBuffer buff = value.mb_str(wxConvUTF8);
			uint1 len=(uint1)strlen(buff);
			buff.data()[0]=len-1; //первый символ размер строки
			fld->setValue(buff.data(),len);
		}
		else if(fld->m_type_id==BD_UINT1)
		{
			value=cutFirstSubStr(sValues,_T(','));

			uint1 v_uint=StrToInt(value);
			fld->setValue(&v_uint,1);
		}
		else if(fld->m_type_id==BD_UINT4)
		{
			value=cutFirstSubStr(sValues,_T(','));

			uint4 v_uint=StrToInt(value);
			fld->setValue(&v_uint,4);
		}
		else if((fld->m_type_id==BD_GPS_4)||(fld->m_type_id==BD_GPS_8)||(fld->m_type_id==BD_GPSS_8)||(fld->m_type_id==BD_BLOB_4)) //если это массив байт то в качестве значения передаётся размер для выделения памяти
		{
			value=cutFirstSubStr(sValues,_T(','));

			uint4 v_uint=StrToInt(value);
			if(v_uint>0) fld->setValue(&v_uint,4);
		}
	}

	//все данные подготовленны теперь ищется свободное место в файле для записи данных
	uint4 size=m_NullSize; //байт под NULL
	for(uint4 i=0; i<fields->count(); i++)
	{
		if((fields->get(i)->m_type_id==BD_GPS_4)||(fields->get(i)->m_type_id==BD_GPS_8)||(fields->get(i)->m_type_id==BD_GPSS_8)||(fields->get(i)->m_type_id==BD_BLOB_4))
		{
			uint4 s=*(uint4*)fields->get(i)->m_value;
			size+=s+4; //размер + место под размер
		}
		else
			size+=fields->get(i)->getSize();
	}
	uint4 pos=findFreeSize(size); //позиция в файле куда можно записать данную запись
	getField(_T("id"))->m_idx->add(pos,getField(_T("id"))->getUint4Val()); //сохраняем в индексе позицию

	m_file->Seek(pos,wxFromStart);
	for(uint4 i=0; i<fields->count(); i++) setBit(m_Null,i,(fields->get(i)->m_size!=0));
	m_file->Write(m_Null,m_NullSize);
	for(uint4 i=0; i<fields->count(); i++)
		if(fields->get(i)->m_size>0)
		{
			m_file->Write(fields->get(i)->m_value,fields->get(i)->m_size); //пишем данные
			//если это даные переменной длины то записываем только размер а курсор перемещяем в конец данных
			if((fields->get(i)->m_type_id==BD_GPS_4)||(fields->get(i)->m_type_id==BD_GPS_8)||(fields->get(i)->m_type_id==BD_GPSS_8)||(fields->get(i)->m_type_id==BD_BLOB_4))
			{
				uint4 s=*(uint4*)fields->get(i)->m_value;
				m_file->Seek(s,wxFromCurrent);
			}
		}

	return true;
}
//------------------------------------------------------------------------------
//найти позицию с свободным местом для записи данных (из индекса свободных мест с начала файла)
//size - Размер необходимой свободной памяти 0 то макс свободное место
uint4 TiptopTable::findFreeSize(uint4 size)
{
	unsigned int pos=0;
	if(m_IndexFreeSize->get(size,pos))
	{
		return pos;
	}
	else
	{
		return m_stream->GetSize(); //Позиция конца файла
	}
}
//------------------------------------------------------------------------------
///<Найти позицию первой попавшейся записи с заданными значениями поля, результат позиция записи в памяти.
//fl - Поле не пренадлежащие таблице по которому будет искаться запись
//pos - результат позиция записи в потоке
//num - результат порядковый номер записи
bool TiptopTable::FindFirstRecord(TiptopField* fl,uint4 &pos,uint4 &num)
{
	int4 p=getNumFieldOnName(fl->m_name); //Найти позицию записи с таким же именем
	if(fields->get(p)->m_type_id!=fl->m_type_id) return false; //Несовпали типы полей

	//Если нету индекса то поиск будет осуществлятся простым перебором
	if(m_Type==0) SeekToStart();
	num=0;
	while(true)
	{
		if(m_Type!=0) //Не плотная таблица обязаны ходить по индексам
		{
			if(num>=m_Pos.count()) return true;
			m_stream->SeekRead(m_Pos[num]);
		}

		wxFileOffset ff=m_stream->TellRead(); //Начальная позиция записи
		if(!ReadRecord(-1)) return false;
		if(fields->get(p)->m_size==fl->m_size)
		{
			if(memcmp(fields->get(p)->m_value,fl->m_value,fl->m_size)==0)
			{
				pos=ff;
				return true;
			}
		}
		num++;
	}
	return false;
}
//------------------------------------------------------------------------------
int4 TiptopTable::getNumFieldOnName(wxString name)
{
	for(uint4 i=0; i<fields->count(); i++)
		if(fields->get(i)->m_name==name) return (int4)i;
	return -1;
}
//------------------------------------------------------------------------------
//Установить курсор в начало BLOB блока
//TODO надо сделать свой защищённый "поток"
wxFFile* TiptopTable::getStream(uint4 id,wxString field)
{
	/*	//Через индекстный файл находим позицию первого байта записи по id
	getField(_T("id"))->


	//Ищем
	uint4 seek=0;
	m_file->Read(m_Null,m_NullSize);
	int4 num=getNumFieldOnName(field);
	if(!testBit(m_Null,num)) return NULL; //если поле NULL то вернём NULL
	for(uint1 i=0;i<num;i++)
	{
	if(testBit(m_Null,i)) //если не NULL то учитываем размер
	{
	if(fields->get(i)->m_type_id==BD_UTF8_1) //Размер в 1м байте
	{
	m_file->Seek(seek,wxFromCurrent);
	uint1 size=0;
	m_file->Read(&size,1);
	m_file->Seek(size,wxFromCurrent);
	seek=0;
	}else
	if((fields->get(i)->m_type_id==BD_GPS_4)||(fields->get(i)->m_type_id==BD_GPS_8)||(fields->get(i)->m_type_id==BD_GPSS_8)||(fields->get(i)->m_type_id==BD_BLOB_4)) //Размер в 4х байтах
	{
	m_file->Seek(seek,wxFromCurrent);
	uint4 size=0;
	m_file->Read(&size,4);
	m_file->Seek(size,wxFromCurrent);
	seek=0;
	}
	else
	seek+=fields->get(i)->m_size;
	}
	}

	m_file->Seek(seek,wxFromCurrent);
	*/
	return m_file; //TODO надо как поток вернуть
}
//------------------------------------------------------------------------------
//Создаёт новый поток в который помещяет таблицу для чтения (TiptopTable не остаётся владельцем)
/*wxMemoryInputStream* TiptopTable::GetInputStream()
{
wxStreamBuffer* osb=m_os->GetOutputStreamBuffer();
wxMemoryInputStream* mis = new wxMemoryInputStream(osb->GetBufferStart(),osb->GetBufferSize());
return mis;
}*/
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
TiptopBD::TiptopBD()
{
	m_maxid=0;
	m_fis=NULL;
	m_fos=NULL;
	list=new TSimpleList<TiptopTable*>(10,true);
}
//------------------------------------------------------------------------------
TiptopBD::~TiptopBD()
{
	delete list;
	delete m_fos;
	delete m_fis;
}
//------------------------------------------------------------------------------
//задаётся путь к ini файлу в котором список таблиц
bool TiptopBD::Open(wxString path)
{
	m_path=path;
	//открываем файл базы и читаем настройки
	if(wxFileExists(path+_T("bd.bd")))
	{
		uint4 i,cnt,id;
		m_fis=new wxFileInputStream(path+_T("bd.bd"));
		m_fos=new wxFileOutputStream(path+_T("bd.bd"));
		m_fis->Read(&i,4);
		if(i!=file_id) goto Exit; //id файла
		m_fis->Read(&i,4);
		if(i!=file_version) goto Exit; //версия файла
		m_fis->Read(&m_maxid,4); //max id таблиц
		m_fis->Read(&cnt,4); //количество таблиц
		for(i=0; i<cnt; i++)
		{
			m_fis->Read(&id,4);//id таблицы
			TiptopTable* tb=new TiptopTable(this,id);
			if(tb->OpenTable()) list->add(tb);
			else delete tb;
		}
Exit:
		m_ok=false;
	}
	else  //создаём новый файл
	{
		if(!wxDirExists(path)) wxMkdir(path);
		m_fos=new wxFileOutputStream(path+_T("bd.bd"));
		m_fis=new wxFileInputStream(path+_T("bd.bd"));
		m_fos->Write(&file_id,4);
		m_fos->Write(&file_version,4);
		uint4 i=0;
		m_fos->Write(&i,4); //max id таблиц
		m_fos->Write(&i,4); //количество таблиц
	}
	return true;
}
//------------------------------------------------------------------------------
//существует ли таблица
bool TiptopBD::TableExist(wxString name)
{
	for(uint4 i=0; i<list->count(); i++)
		if(list->get(i)->m_name==name) return true;
	return false;
}
//------------------------------------------------------------------------------
//взять таблицу
TiptopTable* TiptopBD::getTable(wxString name)
{
	for(uint4 i=0; i<list->count(); i++)
		if(list->get(i)->m_name==name) return list->get(i);
	return NULL;
}
//------------------------------------------------------------------------------
bool TiptopBD::ExecSQL(wxString SQL) //выполнить SQL запрос без результата
{
	//TODO анализатор SQL надо сделать по символам

	//CREATE TABLE tablename(id uint1,data uint2,data3 double);
	//id,seq,del - создаются автоматически
	if(SQL.Find(_T("CREATE TABLE"))!=wxNOT_FOUND)
	{
		TiptopTable* tb=new TiptopTable(this,m_maxid+1);
		if(tb->CreateTable(SQL))
		{
			if(addTable(m_maxid+1)) //Запишем в файл списка таблиц
				list->add(tb);
		}
		else
		{
			delete tb;
			return false;
		}
	}
	else
		//INSERT INTO test(id,seq,del,name)values(1,2,0,"Проба текста");
		if(SQL.Find(_T("INSERT INTO"))!=wxNOT_FOUND)
		{
			TiptopTable* tb=getTable(getAfterLast(getBeforeFirst(SQL,_T("(")),' '));
			if(tb!=NULL) tb->InsertInto(SQL);
			else return false;
		}

	return true;
}
//------------------------------------------------------------------------------
//Записать id в файл списка таблиц, текущее количество +1
bool TiptopBD::addTable(uint4 id)
{
	uint4 cnt,max;
	m_fis->SeekI(8,wxFromStart);
	m_fis->Read(&max,4);
	m_fis->Read(&cnt,4);
	if(max>=id) return false;

	m_fos->SeekO(8,wxFromStart);
	m_fos->Write(&id,4);
	cnt++;
	m_fos->Write(&cnt,4);
	m_fos->SeekO((cnt-1)*4,wxFromCurrent);
	m_fos->Write(&id,4);

	m_maxid=id;
	return true;
}
//------------------------------------------------------------------------------
//Загрузить UTF8 строку из файла, строка не больше 256 байт
wxString loadUTF8String(wxInputStream *is)
{
	char c;
	is->Read(&c,1);
	char* buf = new char[c];
	is->Read(buf,c);
	wxString str=wxString::FromUTF8(buf,c);
	delete[] buf;
	return str;
}
//------------------------------------------------------------------------------
//Загрузить UTF8 строку из файла, строка не больше 256 байт
wxString loadUTF8String(wxFFile *is)
{
	char c;
	is->Read(&c,1);
	char* buf = new char[c];
	is->Read(buf,c);
	wxString str=wxString::FromUTF8(buf,c);
	delete[] buf;
	return str;
}
//------------------------------------------------------------------------------