#include "plugin.hpp"

//--------- Двухсвязный список объектов ---------------

class Link {
  Link *prev;
  Link *next;
  public:
  Link(){prev=next=NULL;};
  void Clear(void){prev=next=NULL;};
  Link *Prev(void){return prev;};
  Link *Next(void){return next;};
  Link *Head(void);         // Находит голову цепочки
  Link *Tail(void);         // Находит хвост цепочки
  Link *Find(int num);     // Находит звено с номером num (счет с головы от 0)
  void After(Link *m);      // Вставляет новое звено m после this
  void Before(Link *m);     // Вставляет новое звено m перед this
  Link *Add(Link *m);       // Прицепляет новое звено m к концу цепочки. Возвращает m.
                            // this может указывать на любое звено. Может быть NULL.
  void Extract(void);       // Отцепляет звено this и смыкает цепочку
  Link *DestroyAll(void);   // Удаляет всю цепочку. Возвращает NULL.
  Link *Step(char &dir);    // Возвращает следующее согласно dir.
  Link *Destroy(char &dir); // Удаляет звено и смыкает цепочку.
/*   Возвращает указатель на другое звено (если dir='N' на next, иначе на prev).
  Если предполагаемый к возвращению указатель NULL (достигнут конец или начало
  цепочки), обнуляется dir и возвращается указатель обратного направления.
       Таким образом, в любом случае после выполнения функция вернет указатель
  на существующее звено цепочки. Если он окажется NULL, это будет означать, что
  удалена полностью вся цепочка).
*/
  };

//================== Преобразование данных ==================
BYTE Upper(BYTE c);
char *Upper(char *text);
__int64 a_i64(char *s, __int64 def, char dec=0);
   // Dec-строка c точкой в целое. dec чисел после точки.
   // При неудаче возвращает def
   // Например: a_i64("12.35",0,3) --> 12350

char *i64_a(char *s, __int64 val, int mins=1, int dec=0);
   // Целое в Dec-строку. Как минимум mins знаков всего.
   // mins>=0 - заполнитель слева пробел, mins<0 - заполнитель ноль.
   // dec чисел после точки. Возвращает s.

__int64 ah_i64(char *s, __int64 def);
   // Hex-строка в целое. При неудаче возвращает def

double a_dbl(char *s);
   // Возвращает double из строки формата g

void Bin_Hex(char *s, BYTE *b, int nb);
   // Преобразовывает строку байтов b длины nb в строку 16-ричных цифр s

void Hex_Bin(char *s, BYTE *b, int nb);
   // Преобразовывает строку 16-ричных цифр s в строку байтов b длины nb
   // Если s короче, или встречена не цифра, остаток заполняется нулями

//================== Операции с датой и временем ==================

//Представление даты:
//  __int32 - число дней с начала эры
//  Digit (char*) - dd.mm.yy
//  Text  (char*) - 12 сентября 1993 г.

extern BYTE Mon[];  // Список месяцев
extern char *DTcode[]; // Список кодов формата

bool DTbad(SYSTEMTIME *t); // Возвращает true, если плохая дата
void DTd2t(__int32 d, SYSTEMTIME *t);  // целая дата --> SYSTEMTIME
void DTt2t(__int32 ti, SYSTEMTIME *t); // целое время --> SYSTEMTIME
DWORD DTt2dat(SYSTEMTIME *t);  // SYSTEMTIME --> целая дата
DWORD DTt2tim(SYSTEMTIME *t);  // SYSTEMTIME --> целое время
void DTsf2t(char *s, char *f, SYSTEMTIME *t); // строка по формату --> SYSTEMTIME
int MyCmp(char *s, char *f);         // возврат = кол-во последоват. совпавших
char *DTs2f(char *s, char *f);       // строка формата в формат
char *DTf2s(char *f, char *s);       // формат в строку формата
char *DTf29(char *f, char *s);       // формат в маску редактирования
int DTw(char *f);                    // ширина по формату
char *DTstr(char *s, SYSTEMTIME *t, char *f); // SYSTEMTIME в строку по формату
char *DTstr(char *s, char *d, char *f);  // стандарт. строка в строку по формату

