#include "velib.h"
#include <clifo.h>

#ifndef __EXECP_H
#include <execp.h>
#endif

#ifndef __TABUTIL_H
#include <tabutil.h>
#endif

///////////////////////////////////////////////////////////
// Lista di documenti
///////////////////////////////////////////////////////////

TDate TLista_documenti::num2date(char provv, int anno, const char* codnum, long num) const
{
  TLocalisamfile doc(LF_DOC);
  CHECK(num > 0, "Numero documento nullo.");
  TDocumento::set_key(doc.curr(), provv, anno, codnum, num);
  
  if (doc.read(_isgteq) != NOERR)     // In caso d'errore ...
    doc.last();                       // prendi l'ultimo
  
  return doc.get("DATADOC");
}

int TLista_documenti::read(char provv, char tipocf, long clifo, int anno, 
                           TToken_string& tipidoc, TToken_string& statidoc,
                           const TDate& dd, const TDate& ad,
                           const char* codnum, long dn, long an)
{       
  CHECK(provv == 'D' || provv == 'P', "Provvisorio o Definitivo?");
  CHECK(tipocf == 'C' || tipocf == 'F', "Il tipo deve essere Cliente o Fornitore");
  CHECKD(clifo > 0L, "Codice cliente non valido", clifo);
  CHECKD(anno > 1900, "Anno non valido: ", anno);
  CHECK(!tipidoc.empty_items(), "Lista dei tipi documento vuota");
  CHECK(!statidoc.empty_items(), "Lista degli stati documento vuota");
  
  TRelation doc(LF_DOC);
  TRectype start(LF_DOC), stop(LF_DOC);

  start.put("TIPOCF", tipocf);
  stop.put("TIPOCF", tipocf);
  
  start.put("CODCF", clifo);
  stop.put("CODCF", clifo);
  
  start.put("PROVV", provv);
  stop.put("PROVV", provv);

  start.put("ANNO", anno);
  stop.put("ANNO", anno);
  
  if (dn > 0)  
  {
    start.put("DATADOC", num2date(provv, anno, codnum, dn));
    start.put("NDOC", dn);
  }
  else
  {
    if (dd.ok() && dd > botime)
      start.put("DATADOC", dd);
  }
  
  if (an > 0)  
  {
    stop.put("DATADOC", num2date(provv, anno, codnum, an));
    stop.put("NDOC", an);
  }
  else
  {
    if (ad.ok() && ad < eotime)
      stop.put("DATADOC", ad);
  }
  
  TString filter(16);
  if (codnum && *codnum)       
  {
    bool numfilter = FALSE;
    
    if (start.get("DATADOC").empty())
      numfilter = TRUE;
    else  
      start.put("CODNUM", codnum);

    if (stop.get("DATADOC").empty())
      numfilter = TRUE;
    else  
      stop.put("CODNUM", codnum);
      
    if (numfilter)
      filter << "CODNUM=\"" << codnum << '"'; 
  }

  TCursor cur(&doc, filter, 2, &start, &stop);
  const TRectype& head = cur.curr();
  
  _documenti.destroy();
  for (cur = 0; cur.ok(); ++cur)
  {                
    const TString16 tipodoc  = head.get("TIPODOC");
    const TString16 statodoc = head.get("STATO");
    if (tipidoc.get_pos(tipodoc) >= 0 && statidoc.get_pos(statodoc) >= 0)
    {
      TDocumento* d = new TDocumento(head);
      _documenti.add(d);
    }  
  }
  
  return _documenti.items();
}                            

int TLista_documenti::write(bool re) const
{
  int err = NOERR;                                      
  for (int i = 0; i < _documenti.items() && err == NOERR; i++)
    err = doc(i).write(re);
  return err;
}

///////////////////////////////////////////////////////////
// Cliente/Fornitore per vendite
///////////////////////////////////////////////////////////

