#include <execp.h>
#include <progind.h>
#include <tabutil.h>

#include "velib04.h"

///////////////////////////////////////////////////////////
// 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_date(DOC_DATADOC);
}

bool TLista_documenti::find(char provv, int anno, const char * codnum, long ndoc) const
{
	bool found = false;

	if (ndoc > 0)
	{
		const int it = items();
		for (int i = 0; !found && i < it; i++)
		{
			const TDocumento & d = doc(i);

			found	=	((d.head().get_long(DOC_NDOC) == ndoc) &&
			         (d.head().get_char(DOC_PROVV) == provv) &&
							 (d.head().get_int(DOC_ANNO) == anno) &&
							 (d.head().get(DOC_CODNUM) == codnum));
		}
	}
	return found;
}

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);
  int anno_start, anno_stop;

  start.put(DOC_TIPOCF, tipocf);
  stop.put(DOC_TIPOCF, tipocf);
  
  start.put(DOC_CODCF, clifo);
  stop.put(DOC_CODCF, clifo);
  
  start.put(DOC_PROVV, provv);
  stop.put(DOC_PROVV, provv);

  anno_start = anno_stop = anno;
  
  if (dd.ok())
  {
    anno_start = dd.year();
    start.put(DOC_ANNO, anno_start);
  }
  
  if (ad.ok())
  {
    anno_stop = ad.year();
    stop.put(DOC_ANNO, anno_stop);
  }
  
  if (dn > 0)  
  {
    const TDate d(num2date(provv, anno_start, codnum, dn));

    start.put(DOC_DATADOC, d);
    start.put(DOC_ANNO, d.year());
    start.put(DOC_NDOC, dn);
  }
  else
  {
    if (dd.ok() && dd > botime)
      start.put(DOC_DATADOC, dd);

    if (anno_start <= anno_stop)
      start.put(DOC_ANNO, anno_start);
  }
  
  if (an > 0)  
  {
    const TDate d(num2date(provv, anno_stop, codnum, an));
    stop.put(DOC_DATADOC, d);
    stop.put(DOC_ANNO, d.year());
    stop.put(DOC_NDOC, an);
  }
  else
  {
    if (ad.ok() && ad < eotime)
      stop.put(DOC_DATADOC, ad);

    stop.put(DOC_ANNO, anno_stop);
  }
  
  TString filter(16);
  if (codnum && *codnum)       
  {
    bool numfilter = FALSE;
    
    if (start.get(DOC_DATADOC).empty())
      numfilter = TRUE;
    else  
      start.put(DOC_CODNUM, codnum);

    if (stop.get(DOC_DATADOC).empty())
      numfilter = TRUE;
    else  
      stop.put(DOC_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 TString4 tipodoc  = head.get(DOC_TIPODOC);
    const TString4 statodoc = head.get(DOC_STATO);
    bool match = false;
    
    for (int i = tipidoc.items()-1; i>=0; i--)
    {
			const TString & tipo = tipidoc.get(i);

      if (tipo.blank() || tipodoc == tipo)
			{
				const TString & stato = statidoc.get(i);

        if (stato.blank() || statodoc == stato)
        {
          match = true;
          break;
        }
			}
    }
    
    if (match)
    {
      TDocumento* d = new TDocumento(head);
      _documenti.add(d);
    }  
  }
  
  return _documenti.items();
}                            

int TLista_documenti::write(bool re) const
{
  const int docs = _documenti.items();
  TString msg; 
  if (re)
    msg.format(FR("Aggiornamento di %d documenti"), docs);
  else
    msg.format(FR("Creazione di %d documenti"), docs);

  TProgind pi(docs, msg, true, true);
  int err = NOERR;                                      
  for (int i = 0; i < docs && err == NOERR; i++)
  {
    TDocumento& d = (TDocumento&)doc(i);
    err = d.write(re);
    d.flush_rows();
    if (!pi.addstatus(1))
      break;
  }
  
  return err;
}

///////////////////////////////////////////////////////////
// TLista_clifo
///////////////////////////////////////////////////////////

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, const char * dz, const char * 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 && (*dz !='\0'))
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << ">=\"" << dz << "\")";
  }  
  if (az && (*az !='\0'))
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << "<=\"" << az << "\")";
  }  
  
  TCursor cur(&clifo, "", 1, &start, &stop);
  if (filter.not_empty())
    cur.setfilter(filter, TRUE);
  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 && (*dz !='\0')) || (az && (*dz !='\0'))) 
        ordina_per_zona();
  
  return _clifo.items();
}

