#include <applicat.h>
#include <config.h>
#include <dongle.h>
#include <recarray.h>
#include <utility.h>

#include <comuni.h>
#include <pagsca.h>
#include <partite.h>
#include <scadenze.h>


#include "halib.h"
#include "ha1250.h"

#include <mov.h>


class THardy_config : public TConfig
{
public:
  virtual const TString& get(const char* var, const char* section = NULL, int index = -1, const char* def = "");
  THardy_config(const char* ini_name);
};

const TString& THardy_config::get(const char* var, const char* section, int index, const char* def)
{
  const TString& original_val = TConfig::get(var, section, index, def);
  if (original_val[0] == '"' && original_val.ends_with("\""))
  {
    TString& val = get_tmp_string();
    val = original_val;
    val.rtrim(1);
    val.ltrim(1);
    val.trim();
    return val;
  }
  return original_val;
}

THardy_config::THardy_config(const char* ini_name)
             : TConfig(ini_name, "Transaction")
{
}



/////////////////////////////////////////////////////////////
//	Applicazione
/////////////////////////////////////////////////////////////
class TIni2Txt: public TSkeleton_application
{
  TFilename _output_dir;

protected:
  //metodi di infimo livello
  TString4 ricava_segno(TConfig& ini);

  //metodi di medio livello
  void crea_nome_txt(const TString& prefisso, TFilename& output_path);
  void get_part_key(TConfig& ini, TToken_string& key) const;
  bool find_part_key(TConfig& ini, const TString_array& ini_paragraphs, 
                     const int logicnum, const TToken_string& key) const;
  void fill_anagrafica_cliente(TConfig& ini, TEsporta_clienti_recordset& clienti, 
                               const bool is_fatt);

  //metodi di alto livello
  void genera_cliente_var_txt(TConfig& ini);
  void genera_riga_listino_var_txt(TConfig& ini);
  void genera_prodotto_var_txt(TConfig& ini);
  void genera_sospeso_var_txt(TConfig& ini, TString_array& ini_paragraphs);

  virtual void main_loop();

public:
  TIni2Txt();
};

TIni2Txt::TIni2Txt()
{
  if (user().blank())
    user() = dongle().administrator();
}


//metodo che genera in automatico il nome del file .txt in output (maggggico)
void TIni2Txt::crea_nome_txt(const TString& prefisso, TFilename& output_path)
{
  TString_array lista_files;
  output_path = _output_dir;

  TString wrkstring;
  wrkstring << prefisso << "??????.txt";
  output_path.add(wrkstring);

  const int items = list_files(output_path, lista_files);
  long n = 1;
  if (items > 0)
  {
    lista_files.sort();
    TFilename ultimo_txt = lista_files.row(items - 1);
    ultimo_txt = ultimo_txt.name();
    const long last_txt = atol(ultimo_txt.mid(prefisso.len(), 6));
    n += last_txt;
  }

  //risfruttiamo lo stesso filename che risparmiamo
  output_path = _output_dir;
  output_path.add(prefisso);
  TString8 numero;
  numero.format("%06ld", n);
  output_path << numero;
  output_path.ext("txt");
}


TString4 TIni2Txt::ricava_segno(TConfig& ini)
{
  TString4 segno = "+";
  const TString& action = ini.get("Action", "Transaction");
  if (action == "R")
    segno = "-";

  return segno;
}


void TIni2Txt::fill_anagrafica_cliente(TConfig& ini, TEsporta_clienti_recordset& clienti, 
                                       const bool is_fatt)
{
  clienti.set_fatt("RagioneSociale", ini.get(CLI_RAGSOC), is_fatt);
  //l'indirizzo va numerato
  TString80 indcf = ini.get(CLI_INDCF);
  indcf << " " << ini.get(CLI_CIVCF);
  clienti.set_fatt("Indirizzo", indcf, is_fatt);
  const TString80 localitacf = ini.get(CLI_LOCCF);
  clienti.set_fatt("Localita", localitacf, is_fatt);
  //panegirico per comune e provincia
  const TString& statocf = ini.get(CLI_STATOCF);
  if (statocf.blank())
  {
    const TString& comcf = ini.get(CLI_COMCF);
    TToken_string key;
    key.add(statocf);
    key.add(comcf);
    const TRectype& rec_comuni = cache().get(LF_COMUNI, key);
    //se la localit� � vuota ci mettiamo la descrizione del comune
    if (localitacf.blank())
    {
      TString80 dencom = rec_comuni.get(COM_DENCOM);
      clienti.set_fatt("Localita", dencom, is_fatt);
    }
    const TString& provcf = rec_comuni.get(COM_PROVCOM);
    clienti.set_fatt("Provincia", provcf, is_fatt);
  }
  
  clienti.set_fatt("CAP", ini.get(CLI_CAPCF), is_fatt);

  clienti.set_fatt("PartitaIVA", ini.get(CLI_PAIV), is_fatt);

  clienti.set_fatt("CodiceFiscale", ini.get(CLI_COFI), is_fatt);
}