//================== Прочее ==================

bool NotNum(char c);
bool LatAlphaNum(BYTE c);
bool WinAlphaNum(BYTE c);
bool DosAlphaNum(BYTE c);
BYTE MyWrite(HANDLE h, void *buf, DWORD len=0);
BYTE MyRead(HANDLE h, void *buf, DWORD len);
void MyDebug(char *fmt,char *txt=NULL,__int64 p1=0,__int64 p2=0,__int64 p3=0,
  __int64 p4=0,__int64 p5=0,__int64 p6=0,__int64 p7=0,__int64 p8=0);

//--------- Работа с базами данных формата DBF ---------------
union dbVal {
  __int64 I;
  double  D;
  };

struct dbHeader {
  BYTE  type;
  BYTE  upd[3];
  DWORD nrec;
  WORD  start;
  WORD  reclen;
  WORD  spare1[8];
  BYTE  ind;
  BYTE  spare2[3];
  };

struct dbField:Link {
  char  name[11];
  BYTE  type;
  DWORD loc;
  BYTE  filen;
  BYTE  dec;
  BYTE  spare[14];
  BYTE  mskN;
  BYTE  indN;
  BYTE  mskV;
  BYTE  indV;
  };

struct dbBase {
  HANDLE f,m;
  char *fmtD,*fmtT; // указатели на форматы даты и даты времени
  dbHeader  dbH;
  dbField  *dbF;
  dbField  *cf;   // указатель на текущее поле в цепочке dbF
  DWORD    pos;   // смещение от начала файла до записи след. после текущей
  DWORD    cur;   // номер текущей записи счет от 1. 0 - ничего не прочитано
  BYTE     *rec;  // текущая запись
  WORD     nfil;  // число полей в записи
  WORD     tmem;  // тип Memo-файла 0-dBaseIII, 1-dBaseIV, 2-FoxPro
  WORD     lmem;  // длина блока в Memo-файле
  BYTE     *Nflg; // ссылка на служебное поле _NullFlags
  BYTE     *Hext; // ссылка на расширение заголовка
  WORD     lhext; // длина расширения заголовка
  WORD     upd;

  dbBase()  { ZeroMemory(this,sizeof(dbBase)); f=m=INVALID_HANDLE_VALUE; }
  ~dbBase() { Close(); }

  BYTE Invalid(void) { return (rec[0]=='*'); };
        // Возвращает 1, если запись помечена для удаления. Иначе 0.
  void Add(char *fname, char ftype, BYTE flen, BYTE fdec);
       // Добавляет структуру поля в цепочку dbF
  void AddF(dbField *of, char *fname=NULL, char ftype=0, BYTE flen=0, BYTE fdec=0);
       // Добавляет копию структуры поля of в цепочку dbF
       // Сверху копии лепит параметры, если заданы.
  BYTE AddNull(void);
       // Добавляет структуру поля _NullFlags в цепочку dbF.
       // Возвращает длину поля _NullFlags.
       // Если в этом поле нет необходимости, ничего не добавляется
       // и возвращается 0
  BYTE Open(char *file, BYTE ronly=0);
       // Открывает существующую БД и считывает первую запись
       // 0 - OK, 1 - плохо
  void OpenMemo(char *file);
       // Если есть мемо, открывает его и определяет тип и длину блока
  BYTE Create(char *file, BYTE t, dbBase *dc=NULL);
       // Создает новую БД, используя данные из струтуры dbBase
       // если задано dc, в новую базу копируется расширение заголовока
       // и его резервные поля
       // 0 - OK, 1 - плохо
  void SaveHeader(void);
       // Перезаписывает заголовок
  void Close(void);
       // Закрывает БД

