#include "hacnvlib.h"
#include "halib.h"

#include <applicat.h>
#include <automask.h>
#include <defmask.h>
#include <progind.h>
#include <reprint.h>
#include <reputils.h>
#include <tabutil.h>

///////////////////////////////////////////////////////////
// THardy_iterator
///////////////////////////////////////////////////////////

bool THardy_iterator::cancelled() const
{
  return _pi != NULL && _pi->iscancelled();
}

bool THardy_iterator::ok() const
{
  if (cancelled())
    return _ht->log_cancelled();
  return _rec >= 0 && _rec < _ht->recordset().items();
}

THardy_iterator& THardy_iterator::operator=(TRecnotype n) 
{ 
  if (_pi != NULL)
    _pi->setstatus(n+1);
  _ht->recordset().move_to(_rec = n); 
  return *this;
}

THardy_iterator& THardy_iterator::operator++() 
{ 
  return *this = ++_rec; 
}

THardy_iterator::THardy_iterator(THardy_transfer* ht) : _ht(ht), _pi(NULL)
{
  const TRecnotype tot = _ht->recordset().items();
  TString title;
  title << _ht->title() << ": " << tot << ' ' << TR("righe");
  if (tot > 1)
    _pi = new TProgind(tot, title, true, true);
  else
    ::begin_wait();

  if (tot > 0)
    _ht->log(title);

  _rec = -1;
}

THardy_iterator::~THardy_iterator()
{
  if (_pi != NULL)
    delete _pi;
  else
    ::end_wait();
}

///////////////////////////////////////////////////////////
// Cache tabelle
///////////////////////////////////////////////////////////

class TCache_tab : public TCache_th
{
protected:
  virtual TObject* key2obj(const char* key);

public:
  virtual const TString& decode(const TToken_string& cod_codtab);
  TCache_tab(THardy_transfer* ht) : TCache_th(ht) {}
};

const TString& TCache_tab::decode(const TToken_string& cod_codtab) 
{
  TString4 cod; cod_codtab.get(0, cod);
  if (cod.full())
  {
    const TRectype& rec = *(const TRectype*)objptr(cod_codtab); 
    if (!rec.empty())
    {
      const char* field = "CODTAB";
      if (cod == "%TPM" || cod == "%TPP" || cod == "%TPI") // Tipo trasporto e porto 
        field = "S6";
      return rec.get(field);
    }
  }
  return EMPTY_STRING;
}

TObject* TCache_tab::key2obj(const char* key)
{
  TToken_string tok(key);
  TString4 tab = tok.get();  tab.upper();
  TString80 cod = tok.get(); cod.upper();

  if (tab == "%IVA")
  {
    // Campo non digerisce i codici IVA numerici di un solo carattere
    if (cod.len() < 2 && isdigit(cod[0]))
      cod.right_just(2, '0'); // per cui aggiungo uno 0 iniziale
  }

  TTable table(tab);
  table.put("CODTAB", cod);
  if (table.read() != NOERR)
  {
    table.zero();
    table.put("CODTAB", cod);
    table.put("S0", cod);
    test_write(table);
  }
  return table.curr().dup();
}


///////////////////////////////////////////////////////////
// THardy_transfer
///////////////////////////////////////////////////////////

void THardy_transfer::init(const char* title, const char* qry_hdr, THardy_log& log)
{
  _log = &log;
  _log->reset();
  _log->set_title(title);
  _query_header = qry_hdr;
  _write_enabled = true;
}

const TString& THardy_transfer::title() const
{ return _log->title(); }

void THardy_transfer::log(const char* msg, int sev) const
{
  _log->log(sev, msg);
}

TRecordset& THardy_transfer::create_recordset(const char* query)
{
  if (_recset != NULL)
  {
    delete _recset;
    _recset = NULL;
  }
  if (_outset != NULL)
  {
    _outset->exec("COMMIT TRANS");
    delete _outset;
    _outset = NULL;
  }

  TString qry = query;
  if (!qry.starts_with("US") && !qry.starts_with("ODBC"))
    qry.insert(query_header());
  _recset = ::create_recordset(qry);
  return *_recset;
}
  
const TRecordset& THardy_transfer::recordset() const
{
  CHECK(_recset != NULL, "NULL recordset");
  return *_recset;
}

TRecordset& THardy_transfer::recordset()
{
  CHECK(_recset != NULL, "NULL recordset");
  return *_recset;
}