///////////////////////////////////////////////
//  metodi di alto livello
///////////////////////////////////////////////
void TIni2Txt::genera_cliente_var_txt(TConfig& ini)
{
  //recordset per i clienti
  TEsporta_clientiVAR_recordset clienti;
  clienti.new_rec("");

  //controlla il tipo di transazione...
  const TString4 segno = ricava_segno(ini);

  //...e poi si lancia a capofitto a riempire il recordset!
  //campi da clifo (20)
  //-------------------
  ini.set_paragraph("20");

  const long codcf = ini.get_long(CLI_CODCF);
  //il codice terminale � il codagente legato al cliente
  clienti.set("CodiceTerminale", hd_find_codag(codcf));

  clienti.set("Segno", segno);
  clienti.set("CodiceCliente", codcf);

  //riempie tutti i campi dell'anagrafica cliente
  fill_anagrafica_cliente(ini, clienti, false);
  //e poi, se serve, i campi del cliente in allegato
  const long codalleg = ini.get_long(CLI_CODALLEG);
  if (codalleg > 0)
    fill_anagrafica_cliente(ini, clienti, true);


  //attenzione alla lunghezza del codpag
  TString4 codpag = ini.get(CLI_CODPAG);
  clienti.set("CodicePagamento", codpag.right(2)); //il campo sul .txt � lungo 2 e vuole i caratteri a destra!

  TString query;
  query << "USE CONDV";
  query << "\nFROM TIPO=C TIPOCF=C CODCF=#CODCF";
  query << "\nTO TIPO=C TIPOCF=C CODCF=#CODCF";

  TISAM_recordset contratti(query);
  contratti.set_var("#CODCF", ini.get_long(CLI_CODCF));
  if (contratti.move_last())
  {
    const TString& cod_contr = contratti.get(CONDV_COD).as_string();
    clienti.set("CodiceListino", cod_contr);
  }

  TString16 ntel = ini.get(CLI_PTEL);
  ntel << ini.get(CLI_TEL);
  clienti.set("NumeroTelefono", ntel);

  clienti.set("TipoDoc", "F");

  //panegirico del fido!
  //poich� il formato di output prevede comunque 2 decimali -> provvediamo a riempire eventuali buchi
  real fido = ini.get(CLI_FIDO);
  fido *= CENTO;
  fido.round();
  clienti.set("Fido", fido);

  //no consegna = sospeso (forse, anzi NO! non ci va)
  //const TString& sospeso = ini.get_bool(CLI_SOSPESO) ? "S" : "";
  //clienti.set("NoConsegna", sospeso);


  //campi da cfven(17)
  //------------------
  ini.set_paragraph("17");
  const TString& str_sconto = ini.get(CFV_SCONTO);
  clienti.set("ScontoFineFattura", hd_find_sconto(str_sconto));

  
  TString4 assfis = ini.get(CFV_ASSFIS);
  clienti.set("EsenteIVA", assfis);

  //prepara il nome corretto del file .txt e lo genera
  //--------------------------------------------------
  const TString prefisso = "clientivar";
  TFilename output_path;
  crea_nome_txt(prefisso, output_path);

  //..e alla fine della fiera salva il file di testo nell directory selezionata
  clienti.save_as(output_path, fmt_text);
}