int TLista_clifo::leggi_ragsoc(const char *dr, const char * ar, long da, long aa, const char * dz, const char * 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 (dr && *dr) 
    start.put(CLI_RAGSOC, dr); 

  stop.put(CLI_TIPOCF, tipo()); 
  if (ar && *ar) 
    stop.put(CLI_RAGSOC, ar);
  
  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 && (*dz !='\0'))
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << ">=\"" << dz << "\")";
  }  
  if (az && (*az !='\0'))
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << "<=\"" << az << "\")";
  }  
  
  TCursor cur(&clifo, "", 2, &start, &stop);
  if (filter.not_empty())
    cur.setfilter(filter, TRUE);
  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);
  }
  
	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
{
	int i;
	
  for (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;
}

///////////////////////////////////////////////////////////
// TParametri_elaborazione
///////////////////////////////////////////////////////////

void TParametri_elaborazione::set(const char * name, const char * val)
{
  _par.add(name, new TString(val), TRUE);
}                          

const TString & TParametri_elaborazione::get(const char * name) const
{
  TObject *val = _par.objptr(name);

  if (val == NULL)
    return EMPTY_STRING;
  else
    return (const TString &) *val;
}                          

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

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

int TElaborazione::read(const char* cod)
{            
  int err = NOERR;

  TRectype::operator=(cache().get("%ELD", cod));
  if (empty())
  {
    yesnofatal_box("Codice elaborazione non valido: %s", cod);
    err = _iskeynotfound;
  }
  return err;  
}

	bool TElaborazione::is_document_ok(const TRectype & doc) const
	{
		bool ok = false;
		const TString4 codnum(doc.get(DOC_CODNUM));
		const TString4 tipodoc(doc.get(DOC_TIPODOC));
		const char stato(doc.get_char(DOC_STATO));
		const TString & codnumel = codice_numerazione_iniziale();

		if (codnumel.blank() || codnum == codnumel)
		{
		  for (int i = 0; !ok && i < TElaborazione::_max_tipi_doc_elab; i++)
			{                
				const TString & tipel = tipo_iniziale(i);
				if (tipel.blank())
					break;
				if (tipodoc == tipel && stato == stato_iniziale(i))
					ok = true;
			} 
		}
		return ok;
	}
///////////////////////////////////////////////////////////
// TElaborazione_esterna
///////////////////////////////////////////////////////////

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

bool TElaborazione_esterna::elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                       const TDate& data_elab, bool interattivo)
{                                            
  if (applicazione_esterna().blank())
    error_box("Non e' stato specificato il nome del'applicazione esterna");
  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");

  TDocumento& d = doc_in[0];
  const int doc_fields = d.items();         
  TString16 par; 

  {
    TConfig c(name, "Transaction");
    c.set("Action", codice());
		c.set("ProvvOut", doc_out[0].get(DOC_PROVV));
		c.set("AnnoOut", doc_out[0].get(DOC_ANNO));
		c.set("CodNumOut", doc_out[0].get(DOC_CODNUM));
		c.set("NDocOut", doc_out[0].get(DOC_NDOC));
    
    par.format("%d", LF_DOC);
    
    for (int i = 0; i < doc_fields; 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)
    {            
      const int row_fields = d[1].items();   
      
      for (int r = 1; r <= rows; r++)
      {
        TRiga_documento row = d[r];
        
        par.format("%d,%d", LF_RIGHEDOC, r);
        for (int i = 0; i < row_fields; 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, "Transaction");
		const TString & res = c.get("Result");
		if (res == "SUCCESS")
    {
      par.format("%d", LF_DOC);
      for (int i = 0; i < doc_fields; 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);
      d.destroy_rows();
    
      int r = 1 ;
      par.format("%d,%d", LF_RIGHEDOC, r);
      while (p.find(par) >= 0)
      {                                 
        const TString& tiporiga = c.get(RDOC_TIPORIGA, par);
        TRiga_documento& row = d.new_row(tiporiga);
        for (int i = row.items()-1; i >= 0; i--)
        {                      
          const TString16 fname(row.fieldname(i));
          TFieldref f(fname, LF_RIGHEDOC);
          const TString& val = f.read(c, par);
          if (val.not_empty())
            row.put(fname, val);
        }
        r++;           
        par.format("%d,%d", LF_RIGHEDOC, r);
      }
			d.stato(stato_finale_doc_iniziale()[0]);
      return true;
    }  
		else
			if (res == "OUTDOC")
			{
				doc_out[0].read();
				return true;
			}

  }
  return false;     
}

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

TCopia_documento::TCopia_documento(const char* cod) 
                : TElaborazione(cod), _preserve_original_rif(false)                    
{ }

bool TCopia_documento::elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                               const TDate& data_elab, bool /*interattivo*/)
{  
//  CHECK(doc_in.items() == doc_out.items(), "Si deve specificare un numero uguale di documenti in entrata ed in uscita");

	pre_process_input(doc_in);
  for (int d = 0; d < doc_in.items(); d++)
  {
    TDocumento& doc_src = doc_in[d];
		if (doc_out.items() <= d)
		{
			const TString4 codnum = codice_numerazione_finale();
			doc_out.add(new TDocumento('D', doc_src.get_int(DOC_ANNO), codnum, 0L));
		}
    TDocumento& doc_dest = doc_out[d]; 
     
    doc_dest.copy_contents(doc_src);
		const TString4 tipodoc = tipo_finale();
		doc_dest.put(DOC_TIPODOC, tipodoc);
		const TString4 stato = stato_finale();
		doc_dest.put(DOC_STATO, stato);

    // Scancello tutti gli inutili riferimenti al documento origine
    if (!_preserve_original_rif) 
			for (int r = doc_dest.physical_rows(); r > 0; r--)
				doc_dest[r].reset_original_rdoc_key();

    if (data_elab.ok())
      doc_dest.put(DOC_DATADOC, data_elab);
		doc_src.put(DOC_STATO, stato_finale_doc_iniziale());
  }
	post_process_input(doc_in);
  post_process_output(doc_out);

  return TRUE;     
}