void TLista_clifo::TClifo::init(const TRectype& rec, const TRectype& ven)
{                              
  _codice = rec.get_long(CLI_CODCF);
  CHECK(_codice > 0, "Codice cliente nullo");

  if (!ven.empty())
  {
    _agente = ven.get_long(CLI_CODAG);
    _zona   = ven.get_long(CLI_CODZONA);
  }
  else
    _agente = _zona = 0;
}

bool TLista_clifo::TClifo::read(char tipo, long cod)
{
  TRelation clifo(LF_CLIFO);
  clifo.add(LF_CFVEN, "TIPOCF=TIPOCF|CODCF=CODCF");
  
  TRectype& curr = clifo.curr();
  curr.put(CLI_TIPOCF, tipo);
  curr.put(CLI_CODCF, cod);
  if (clifo.read() == NOERR)
    init(curr, clifo.curr(LF_CFVEN));
  else
    zero();  
  
  return ok();  
}

TLista_clifo::TClifo::TClifo(const TRectype& rec)
{
  CHECK(rec.num() == LF_CLIFO, "Record non clienti");
  const char tipo = rec.get_char(CLI_TIPOCF);
  const long codice = rec.get_long(CLI_CODCF);
  read(tipo, codice);
}

int TLista_clifo::leggi(long dc, long ac, long da, long aa, long dz, long az)
{
  TRelation clifo(LF_CLIFO);
  clifo.add(LF_CFVEN, "TIPOCF=TIPOCF|CODCF=CODCF");
  
  TRectype start(LF_CLIFO), stop(LF_CLIFO);
  
  start.put(CLI_TIPOCF, tipo()); 
  if (dc > 0) 
    start.put(CLI_CODCF, dc); 

  stop.put(CLI_TIPOCF, tipo()); 
  if (ac > 0) 
    stop.put(CLI_CODCF, ac);
  
  TString filter(32);
  if (da > 0) 
    filter << '(' << LF_CFVEN << "->" << CLI_CODAG << ">=" << da << ')';
  if (aa > 0) 
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODAG << "<=" << aa << ')';
  }
  if (dz > 0) 
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << ">=" << dz << ')';
  }  
  if (az > 0) 
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << "<=" << az << ')';
  }  
  
  TCursor cur(&clifo, filter, 1, &start, &stop);
  const TRectype& cli = cur.curr();
  const TRectype& ven = cur.curr(LF_CFVEN);
  for (cur = 0; cur.ok(); ++cur)
  {
    TClifo* c = new TClifo(cli, ven);
    _clifo.add(c);
  }
  
  if (dc > 0 || ac > 0) ordina_per_codice(); else
  if (da > 0 || aa > 0) ordina_per_agente(); else
  if (dz > 0 || az > 0) ordina_per_zona();
  
  return _clifo.items();
}

int TLista_clifo::sort_by_code(const TObject** o1, const TObject** o2)
{
  TLista_clifo::TClifo* c1 = (TLista_clifo::TClifo*)*o1;
  TLista_clifo::TClifo* c2 = (TLista_clifo::TClifo*)*o2;
  const long d = c1->codice() - c2->codice();
  return d == 0L ? 0 : (d > 0 ? +1 : -1);
}

int TLista_clifo::sort_by_agent(const TObject** o1, const TObject** o2)
{
  TLista_clifo::TClifo* c1 = (TLista_clifo::TClifo*)*o1;
  TLista_clifo::TClifo* c2 = (TLista_clifo::TClifo*)*o2;
  const long d = c1->agente() - c2->agente();
  return d == 0L ? 0 : (d > 0 ? +1 : -1);
}

int TLista_clifo::sort_by_zone(const TObject** o1, const TObject** o2)
{
  TLista_clifo::TClifo* c1 = (TLista_clifo::TClifo*)*o1;
  TLista_clifo::TClifo* c2 = (TLista_clifo::TClifo*)*o2;
  const long d = c1->zona() - c2->zona();
  return d == 0L ? 0 : (d > 0 ? +1 : -1);
}

