#ifndef CB6

#include <xvt.h>
#include <codeb.h>

#include <config.h>
#include <diction.h>
#include <extcdecl.h>
#include <lffiles.h>
#include <netsock.h>
#include <prefix.h>
#include <xml.h>

#include <strstrea.h>

class TDB_table : public TObject
{
  TString _study, _firm, _name;
  int _logicnum;
  int _mode, _index;
  int _rec_len;
  long _rec_count;
  TString _query;
  TString _query_name;
  long _recno;        // 0 = bof; 1 = first record; 2 second record
  TXmlItem _record;
  TString _record_buf, _key_buf;

protected:
  void build_soap_method(const char* strMethod, TXmlItem& method) const;
  int fill_record(const TXmlItem* res);

public:
  virtual bool ok() const { return rec_len() > 0; }

  int select_tag(int t);
  int tag() const { return _index; }
  int rec_len() const;
  long rec_count() const;
  bool bof() const { return _recno <= 0; }
  bool eof() const { return _recno > rec_count(); }
  char* get_record();
  char* get_key();
  long recno() const { return _recno; }

  int go_to(long recno);
  int skip_to(long recno, long offset);
  int seek(const char* key);

  TDB_table(const char* file, int mode, int index);
};

class TDB_manager : public TSocketClient
{
	TString _server;
  TArray _table;
  int _error;

protected:

public:
  TCodeb_handle open(const char* name, int mode, int index);
  int close(TCodeb_handle h);

  TDB_table& table(TCodeb_handle h) const;
  
  int set_error(int e) { return _error = e; }
  int get_error() const { return _error; }

  TXmlItem* call_soap(const TXmlItem& method, TXmlItem& answer);

  TDB_manager();
  ~TDB_manager();
};

static TDB_manager* _db = NULL;

///////////////////////////////////////////////////////////
// TDB_table
///////////////////////////////////////////////////////////

void TDB_table::build_soap_method(const char* strMethod, TXmlItem& method) const
{
  CHECKS(strMethod[0] == 'm' && strMethod[1] == ':', "Missing m: in SOAP method", strMethod);
  method.Destroy();
  method.SetTag(strMethod);
  method.AddSoapString("Study", _study);
  method.AddSoapString("Firm", _firm);
  method.AddSoapString("Table", _name);
  method.AddSoapInt("Index", _index);
}

long TDB_table::rec_count() const
{
  if (_rec_count <= 0)
  {
    TXmlItem method, answer;
    build_soap_method("m:TableRows", method);
    const TXmlItem* res = _db->call_soap(method, answer);
    if (res != NULL)
    {
      TString16 str;
      res->GetEnclosedText(str);
      (long&)_rec_count = atol(str);
    }
  }
  return _rec_count;
}

int TDB_table::fill_record(const TXmlItem* res)
{
  if (res != NULL)
  {
    const TXmlItem* rec = res->FindFirst("Record");

    _recno = atol(rec->GetAttr("RecNo"));
    if (_recno > _rec_count)
      _rec_count = _recno;

    const RecDes& rd = prefix().get_recdes(_logicnum);
    TString16 name;
    TString value;
    for (int i = 0; i < rec->GetChildren(); i++)
    {
      const TXmlItem& fld = *rec->GetChild(i);
      name = fld.GetAttr("Name"); name.upper();
      const int index = findfld(&rd, name);
      if (index != FIELDERR)
      {
        fld.GetEnclosedText(value);
        switch (rd.Fd[index].TypeF)
        {
        case _boolfld: 
          value = value[0] == '1' ? "Y" : "N"; break;
        case _intfld: 
        case _longfld:
        case _intzerofld: 
        case _longzerofld:
          value.right_just(rd.Fd[index].Len); break;
        case _memofld: 
          value.cut(0); break;
        default: 
          break;
        }
        _record_buf.overwrite(value, rd.Fd[index].RecOff); 
      }
    }
    return NOERR;
  }
  _recno = _rec_count*4+1; // EOF
  return _db->set_error(5);
}

int TDB_table::go_to(long recno)
{
  if (recno == _recno)
    return NOERR;

  TXmlItem method;
  build_soap_method("m:GetRecord", method);
  method.AddSoapInt("RecNo", recno);
  const TXmlItem* res = _db->call_soap(method, _record);
  return fill_record(res);
}

int TDB_table::skip_to(long recno, long offset)
{
  TXmlItem method;
  build_soap_method("m:SkipRecord", method);
  method.AddSoapInt("RecNo", recno);
  method.AddSoapInt("Offset", offset);
  const TXmlItem* res = _db->call_soap(method, _record);
  return fill_record(res);
}

char* TDB_table::get_record()
{
  return _record_buf.get_buffer();
}

