#include "wxinc.h"
#include "xvt.h"

#include <wx/db.h>

///////////////////////////////////////////////////////////
// TwxConnectionDlg
///////////////////////////////////////////////////////////

class TwxConnectionDlg : public wxDialog
{
protected:
  wxTextCtrl* AddString(wxSizer* ctlSizer, int id, const char* label, wxString* str);

public:
  wxString _strDsn, _strUsr, _strPwd, _strDir;

  TwxConnectionDlg();
};

wxTextCtrl* TwxConnectionDlg::AddString(wxSizer* ctlSizer, int id, const char* label, wxString* str)
{
  wxStaticText* lbl = new wxStaticText(this, wxID_ANY, label);

  const int k = 20, b = k/10;
  wxTextCtrl* txt = new wxTextCtrl(this, id, wxEmptyString, wxDefaultPosition, wxSize(20*k, k), 
                                   0, wxTextValidator(wxFILTER_ASCII, str));
  ctlSizer->Add(lbl, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, b);
  ctlSizer->Add(txt, 1, wxALIGN_LEFT | wxALL, b);

  return txt;
}

TwxConnectionDlg::TwxConnectionDlg() : wxDialog(NULL, wxID_ANY, "ODBC", 
                                                wxDefaultPosition, wxDefaultSize,
                                                wxDEFAULT_DIALOG_STYLE)
{
  wxSizer* ctlTextSizer = new wxFlexGridSizer(4, 2, 8, 8);
  AddString(ctlTextSizer, 1001, "Dsn", &_strDsn);
  AddString(ctlTextSizer, 1002, "User", &_strUsr);
  AddString(ctlTextSizer, 1003, "Password", &_strPwd);
  AddString(ctlTextSizer, 1004, "Directory", &_strDir);

  wxSizer* ctlButtonSizer = CreateButtonSizer(wxOK | wxCANCEL);

  wxBoxSizer* ctlTopSizer = new wxBoxSizer(wxVERTICAL);
  ctlTopSizer->Add(ctlTextSizer,   0, wxALIGN_CENTER);
  ctlTopSizer->Add(ctlButtonSizer, 0, wxALIGN_CENTER);

  SetSizer(ctlTopSizer);
  ctlTopSizer->SetSizeHints(this);
}

///////////////////////////////////////////////////////////
// xvt_odbc_...
///////////////////////////////////////////////////////////

XVT_ODBC xvt_odbc_get_connection(const char* dsn, const char* usr, const char* pwd, const char* dir)
{
  static wxDbConnectInf* ci = NULL;
  if (ci == NULL)
  {
    ci = new wxDbConnectInf;
    ci->AllocHenv();
  }

  wxString strDsn(dsn), strUsr(usr), strPwd(pwd), strDir(dir);
  if (strDsn.IsEmpty())
  {
    TwxConnectionDlg* dlg = new TwxConnectionDlg;
    if (dlg->ShowModal() == wxID_OK)
    {
      strDsn = dlg->_strDsn;
      strUsr = dlg->_strUsr;
      strPwd = dlg->_strPwd;
      strDir = dlg->_strDir;
    }
    dlg->Destroy();
    if (strDsn.IsEmpty())
      return NULL;
  }

  bool bSuccess = false;
  wxDb* db = new wxDb(ci->GetHenv(), true);

  if (strDsn.Find(';') > 0)
    bSuccess = db->Open(strDsn, NULL, true);
  else
    bSuccess = db->Open(strDsn, strUsr, strPwd, true);

  if (!bSuccess)
  {
    delete db;
    db = NULL;
  }

  return (XVT_ODBC)db;
}

BOOLEAN xvt_odbc_free_connection(XVT_ODBC handle)
{
  BOOLEAN ok = handle != NULL;
  if (ok)
  {
    wxDb* db = (wxDb*)handle;
    db->CommitTrans();
    db->Close();
    delete db;
  }
  return ok;
}

ULONG xvt_odbc_execute(XVT_ODBC handle, const char* sql, ODBC_CALLBACK cb, void* jolly)
{
  ULONG nCount = 0;

  if (handle && sql && *sql)
  {
    wxDb* db = (wxDb*)handle;
    if (cb != NULL) // Ho una vera callback?
    {
      wxDbColInf* columns = NULL;
      short numcols = 0;

      if (db->ExecSql(sql, &columns, numcols) && numcols > 0)
      {
        const size_t BUF_SIZE = 1024*64;
        char* buffer = new char[BUF_SIZE];   // Valore di un singolo campo

        char** values = new char*[numcols];  // Lista dei valori del record corrente 
        memset(values, 0, numcols*sizeof(char*));
    
        char** names = new char*[numcols*2]; // Lista dei nomi dei campi e dei tipi
        memset(names, 0, numcols*2*sizeof(char*));

        short c;
        for (c = 0; c < numcols; c++)
        {
          const wxDbColInf& info = columns[c];
          names[c] = (char*)info.colName;
          switch (info.dbDataType)
          {
          case DB_DATA_TYPE_INTEGER: 
          case DB_DATA_TYPE_FLOAT: 
            names[c+numcols] = "NUMERIC"; break;
          case DB_DATA_TYPE_DATE: 
            names[c+numcols] = "DATE"; break;
          default: 
            names[c+numcols] = "VARCHAR"; break;
          }
        }

        wxArrayString data;
        for (nCount = 0; db->GetNext(); nCount++)
        {
          data.Empty(); // Svuota l'array
          for (c = 0; c < numcols; c++)
          {
            const wxDbColInf& info = columns[c];

            try 
            {
              SDWORD cbReturned = SQL_NULL_DATA;

              switch (info.dbDataType)
              {
              case DB_DATA_TYPE_DATE:
                {
                  db->GetData(c+1, SQL_C_CHAR, buffer, BUF_SIZE, &cbReturned);
                  int d = 0, m = 0, y = 0;
                  int n = sscanf(buffer, "%04d-%02d-%02d", &y, &m, &d);
                  if (n == 3 && d > 0)
                    sprintf(buffer, "%04d%02d%02d", y, m, d);
                  else
                    buffer[0] = '\0';
                }
                break;
              default:
                db->GetData(c+1, SQL_C_CHAR, buffer, BUF_SIZE, &cbReturned);
                break;
              }
              if (cbReturned == SQL_NULL_DATA)
                buffer[0] = '\0';
              data.Add(buffer);
            }
            catch (...) 
            {
              break;
            }
          }
          for (c = 0; c < (short)data.GetCount(); c++)
            values[c] = (char*)data[c].c_str();
          const int err = cb(jolly, numcols, values, names);
          if (err != 0)
            break;
        }

        delete values;   // butta la lista dei valori
        delete names;    // butta la lista dei nomi
        delete buffer;   // butta il buffer temporaneo
      }
    }
    else
    {
      // Senza callback mi limito a contare i records
      if (db->ExecSql(sql))
      {
        for (nCount = 0; db->GetNext(); nCount++);
      }
    }
  }  
  
  return nCount;
}

BOOLEAN xvt_odbc_driver(XVT_ODBC handle, char* str, int max_size) 
{ 
  if (str != NULL && max_size > 8)
  {
    if (handle != NULL)
    {
      const wxDb* db = (const wxDb*)handle;
      wxStrncpy(str, db->dbInf.driverName, max_size);
    }
    else
      wxStrncpy(str, "ODBC 2.0", max_size);
  }
  return handle != NULL; 
}