int TLista_clifo::ordina_per_codice()
{
  _clifo.sort(sort_by_code);
  return _clifo.items();
}

int TLista_clifo::ordina_per_agente()
{
  _clifo.sort(sort_by_agent);
  return _clifo.items();
}

int TLista_clifo::ordina_per_zona()
{
  _clifo.sort(sort_by_zone);
  return _clifo.items();
}

int TLista_clifo::find(long cod) const
{
  for (int i = items()-1; i >= 0; i--)
    if (clifo(i).codice() == cod) break;
  return i;  
}

int TLista_clifo::add(long cod)
{
  int pos = find(cod);
  if (pos < 0)
  {
    TClifo* c = new TClifo(tipo(), cod);
    pos = _clifo.add(c);
  }
  return pos;
}

///////////////////////////////////////////////////////////
// TElaborazione
///////////////////////////////////////////////////////////

TElaborazione::TElaborazione(const char* cod) : TRectype(LF_TABCOM)
{               
  settab("ELD");
  if (cod && *cod)
    read(cod);
}

int TElaborazione::read(const char* cod)
{            
  CHECK(cod && *cod, "Codice elaborazione nullo");
  TTable eld("%ELD");
  put("CODTAB", cod);
  const int err = TRectype::read(eld);
  if (err != NOERR)
    yesnofatal_box("Codice elaborazione non valido: %s", cod);
  return err;  
}

///////////////////////////////////////////////////////////
// TEsterna
///////////////////////////////////////////////////////////

TEsterna::TEsterna(const char* cod) 
        : TElaborazione(cod)
{
}

bool TEsterna::elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                       const TDate& data_elab)
{                                            
  CHECK(doc_in.items() == 1, "Si deve specificare uno e un solo documento in entrata");
  CHECK(doc_out.items() == 1, "Si deve specificare uno e un solo documento in uscita");
  TFilename name; name.temp("ext");
  {
    TConfig c(name);
    TDocumento & d = doc_in[0];
    int nfields = d.items();         
    TString par; par.format("%d,0", LF_DOC);
    
    for (int i = 0; i < nfields; i++)
    {                      
      const TString16 fname(d.fieldname(i));
      TFieldref f(fname, LF_DOC);

      f.write(c, par, d.get(fname));
    }     
    for (TVariable_field * v = d.first_variable_field(); v ; v = d.succ_variable_field())
      c.set(v->name(), v->get(), par);
    const int rows = d.physical_rows();
    if (rows > 0)
    {            
      nfields = d[0].items();   
      
      for (int r = 0; r < rows; r++)
      {
        TRiga_documento row = d[r];
        
        par.format("%d,%d", LF_RIGHEDOC, r + 1);
        for (int i = 0; i < nfields; i++)
        {                      
          const TString16 fname(row.fieldname(i));
          TFieldref f(fname, LF_RIGHEDOC);

          f.write(c, par, row.get(fname));
        }              
      }
    }                 
  }
  TString command_line(applicazione_esterna()); command_line << " -i " << name;
  TExternal_app app(command_line);
  if (app.run() == 0)
  {
    TConfig c(name);
    TDocumento & d = doc_out[0];
    int nfields = d.items();         
    TString par; par.format("%d,0", LF_DOC);
    
    for (int i = 0; i < nfields; i++)
    {                      
      const TString16 fname(d.fieldname(i));
      TFieldref f(fname, LF_DOC);

      d.put(fname, f.read(c, par));
    }     
    for (TVariable_field * v = d.first_variable_field(); v ; v = d.succ_variable_field())
      v->put(c.get(v->name(), par));

    TString_array p;
    
    c.list_paragraphs(p);
    nfields = d[0].items();   
    d.destroy_rows();
    int r = 1 ;
    par.format("%d,%d", LF_RIGHEDOC, r);
    while (p.find(par) >= 0)
    {                                 
      TRiga_documento row = d.new_row();
      for (int i = 0; i < nfields; i++)
      {                      
        const TString16 fname(row.fieldname(i));
        TFieldref f(fname, LF_RIGHEDOC);

        row.put(fname, f.read(c, par));
      }   
      r++;           
      par.format("%d,%d", LF_RIGHEDOC, r);
    }                 
  }
  return TRUE;     
}