void TIni2Txt::genera_riga_listino_var_txt(TConfig& ini)
{
  TEsporta_listiniVAR_recordset riga_listino;

  const TString4 segno = ricava_segno(ini);

  TISAM_recordset agenti("USE AGENTI");
  const long agenti_items = agenti.items();
  for (bool ok = agenti.move_first(); ok; ok = agenti.move_next())
  {
    TString8 codage = agenti.get(AGE_CODAGE).as_string();
    codage = codage.right(3);

    //campi da condv (52)
    //-------------------
    //ini.set_paragraph("52");

    //campi da rcondv (53)
    //--------------------
    //ATTENZIONE!!! Ci sono N righe di tipo 53 per ogni testata, quindi dobbiamo fare il giro
    for (int r = 1;; r++)
    {
      TString8 paragraph;
      paragraph.format("%d,%d", LF_RCONDV, r);
      if (!ini.set_paragraph(paragraph))
        break;

      riga_listino.new_rec("");

      riga_listino.set("CodiceTerminale", codage);
      riga_listino.set("Segno", segno);

      riga_listino.set(RCONDV_COD, ini.get(RCONDV_COD));
      TString80 codart = ini.get(RCONDV_CODRIGA);
      riga_listino.set(RCONDV_CODRIGA, codart);

      //prezzo
      real prezzo = ini.get(RCONDV_PREZZO);
      if (prezzo.is_zero())
      {
        TToken_string key_umart;
        key_umart.add(codart);
        key_umart.add(1);
        const TRectype& rec_umart = cache().get(LF_UMART, key_umart);
        prezzo = rec_umart.get_real(UMART_PREZZO);
      }
      prezzo *= 1000;
      prezzo.round();
      riga_listino.set(RCONDV_PREZZO, prezzo.integer());

      //sconto
      const TString& str_sconto = ini.get(RCONDV_SCONTO);
      riga_listino.set(RCONDV_SCONTO, hd_find_sconto(str_sconto));

    } //for(int r=1;;...

  }
  //prepara il nome corretto del file .txt e lo genera
  //--------------------------------------------------
  const TString prefisso = "listvar";
  TFilename output_path;
  crea_nome_txt(prefisso, output_path);

  riga_listino.save_as(output_path, fmt_text); 
}


void TIni2Txt::genera_prodotto_var_txt(TConfig& ini)
{
  TEsporta_prodottiVAR_recordset prodotto;
  prodotto.new_rec("");

  const TString4 segno = ricava_segno(ini);

  //campi da anamag (47)
  //-------------------
  ini.set_paragraph("47");

  prodotto.set("Segno", segno);

  TString80 codart = ini.get(ANAMAG_CODART);
  prodotto.set(ANAMAG_CODART, codart);

  TString80 descr = ini.get(ANAMAG_DESCR);
  prodotto.set(ANAMAG_DESCR, descr);

  const TString& codiva = ini.get(ANAMAG_CODIVA);
  //esportiamo un'aliquota, non un codice iva!
  const real aliq_iva = cache().get("%IVA", codiva, "R0");
  prodotto.set(ANAMAG_CODIVA, aliq_iva.integer());

  //sconto
  const TString& str_sconto = ini.get(ANAMAG_SCONTO);
  prodotto.set(ANAMAG_SCONTO, hd_find_sconto(str_sconto));

  //campi da umart (49) (per ora una sola riga)
  //-------------------------------------------
  ini.set_paragraph("49,1");

  real prezzo = ini.get(UMART_PREZZO);
  prezzo *= 1000;
  prezzo.round();
  prodotto.set(UMART_PREZZO, prezzo.integer());

  prodotto.set(UMART_UM, ini.get(UMART_UM));


  //prepara il nome corretto del file .txt da generare
  //--------------------------------------------------
  const TString prefisso = "prodottivar";
  TFilename output_path;
  crea_nome_txt(prefisso, output_path);

  prodotto.save_as(output_path, fmt_text);
}


void TIni2Txt::get_part_key(TConfig& ini, TToken_string& key) const
{
  //tracciato: tipocf-sottoconto(=codcf)-anno-numpart-nriga
  key.cut(0);
  key.add(ini.get(PART_TIPOCF));
  key.add(ini.get(PART_SOTTOCONTO));
  key.add(ini.get_int(PART_ANNO));
  key.add(ini.get(PART_NUMPART));
  key.add(ini.get_int(PART_NRIGA));
}


bool TIni2Txt::find_part_key(TConfig& ini, const TString_array& ini_paragraphs, 
                             const int logicnum, const TToken_string& key) const
{
  TString4 start;
  start << logicnum << ',';

  //cerca i pagamenti della partita
  FOR_EACH_ARRAY_ROW(ini_paragraphs, t, row)
  {
    if (row->starts_with(start))
    {
      TToken_string key_row;
      ini.set_paragraph(*row);
      get_part_key(ini, key_row);
      if (key_row == key)
        return true;
    } //if (part_row->starts_with("30...
  } //FOR_EACH_ARRAY_ROW(ini_paragraphs, t...
  return false;
}