long THardy_transfer::odbc_exec(const char* cmd)
{
  long err = 0;
  if (_write_enabled)
  {
    if (_outset == NULL)
    {
      _outset = new TODBC_recordset(query_header());
      err = _outset->exec("BEGIN TRANS");
    }
    if (err >= 0)
    {
      err = _outset->exec(cmd);
      if (err < 0)
      {
        log("", 0);                       // Salto una riga
        TParagraph_string msg(cmd, 100);
        FOR_EACH_TOKEN(msg, str)         
          log(str, 0);                    // Riporto tutta la query
        log_error(TR("ERRORE in esecuzione della query."));
      }
    }
    else
    {
      TString msg;
      msg << TR("ERRORE di connessione a ") << _outset->dsn() << " : BEGIN TRANS";
      log_error(msg);
    }
  }
  return err;
}

bool THardy_transfer::log_error(const char* msg)
{
  log(msg, 2);
  if (_write_enabled)
  {
    _write_enabled = false;
    log("");
    log(TR("LA SCRITTURA SUGLI ARCHIVI VIENE DISABILITATA DA QUESTO MOMENTO IN POI"), 2);
    log("");
  }
  return false;
}

bool THardy_transfer::log_cancelled()
{
  return log_error(TR("Procedura interrotta dall'utente"));
}

void THardy_transfer::show_log()
{
  _log->preview();
  _log->destroy();
}

bool THardy_transfer::test_write(TBaseisamfile& file)
{
  int err = NOERR;
  if (_write_enabled)
  {
    err = file.write_rewrite();
    if (err != NOERR)
    {
      TString msg; 
      TString80 code, desc;
      const int logic = file.num();
      if (logic == LF_TAB || logic == LF_TABCOM || logic == LF_TABMOD)
      {
        code = file.get("CODTAB");
        desc = file.get("S0");
      }
      else
      {
        code = file.curr().build_key(1);
        desc = file.curr().build_key(2);
      }
      msg.format(FR("Errore %d durante la scrittura sul file %d: %s - %s"), 
                 err, logic, (const char*)code, (const char*)desc);
      log_error(msg);
    }
  }
  return err == NOERR;
}

const TString& THardy_transfer::get_str(const char* field) const
{
  return recordset().get(field).as_string();
}

const TString& THardy_transfer::get_real_str(const char* campo) const
{
  const TVariant& var = recordset().get(campo);
  if (var.is_zero())
    return EMPTY_STRING;
  return var.as_string();
}

real THardy_transfer::get_real(const char* field) const
{
  return recordset().get(field).as_real();
}

long THardy_transfer::get_long(const char* field) const
{
  return recordset().get(field).as_int();
}

TDate THardy_transfer::get_date(const char* field) const
{
  return recordset().get(field).as_date();
}

long THardy_transfer::get_cli(const char* field) const
{
   const TString& key = get_str(field);
   return hd_key2cli(key);
}

long THardy_transfer::get_for(const char* field) const
{
   const TString& key = get_str(field);
   return hd_key2for(key);
}


const TString& THardy_transfer::decode_value(const char* tab, const TString& cod)
{
  if (cod.full())
  {
    if (_tab == NULL)
      _tab = new TCache_tab(this);
    TToken_string tok; tok.add(tab); tok.add(cod);
    return _tab->decode(tok);
  }
  return EMPTY_STRING;
}

const TString& THardy_transfer::decode_field(const char* tab, const char* field)
{
  const TString& cod = get_str(field);
  return decode_value(tab, cod);
}

const TString& THardy_transfer::build_insert_query(const char* table, const char* f, const char* v) const
{
  TString qry(255);

  qry << "INSERT INTO " << table;

  TAuto_token_string fields(f);
  TToken_string values(v);
  if (fields.items() > 0)
  {
    qry << " (";
    FOR_EACH_TOKEN(fields, tok)
      qry << tok << ',';
    qry.rtrim(1);
    qry << ')';
  }
  qry << " VALUES (";

  TString tmp;
  FOR_EACH_TOKEN(values, tok)
  {
    tmp = tok;
    if (tmp.full() && !tmp.starts_with("0") && real::is_natural(tmp))
      qry << tok;
    else
    {
      if (tmp[0] != '\'')
      {
        for (int i = tmp.len()-1; i >= 0; i--)
        {
          if (tmp[i] == '\'')
            tmp.insert("'", i);
        }
        qry << '\'' << tmp << '\'';
      }
      else
        qry << tmp;
    }
    qry << ',';
  }
  qry.rtrim(1);
  qry << ')';

  return get_tmp_string() = qry;
}

THardy_transfer::THardy_transfer() 
              : _log(NULL), _config("hacnv100a.ini"), _recset(NULL), _outset(NULL), _tab(NULL)
{}