char* TDB_table::get_key()
{
  _key_buf.cut(0);
  if (_logicnum > 0 && _index > 0 && _recno > 0 && _recno <= rec_count())
  {
    const TXmlItem* rec = _record.FindFirst("Record");
    if (rec != NULL)
    {
      const RecDes& rd = prefix().get_recdes(_logicnum);
      const KeyDes& kd = rd.Ky[_index-1];       // Elenco dei campi della chiave
      TString str;
      for (int i = 0; i < kd.NkFields; i++)
      {                        
        const int nf = kd.FieldSeq[i] % MaxFields;
        const RecFieldDes& rf = rd.Fd[nf];  
        for (int i = 0; i < rec->GetChildren(); i++)
        {
          const TXmlItem& fld = *rec->GetChild(i);
          if (fld.GetAttr("Name").compare(rf.Name, -1, true) == 0)
          {
            fld.GetEnclosedText(str);
            switch (rf.TypeF)
            {
            case _intfld: 
            case _longfld:
            case _intzerofld: 
            case _longzerofld:
              str.right_just(rf.Len); break;
            default:
              str.rpad(rf.Len); break;
            }
            _key_buf << str;
            break;
          }
        }
      }
    }
  }
  return _key_buf.get_buffer();
}

int TDB_table::seek(const char* key)
{
/*
  TXmlItem method;
  build_soap_method("m:SeekRecord", method);
  method.AddSoapString("Key", key);
  const TXmlItem* res = _db->call_soap(method, _record);
  return fill_record(res);
*/
  return skip_to(0, 0);
}

int TDB_table::rec_len() const
{
  if (_rec_len <= 0)
  {
    TXmlItem method, answer;
    build_soap_method("m:GetRecordLength", method);
    const TXmlItem* res = _db->call_soap(method, answer);
    if (res != NULL)
    {
      TString16 str;
      if (res->GetEnclosedText(str))
        (int&)_rec_len = atoi(str)+1;  // Add deleted flag (1 byte)
    }
  }
  return _rec_len;
}

int TDB_table::select_tag(int t)
{
  int err = NOERR;
  if (t != _index)
  {
    _index = t;

    if (ok())
    {
      TXmlItem method, answer;
      build_soap_method("m:CreateIndex", method);
    
      const RecDes& rd = prefix().get_recdes(_logicnum);
      const KeyDes& kd = rd.Ky[_index-1];       // Elenco dei campi della chiave
      for (int i = 0; i < kd.NkFields; i++)
      {                        
        const int nf = kd.FieldSeq[i] % MaxFields;
        const RecFieldDes& rf = rd.Fd[nf];  
        TXmlItem& fld = method.AddChild("Field");
        fld.SetAttr("Name", rf.Name);
        if (kd.FieldSeq[i] >= MaxFields)
          fld.SetAttr("Upper", "1");
      }

      TString16 str;
      const TXmlItem* res = _db->call_soap(method, answer);
      if (res != NULL)
        res->GetEnclosedText(str);
      if (str != "1")
        err = _db->set_error(883); // ???     
    }
  }
  return err;
}

TDB_table::TDB_table(const char* file, int mode, int index)
         : _mode(mode), _index(index), _recno(-1), _rec_count(-1), _rec_len(-1), _logicnum(0)
{
  TToken_string tok(file, '/' );
  const int dot = tok.rfind('.');
  if (dot > 0) tok.cut(dot);
  tok.replace('\\', '/');
  tok.upper();
  const int items = tok.items();
  tok.get(items-1, _name);
  tok.get(items-2, _firm);
  tok.get(items-3, _study);

  FileDes filedes;
  memset(&filedes, 0, sizeof(filedes));
  for (int i = LF_USER; i < 100; i++)
  {
    CGetFile(i, &filedes, _nolock, _nordir);
    if (_name.compare(filedes.SysName+1, -1, true) == 0)
    {
      _logicnum = i;
      break;
    }
  }
}

///////////////////////////////////////////////////////////
// TDB_manager
///////////////////////////////////////////////////////////

TCodeb_handle TDB_manager::open(const char* name, int mode, int index)
{
  TCodeb_handle h = -1; // Inizializzo handle non valido

    // Crea tabella
  TDB_table* t = new TDB_table(name, mode, index);
  if (t->ok())
  {
    // Cerca primo handle disponibile
    for (h = 0; _table.objptr(h) != NULL; h++);
    _table.add(t, h);
  }
  else
    delete t;

  return h;
}

int TDB_manager::close(TCodeb_handle h)
{
  const int err = _table.destroy(h) ? 0 : 2;
  return err;
}

TDB_table& TDB_manager::table(TCodeb_handle h) const
{
  TDB_table* t = (TDB_table*)_table.objptr(h);
  CHECKD(t != NULL, "Invalid TABLE handle ", h);
  return *t;
}

TXmlItem* TDB_manager::call_soap(const TXmlItem& method, TXmlItem& answer)
{
  TXmlItem* result = NULL;

  CONNID conn = 0;
  for (int i = 0; conn == 0 && i < 5; i++)
    conn = QueryConnection("", _server);
  if (conn != 0)
  {
    TString str(8192);
    method.AsString(str);
    if (HttpSoap(conn, str))
    {
      size_t size;
		  char* buf = (char*)GetBuffer(size);
      istrstream inf(buf, size);
      if (answer.Read(inf))
      {
        str = method.GetTag();
        str << "Result";
        result = answer.FindFirst(str);
      }
    }
    else
      error_box(FR("Impossible eseguire '%s' sul soap server '%s'"), 
                (const char*)method.GetTag(), (const char*)_server);

    RemoveConnection(conn);
  }
  else
    error_box(FR("Impossible connettersi al soap server '%s'"), (const char*)_server);
  
  return result;
}