///////////////////////////////////////////////////////////
// TConsegna ordini
///////////////////////////////////////////////////////////

TConsegna_ordini::TConsegna_ordini(const char* cod) 
                : TElaborazione(cod)
{
}

bool TConsegna_ordini::elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                               const TDate& data_elab)
{  
  return TRUE;     
}

///////////////////////////////////////////////////////////
// TCopia_documento
///////////////////////////////////////////////////////////

TCopia_documento::TCopia_documento(const char* cod) 
                : TElaborazione(cod)
{
}

bool TCopia_documento::elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                                 const TDate& data_elab)
{  
  CHECK(doc_in.items() == 1, "Si deve specificare uno e un solo documento in entrata");
  CHECK(doc_out.items() == 1, "Si deve specificare uno e un solo documento in uscita");
  TDocumento & doc_src = doc_in[0];
  TDocumento & doc_dest = doc_out[0];  

  doc_dest.copy_contents(doc_src); 
  return TRUE;     
} 

///////////////////////////////////////////////////////////
// TLista_elaborazioni
///////////////////////////////////////////////////////////

void TLista_elaborazioni::read()
{                        
  if (_elab == NULL)
  { 
    _elab = new TAssoc_array();
    
    TTable eld("%ELD");
  
    for (int err = eld.first(); err == NOERR; err = eld.next()) 
    { 
      TElaborazione * el = NULL;  
      switch (eld.curr().get_int("I0"))
      {
        case _esterna :
          el = new TEsterna(eld.curr());
          break;
        case _consegna_ordini:
          el = new TConsegna_ordini(eld.curr());
          break;  
        case _fatturazione_bolle :
          el = new TFatturazione_bolle(eld.curr());
          break;
        case _contabilizzazione :
          el = new TContabilizzazione(eld.curr());
          break;
        case _copia_documento :
          el = new TCopia_documento(eld.curr());
          break;
        case _generazione_effetti :
          el = new TGenerazione_effetti(eld.curr());
          break;
        default :
          break;         
      }
      _elab->add(el->codice(), el);  
    }
  }
}  

int TLista_elaborazioni::select(TString_array & result, bool interattivo, bool insert_mode, const char * tipo_iniziale, const char * stato_iniziale, const char * tipo_finale, const char * stato_finale)
{ 
  read();                     
  _elab->restart();
  result.destroy();
  for (TElaborazione * el = (TElaborazione *)_elab->get(); el ; el = (TElaborazione *) _elab->get())
  {             
    bool ok = TRUE;
    if (tipo_iniziale && stato_iniziale)
    {          
      bool found = FALSE;
      for (int i = 0; !found && i < 5; i++) 
        found = el->tipo_iniziale(i) == tipo_iniziale && el->stato_iniziale(i) == *stato_iniziale;
      ok = found;
    }
    ok &= (tipo_finale && stato_finale && el->tipo_finale() == tipo_finale && el->stato_finale() == *stato_finale);
    if (ok && (!interattivo || interattivo == el->interattivo()) &&
        (!insert_mode || insert_mode == el->insert_mode()))
      result.add(el->codice());
  }
  return result.items();
} 
  
TElaborazione & TLista_elaborazioni::operator [](const char * key) const
{
  ((TLista_elaborazioni *)this)->read();
  return (TElaborazione &) (*_elab)[key];
}

void TLista_elaborazioni::update()
{
  delete _elab; _elab = NULL;
  read();
  
}  

TLista_elaborazioni::~TLista_elaborazioni()
{              
  if (_elab)
    delete _elab;
}