  BYTE NextRec(void);
       // Читает следующую запись. Устанавливает pos и cur.
       // Возврат: 0 - OK, 1 - текущая запись была последней,
       //          2 - ошибка чтения файла.
  BYTE CurrRec(void);
       // Перечитывает текующую запись. Устанавливает pos и cur.
       // Возврат: 0 - OK, 1 - текущей записи не было (cur=0),
       //          2 - ошибка чтения файла.
  BYTE PrevRec(void);
       // Читает предыдующую запись. Устанавливает pos и cur.
       // Возврат: 0 - OK, 1 - текущая запись была первой,
       //          2 - ошибка чтения файла.
  BYTE Read(DWORD rn);
       // Читает запись с номером rn (счет от 1).
       // Устанавливает pos и cur. Запись становится текущей.
       // Возврат: 0 - OK, 1 - rn превышает число записей,
       //          2 - ошибка чтения файла.
  BYTE Write(void);
       // Записывает rec с текущей позиции pos.
       // Устанавливает pos и cur. Запись становится текущей.
       // Возврат: 0 - OK, 2 - ошибка записи файла.
  BYTE ReWrite(void);
       // Перезаписывает rec как текущую запись.
       // pos и cur не изменяется.
       // Возврат: 0 - OK, 1 - текущей записи не было (cur=0),
       //          2 - ошибка записи файла.
  BYTE Append(void);
       // Добавляет rec как новую запись в конец файла.
       // Устанавливает pos и cur, наращивает dbH.nrec. Запись становится текущей.
       // Возврат: 0 - OK, 2 - ошибка записи файла.

  dbField *FiName(char *fname);
       // Находит в цепочке dbF поле с именем fname.
       // Устанавливает cf, возвращает cf.
       // Если не нашла, устанавливает cf=NULL.
  dbField *FiNum(WORD fnum);
       // Находит в цепочке dbF поле с номером fnum (счет от 0).
       // Устанавливает cf, возвращает cf.
       // Если не нашла, устанавливает cf=NULL.
  BYTE FiNull(void);
       // Возвращает 1, если текущее поле не имеет значения, иначе 0.
  BYTE FiNotFull(void);
       // Возвращает 1, если текущее поле переменной длины неполное, иначе 0.
  WORD FiDisp(char* s, BYTE nll=1);
       // Преобразует значение поля в читабельный формат в зависимости от
       // его типа и помещает в стрку s. Завершает строку нулевым байтом.
       // Возвращает длину строки.
       // Если nll=1 и у поля нет значения (NULL), возвращает строку "Null".
       // Иначе возвращает содержимое поля, даже если оно помечено как Null.
  WORD FiDispE(char* s);
       // То же, но убирает левые пробелы. Используется для редактирования.
       // Возвращает содержимое поля, даже если оно помечено как Null.
  char *FiType(char *s);
       // Возвращает текстовае название поля вместе с длиной, точностью
       // и нуль-флагом 
  WORD FiWidth(void);
       // Возвращает число символов для текстового представления поля