TDB_manager::TDB_manager()
{
  TConfig ini(CONFIG_INSTALL, "Server");
  _server = ini.get("DataBase", NULL, -1, "127.0.0.1:3885");
}

TDB_manager::~TDB_manager()
{ }


///////////////////////////////////////////////////////////
// codeb.h implementation
///////////////////////////////////////////////////////////

void DB_init(void) 
{ 
  DB_exit();
  _db = new TDB_manager();
}

void DB_exit(void) 
{ 
  if (_db != NULL)
  {
    delete _db;
    _db = NULL;
  }
}

TCodeb_handle DB_open(const char *filename, int mode, int index) 
{ 
  return _db->open(filename, mode, index);
}

int DB_close(TCodeb_handle handle) 
{ 
  return _db->close(handle);
}

char* DB_getrecord(int handle) 
{ 
  return _db->table(handle).get_record();
}

int DB_reclen(int handle)
{ 
  return _db->table(handle).rec_len();
}

int DB_keylen(int handle)
{ 
  return 0;
}

long DB_recno(int handle)
{ 
  return _db->table(handle).recno();
}

long int DB_reccount(int handle)
{ 
  return _db->table(handle).rec_count();
}

int DB_tagselect(int handle,int index_no)
{ 
  return _db->table(handle).select_tag(index_no);
}

int DB_tagget(int handle)
{ 
  return _db->table(handle).tag();
}

int DB_first(int handle)
{ 
  return _db->table(handle).skip_to(0, 0);
}

int DB_last(int handle)
{ 
  return _db->table(handle).skip_to(-1, 0);
}

int DB_next(int handle)
{ 
  return DB_skip(handle, +1);
}

int DB_prev(int handle)
{ 
  return DB_skip(handle, -1);
}

int DB_skip(int handle,long offset)
{ 
  TDB_table& tab = _db->table(handle);
  return tab.skip_to(tab.recno(), offset);
}

int DB_lock(int handle)
{ 
  return 0;
}

int DB_unlock(int handle)
{ 
  return 0;
}

int DB_seek(int handle,char *key)
{ 
  return _db->table(handle).seek(key);
}

int DB_eof(int handle)
{ 
  return _db->table(handle).eof();
}

int DB_bof(int handle)
{ 
  return _db->table(handle).bof();
}

int DB_go(int handle,long recno)
{ 
  return _db->table(handle).go_to(recno);
}

int DB_delete(int handle)
{ 
  return 0;
}

int DB_recall(int handle)
{ 
  return 0;
}

int DB_delkey(int handle, char* key, long recno)
{ 
  return 0;
}

int DB_flush(int handle)
{ 
  return 0;
}

int DB_rewrite(int handle)
{ 
  return 0;
}

bool DB_has_memo(int handle) 
{ 
  return false;
}

int DB_add(int handle)
{ 
  return 0;
}

int DB_lockfile(int handle)
{ 
  return 0;
}

int DB_packfile(short vis, const char * filename, long eod)
{ 
  return 0;
}

int DB_packmemo(short vis, const char * filename)
{ 
  return 0;
}

int DB_packindex(short vis, const char * filename, RecDes *r, long *peod, bool ask )
{ 
  return 0;
}

int DB_build(const char * filename, RecDes *r)
{ 
  return 0;
}

int DB_recinfo(const char * filename, FileDes *d, RecDes *r, char* keys)
{ 
  return 0;
}

int DB_get_error(void)
{ 
  return _db->get_error();
}

void DB_zero_error(void)
{ 
  _db->set_error(0);
}

int DB_index_seek(int handle, char* from) 
{ 
  return DB_seek(handle, from);
}

long DB_index_recno(int handle)
{ 
  return DB_recno(handle);
}

long DB_index_next(int handle)
{ 
  return DB_next(handle);
}

char* DB_index_getkey(int handle)
{ 
  return _db->table(handle).get_key();
}

int DB_index_go(int handle, const char * key, long recno)
{ 
  return DB_go(handle, recno);
}

char* DB_memoptr(const int handle, const char * fieldname )
{ 
  return 0;
}

int DB_memowrite(const int handle, const char * fieldname, const char * data )
{ 
  return 0;
}

int DB_index_eof(int handle)
{ 
  return DB_eof(handle);
}

int DB_lock_rec(int handle,long nrec)
{ 
  return 0;
}

int DB_file_locked(int handle)
{ 
  return 0;
}

int DB_rec_locked(int handle,long nrec)
{ 
  return 0;
}

long DB_getconf()
{ 
  return 0;
}

long DB_changed(int handle)
{ 
  return 0;
}

#endif