THardy_transfer::~THardy_transfer()
{
  if (_tab != NULL)
    delete _tab;
  if (_outset != NULL)
  {
    _outset->exec("COMMIT TRANS");
    delete _outset;
  }
  if (_recset != NULL)
    delete _recset;
}

void THardy_transfer::aggiorna_record(TRectype& rec, const TString_array& lista_campi)
{
	TString campo_dest, campo_orig, valore, str;
	FOR_EACH_ARRAY_ROW(lista_campi,i,row)
	{
		row->get(0, campo_dest); 
		row->get(1, campo_orig);
		if (campo_orig.full())
		{
			if (campo_orig[0] == '_')
			{
        if (campo_orig.starts_with("_SCONTO")) // � uno sconto (ca..o!)
        {
          valore.cut(0);
          real sconto;
          TString8 field;
        	for (int i = 1; i < 6; i++)
          {
            field.format("Sconto%1d",i);
            sconto = get_real(field);
            sconto.round(2);
            if (sconto != ZERO)
            {
              valore << sconto.string();
              valore << "+";
            }
          }
          if (valore.len()>0)
            valore = valore.left(valore.len()-1);
        } else
        if (campo_orig.starts_with("_REAL")) // � un real
        {
          const TString80 campo = campo_orig.after(','); 
          valore = get_real_str(campo);
        } else
        if (campo_orig.starts_with("_ROUND")) // arrotondo a due decimali
        {
          const TString80 campo = campo_orig.after(','); 
          real contenuto = get_real(campo);
          contenuto.round(2);
          valore = contenuto.string();
        } else
        if (campo_orig.starts_with("_FISSO")) // valore fisso indicato in configurazione 
        {
  			  valore = campo_orig.after(','); 
          valore.trim();
        } else
				if (campo_orig.starts_with("_STREXPR")) // formato _STREXPR, espressione
				{
          TExpression expr(campo_orig.after(','), _strexpr);
          for (int v = 0; v < expr.numvar(); v++)
          {
            const char* varname = expr.varname(v);
            expr.setvar(v, get_str(varname));
          }
  			  valore = expr.as_string();
          valore.trim();
				}	else 
				if (campo_orig.starts_with("_TAB")) // formato _TAB,<tabella da leggere>,<valore CODTAB>, <campo da leggere>
				{
  				TToken_string elabora(campo_orig, ',');
					const TString4 tab = elabora.get(1); // tabella da leggere
					const TString16 codtab = get_str(elabora.get());
					const TString16 campotab = elabora.get();
					valore = cache().get(tab, codtab, campotab);
        } else
				if (campo_orig.starts_with("_TRADUCI"))
        {
          const TString80 campo = campo_orig.after(','); 
          const TString80 contenuto = get_str(campo);
         	TConfig& ini = config();
          valore = ini.get(contenuto,campo);
        }
        else
          valore.cut(0);
      }
      else
			  valore = get_str(campo_orig);
			rec.put(campo_dest, valore);
		}
	}
}

void THardy_transfer::aggiorna_ini(TConfig& conf, const TString_array& lista_campi)
{
	TString campo_dest, campo_orig, valore, str;
	FOR_EACH_ARRAY_ROW(lista_campi,i,row)
	{
		row->get(0, campo_dest); 
		row->get(1, campo_orig);
		if (campo_orig.full())
		{
			if (campo_orig[0] == '_')
			{
        if (campo_orig.starts_with("_FISSO")) // valore fisso indicato in configurazione 
        {
  			  valore = campo_orig.after(','); 
          valore.trim();
        } else
				if (campo_orig.starts_with("_STREXPR")) // formato _STREXPR, espressione
				{
          TExpression expr(campo_orig.after(','), _strexpr);
          for (int v = 0; v < expr.numvar(); v++)
          {
            const char* varname = expr.varname(v);
            expr.setvar(v, get_str(varname));
          }
  			  valore = expr.as_string();
          valore.trim();
				}	else 
				if (campo_orig.starts_with("_TAB")) // formato _TAB,<tabella da leggere>,<valore CODTAB>, <campo da leggere>
				{
  				TToken_string elabora(campo_orig, ',');
					const TString4 tab = elabora.get(1); // tabella da leggere
					const TString16 codtab = get_str(elabora.get());
					const TString16 campotab = elabora.get();
					valore = cache().get(tab, codtab, campotab);
        } 
        else
				if (campo_orig.starts_with("_TRADUCI"))
        {
          const TString80 campo = campo_orig.after(','); 
          const TString80 contenuto = get_str(campo);
         	TConfig& ini = config();
          valore = ini.get(contenuto,campo);
        }
        else
          valore.cut(0);
      }
      else
			  valore = get_str(campo_orig);
			conf.set(campo_dest, valore);
		}
	}
}