  void Clear(void);
       // Зполняет пробелами rec
  void SetEmpty(void);
       // Заполняет пробелами или нулями текущее поле в rec.
  void SetNull(void);
       // Устанавливает NullFlag для текущего поля.
  void SetNotNull(void);
       // Сбрасывает NullFlag для текущего поля.
  void SetNotFull(void);
       // Устанавливает флаг неполноты для текущего поля переменной длины.
  void SetFull(void);
       // Сбрасывает флаг неполноты для текущего поля переменной длины.
  BYTE IsEmpty(void);
       // Определяет пусто ли текущее поле
  BYTE SetFiEmpty(char *fname);
       // То же с предварительным поиском поля по имени fname.
       // Если ОК, возвращает 0. Если поле не найдено возвращает 1.
  void SetLeft(char *fval);
       // Переносит fval в текущее поле в rec. Выравнивание влево.
  BYTE SetFiLeft(char *fname, char *fval);
       // То же с предварительным поиском поля по имени fname.
       // Если ОК, возвращает 0. Если поле не найдено возвращает 1.
  void SetRight(char *fval);
       // Переносит fval в текущее поле в rec. Выравнивание вправо.
  BYTE SetFiRight(char *fname, char *fval);
       // То же с предварительным поиском поля по имени fname.
       // Если ОК, возвращает 0. Если поле не найдено возвращает 1.
  void SetLong(long fval, char dec=1);
       // Если тип поля I (Integer*4), то так и вставляется. Иначе,
       // преобразует fval в текст, вставляет точку в нужном месте и
       // переносит его в текущее поле в rec. Выравнивание вправо.
       // Точка вставляется, если dec=1. Если же dec=0 то нет.
       // Например:
       // Поле Number 8.2 SetLong(12345[,1]) -->   123.45
       // Поле Number 8.2 SetLong(12345,0)   --> 12345.00
       // Поле Number 8.0 SetLong(12345[,1]) -->    12345
       // Поле Number 8.0 SetLong(12345,0)   -->    12345
       // Поле Number 6.2 SetLong(12345[,1]) -->   123.45
       // Поле Number 6.2 SetLong(12345,0)   -->   ***.**
  BYTE SetFiLong(char *fname, long fval, char dec=1);
       // То же с предварительным поиском поля по имени fname.
       // Если ОК, возвращает 0. Если поле не найдено возвращает 1.
  void SetDouble(double fval);
       // Преобразует fval в текст и переносит его в текущее поле в rec.
       // Выравнивание вправо. Учитывает число десятичных знаков.
  BYTE SetFiDouble(char *fname, double fval);
       // То же с предварительным поиском поля по имени fname.
       // Если ОК, возвращает 0. Если поле не найдено возвращает 1.
  void SetField(char *s);
       // Заполняет текущее поле из данного текста, преобразуя его в
       // зависимости от типа поля.

  void Accum(dbVal *V);
  BYTE Numeric(void);
  BYTE *GetByte(BYTE *s);
  void SetByte(BYTE *fval);
  BYTE GetMemo(char *file, DWORD *blocknum=NULL);
  char *GetRight(void);
       // Создает новую строку и переносит туда текущее поле.
       // Возвращает указатель на эту строку.
       // После использования нужно сделать delete.
       // Возвращает NULL, если текущего поля нет.
  char *GetRight(char *s);
       // Переносит текущее поле в строку s.
       // Возвращает указатель на эту строку.
       // Возвращает NULL, если текущего поля нет.
  char *GetRight(char *s, WORD n);
       // Переносит поле с номером n в строку s.
       // Возвращает указатель на эту строку.
       // Возвращает NULL, если текущего поля нет.
  char *GetLeft(char *s);
       // Переносит текущее поле в строку s, выравнивает влево (то есть
       // убирает левые пробелы).
       // Возвращает указатель на эту строку.
       // Возвращает NULL, если текущего поля нет.
  __int64 Get64(void);
  __int64 GetBinary(void);
  long GetLong(void);
       // Преобразует текст в текущем поле в целое число и возвращает его.
       // Действует сообразно с типом поля. Если тип T (DateTime), возвращает 0.
       // Если тип I (Integer*4), возвращает правильное значение поля.
       // Если тип L (Logical), возвращает соответственно 0(false) или 1(true).
       // В остальных случаях трактует поле как число в текстовом виде, причем
       // десятичная точка игнорируется и учитывается значение dec для поля:
       // Поле Number 6.2 содержит 123.45 --> GetLong()=12345
       // Поле Number 6.2 содержит 1234.5 --> GetLong()=123450
  char *GetDate(char *s);
       // Преобразовывает дату в текущем поле типа D (Date) в формат DateDigit
       // Результат помещает в s и возвращает указатель на него
  WORD GetDate(void);
       // Пытается представить текущее поле как дату.
       // Возвращает дату в формате здешних функций (см. выше).
       // Возвращает нуль, если попытка не удалась.
  void GetDT(WORD *d, WORD *t);
       // Трактует текущее поле как 8-байтовое DateTime binary (тип T).
       // Возвращает дату d и время t в формате здешних функций (см. выше).
       // Возвращает нули, если текущего поля нет.
  double GetDouble(void);
       // Преобразует текст в текущем поле в double число и возвращает его.
  };