void TIni2Txt::genera_sospeso_var_txt(TConfig& ini, TString_array& ini_paragraphs)
{
  TEsporta_sospesiVAR_recordset sospeso;
  sospeso.new_rec("");

  const TString4 segno = ricava_segno(ini);

  //delirio di campi da part (28) pagsca (29) scad(30)
  //--------------------------------------------------
  //ricava il numreg dal mov
  const long numreg = ini.get_long(MOV_NUMREG, "23");
  TAssoc_array clienti;

  //costruisce la lista dei clienti presenti nel .ini
  FOR_EACH_ARRAY_ROW(ini_paragraphs, r, riga)
  {
    if (riga->starts_with("28,"))
    {
      const long part_numreg = ini.get_long(PART_NREG, *riga);  //va specificata il paragrafo alla prima get
      const char tipocf = ini.get_char(PART_TIPOCF);
      if (numreg == part_numreg && tipocf == 'C')
        clienti.add(ini.get(PART_SOTTOCONTO));
    }
  }

  FOR_EACH_ARRAY_ROW(ini_paragraphs, s, part_row)
  {
    if (part_row->starts_with("28,"))
    {
      const int tipomov = ini.get_int(PART_TIPOMOV, *part_row);
      const char tipocf = ini.get_char(PART_TIPOCF);
      const TString8 str_codcf = ini.get(PART_SOTTOCONTO);
      const bool part_chiusa = ini.get_bool(PART_CHIUSA);
      //cerca le partite, derivanti da fatture, dei soli clienti che ha nella lista clienti
      if (!part_chiusa && tipomov == 1 && tipocf == 'C' && clienti.is_key(str_codcf))
      {
        TToken_string key;
        get_part_key(ini, key);
        
        const long numfatt = ini.get_long(PART_NUMDOC);
        const TDate datadoc = ini.get(PART_DATADOC);
        const TString8 numpart = key.get(3);

        //cerca i pagamenti della rata
        if (find_part_key(ini, ini_paragraphs, LF_SCADENZE, key))
        {
          real imp_rata = ini.get(SCAD_IMPORTO);
          const TDate data_rata = ini.get(SCAD_DATASCAD);
          real imp_pag;
          if (find_part_key(ini, ini_paragraphs, LF_PAGSCA, key))
          {
            const char acc_sal = ini.get(PAGSCA_ACCSAL)[0];
            imp_pag = acc_sal == 'S' ? imp_rata : real(ini.get(PAGSCA_IMPORTO));
          }
          //se il pagamento non chiude la rata -> il saldo resta in sospeso e il record va aggiunto
          if (imp_pag < imp_rata)
          {
            real residuo = imp_rata - imp_pag;

            //riempie il record da esportare
            const long codcf = ini.get_long(PART_SOTTOCONTO);
            const TString& codage = hd_find_codag(codcf);
            sospeso.set("CodiceTerminale", codage);
            sospeso.set("Segno", segno);
            sospeso.set("CodiceCliente", codcf);
            sospeso.set("NumeroFattura", numfatt);
            sospeso.set("DataFattura", hd_format_date6(datadoc));
            residuo *= CENTO;
            residuo.round();
            sospeso.set("ImportoResiduo", residuo);
            imp_rata.round();
            sospeso.set("ImpOriginalDoc", imp_rata);
            sospeso.set("DataScadenza", hd_format_date6(data_rata));
            sospeso.set("TipoDocumento", "F");
          }
        } //if (find_part_key(ini, ini_paragraphs, LF_SCADENZE...
      } //if (tipomov == 1 && tipocf...
    } //if (part_row->starts_with("28,...
  } //FOR_EACH_ARRAY_ROW(ini_paragraphs, s...

  //prepara il nome corretto del file .txt da generare
  //--------------------------------------------------
  const TString prefisso = "sospesivar";
  TFilename output_path;
  crea_nome_txt(prefisso, output_path);

  sospeso.save_as(output_path, fmt_text);

}


void TIni2Txt::main_loop()
{
  //stabilisce una volta per tutte ad inizio programma quale cavolo � la directory dove sbattere i .txt generati
  TConfig hardy(CONFIG_DITTA, "ha");
  _output_dir = hardy.get("OutputPath");

  //dalla riga di comando raccatta il path completo del file .ini da tradurre
  TFilename path = argv(2);
  if (path.exist())
  {
    //crea un config del .ini di tipo Hardy_config con le get astute!
    THardy_config input_ini_file(path);

    //elabora il .ini
    TString_array ini_paragraphs;
    input_ini_file.list_paragraphs(ini_paragraphs);

    //clienti
    if (ini_paragraphs.find("20") >= 0)
      genera_cliente_var_txt(input_ini_file);

    //sospesi
    if (ini_paragraphs.find("28,1,1") >= 0)
      genera_sospeso_var_txt(input_ini_file, ini_paragraphs);

    //listino
    if (ini_paragraphs.find("52") >= 0)
      genera_riga_listino_var_txt(input_ini_file);

    //prodotti
    if (ini_paragraphs.find("47") >= 0)
      genera_prodotto_var_txt(input_ini_file);
  }
}


int ha1100(int argc, char* argv[])
{
  TIni2Txt a;
  a.run(argc, argv, "Generazione .txt da .ini");
  return 0;
}