#include <automask.h>
#include <applicat.h>
#include <progind.h>
#include <reputils.h>
#include <textset.h>
#include <utility.h>

#include "../cg/cglib01.h"
#include "ps0430400a.h"

#include "../ve/velib.h"

#include "../cg/cfban.h"
#include "cfven.h"

///////////////////////////////////////////////////////////
// TCBA_recset
///////////////////////////////////////////////////////////

class TCBA_recset : public TCSV_recordset
{
public:
  virtual const TVariant& get(unsigned int column) const;
  TDate get_date(unsigned int column) const;
  TCBA_recset() : TCSV_recordset("CSV(;)") {}
};

const TVariant& TCBA_recset::get(unsigned int column) const
{
  const TVariant& var = TCSV_recordset::get(column);
  if (var.is_string())
  {
    const TString& str = var.as_string();
    if (str[0] == '"')
      ((TString&)str).strip("\"");
  }
  return var;
}

TDate TCBA_recset::get_date(unsigned int column) const
{
  const TVariant& var = TCSV_recordset::get(column);
  const long v = var.as_int();
  const int d = v / 1000000;
  const int m = (v / 10000) % 100;
  const int y = v % 10000;
  return TDate(d, m, y);
}


///////////////////////////////////////////////////////////
// TImport_mask
///////////////////////////////////////////////////////////

class TImport_mask : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);

public:
  TImport_mask() : TAutomask("ps0430400a") {}
};

bool TImport_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_CLI:
  case F_FAT:
    if (e == fe_button)
    {
      TFilename n = get(F_DIR);
      n.add("*.csv");
      if (input_filename(n))
        o.set(n.name());
    } else
    if (e == fe_close)
    {
      TFilename n = get(F_DIR);
      n.add(o.get());
      if (!n.exist())
        return cantread_box(n);
    }
    break;
  default: break;
  }
  return true;
}

///////////////////////////////////////////////////////////
// TImport_app
///////////////////////////////////////////////////////////

class TImport_app : public TSkeleton_application
{
protected:
  virtual const char* extra_modules() const { return "cm|ve"; }
  virtual void main_loop();

public:
  void cli_ragsoc(const TCBA_recset& cli, TString& ragsoc) const;
  long find_customer(const TString& cf_pi, const TString& codpag, const TString& ragsoc) const;
  const TRectype& find_codart(TString& codart) const;
  bool find_or_create_clifo(const TCBA_recset& cli, TLocalisamfile& clifo, 
                            const TString& cdc, TLog_report& log) const;
  const TString& get_periodo(const TCBA_recset& fat) const;
  void aggiorna_referente(long codcf, const char* ospite) const;

  bool importa_clienti(const TFilename& fname, const TString& cdc, TLog_report& log) const;
  bool importa_fatture(const TFilename& fname, const TString& codnum, const TString& cdc, TLog_report& log) const;
};

static void normalize_code(TString& code)
{
  code.trim(); 
  if (code.not_empty())
  {
    code.upper();
    if (code.len() < 11 && real::is_natural(code))
      code.right_just(11, '0');
  }
}

long TImport_app::find_customer(const TString& cf_pi, const TString& codpag, const TString& ragsoc) const
{
  long codice = 0L;
  if (cf_pi.blank() || cf_pi.len() < 9)
    return codice;

  TString20 cod = cf_pi;
  normalize_code(cod);
  const bool numeric = cod.len() < 16 && real::is_natural(cod);

  // Tento prima il codice fiscale o la partita IVA?
  const char* const cofi_paiv = numeric ? "PC" : "CP";
  for (int t = 0; t < 2 && codice <= 0; t++)
  {
    const int key = cofi_paiv[t] == 'C' ? 4 : 5;
    TString query;
    query << "USE " << LF_CLIFO << " KEY " << key;
    for (int f = 0; f < 2; f++)
      query << (f ? "\nTO " : "\nFROM ") << CLI_TIPOCF << "=C " << (key==4 ? CLI_COFI : CLI_PAIV) << '=' << cod;

    TISAM_recordset recset(query);
    if (recset.move_first())
    {
      codice = recset.get(CLI_CODCF).as_int();
  
      const TRecnotype candidates = recset.items();
      if (candidates > 1) // Cliente ambiguo
      {
        if (ragsoc.full()) // Ricerca da clifo
        {
          codice = 0;
          for (TRecnotype i = 0; recset.move_to(i); i++)
          {
            TString80 kr; recset.get(CLI_RAGSOC).as_string(kr);
            if (ragsoc == kr)
            {
              codice = recset.get(CLI_CODCF).as_int();
              break;
            }
          }
        } else
        if (codpag.full()) // Ricerca da fatt
        {
          TRecnotype best = -1;       
          for (TRecnotype i = 0; recset.move_to(i); i++)
          {
            TString4 kp; recset.get(CLI_CODPAG).as_string(kp);
            if (codpag == kp || (kp.blank() && best < 0))
            {
              best = i;
              if (codpag == kp)
                break;
            }
          }
          if (best > 0)
          {
            recset.move_to(best);
            codice = recset.get(CLI_CODCF).as_int();
          }
        }
      }
    }
  }

  return codice;
}

const TRectype& TImport_app::find_codart(TString& cod) const
{
  cod.trim(); cod.upper();
  const TRectype& anamag = cache().get(LF_ANAMAG, cod);
  return anamag;
}

void TImport_app::cli_ragsoc(const TCBA_recset& cli, TString& ragsoc) const
{
  TString80 rs1, rs2;
  cli.get(6).as_string(rs1); rs1.trim();
  cli.get(7).as_string(rs2); rs2.trim();

  TString80 str;
  if (rs2.blank())
  {
    const int spc = rs1.find(' ');
    if (spc > 0)
    {
      str = rs1.left(spc);
      str.left_just(30);
      str << rs1.mid(spc+1);
    }
  }   
  if (str.blank())
  {
    str << rs1 << ' ' << rs2;
    str.strip_double_spaces();
  }
  TParagraph_string ps(str, 50);
  ragsoc = ps.get();
 }

bool TImport_app::find_or_create_clifo(const TCBA_recset& cli, TLocalisamfile& clifo, const TString& cdc, TLog_report& log) const
{
  TString16 piva;   cli.get(1).as_string(piva);
  TString20 cofi;   cli.get(2).as_string(cofi);
  TString80 ragsoc; cli_ragsoc(cli, ragsoc);
  TString4 codpag;  cli.get(20).as_string(codpag);

  if (piva.blank() && cofi.blank())
  {
    TString msg; 
    msg << ragsoc << TR(" non ha n� codice fiscale n� partita IVA")
        << TR(" alla riga ") << (cli.current_row()+1);
    log.log(2, msg);
    return false;
  }

  long codcf = 0;
  if (cofi.full())
    codcf = find_customer(cofi, codpag, ragsoc);
  if (codcf <= 0 && piva.full())
    codcf = find_customer(piva, codpag, ragsoc);
  if (codcf > 0)
  {
    clifo.setkey(1);
    clifo.zero();
    clifo.put(CLI_TIPOCF, 'C');
    clifo.put(CLI_CODCF, codcf);
    return clifo.read() == NOERR;
  }

  long next_clifo = 1;
  TISAM_recordset recset("USE CLIFO\tTO TIPOCF=C");
  if (recset.move_last())
    next_clifo += recset.get(CLI_CODCF).as_int();

  clifo.setkey(1);
  clifo.zero();
  clifo.put(CLI_TIPOCF, 'C');
  clifo.put(CLI_CODCF, next_clifo);
  clifo.put(CLI_RAGSOC, ragsoc); 

  normalize_code(cofi);
  clifo.put(CLI_COFI, cofi);

  normalize_code(piva);
  clifo.put(CLI_PAIV, piva); 

  const int err = clifo.write();
  if (err != NOERR)
  {
    TString msg; 
    msg << TR("Impossibile creare l'anagrafica di ") << ragsoc << TR(" alla riga ") << (cli.current_row()+1);
    log.log(2, msg);
  }

  return err == 0;
}

bool TImport_app::importa_clienti(const TFilename& fname, const TString& cdc, TLog_report& log) const
{
  TString str = TR("Importazione clienti");
  log.log(0, str);
  
  TLocalisamfile clifo(LF_CLIFO);
  TLocalisamfile cfven(LF_CFVEN);
    
  TCBA_recset cli;
  cli.load_file(fname);

  long clifos = 0;

  bool done = true;
  
  TProgress_monitor pi(cli.items(), str);
  for (bool ok = cli.move_first(); ok && done; ok = cli.move_next())
  {
    if (cli.get(0).is_empty()) // Salta righe vuote
      continue;
    
    done = find_or_create_clifo(cli, clifo, cdc, log);
    if (!done)
      break;

    // Aggiorna dati cliente
    TString16 piva; cli.get(1).as_string(piva);
    normalize_code(piva);

    TString20 cofi; cli.get(2).as_string(cofi); 
    normalize_code(cofi);
    
    const int privato = cli.get(27).as_int();

    TRectype& curr = clifo.curr();

    curr.put(CLI_PAIV, piva);
    curr.put(CLI_COFI, cofi);
    if (cofi.len() == 16)
    {
      curr.put(CLI_SESSO, cofi[9] >  '3' ? 'F': 'M');
      curr.put(CLI_COMNASC, cofi.mid(11, 4));
    }
       
    TString80 ragsoc; cli_ragsoc(cli, ragsoc);
    curr.put(CLI_RAGSOC, ragsoc);
    curr.put(CLI_REFERENTE, curr.get(CLI_RAGSOC));

    const TString& indir = cli.get(8).as_string();
    curr.put(CLI_INDCF, indir);
    if (isdigit(indir.right(1)[0]))
      curr.zero(CLI_CIVCF);

    const TString80 loc = cli.get(10).as_string();
    curr.put(CLI_LOCCF, loc);

    const TString8 cap = cli.get(9).as_string();
    curr.put(CLI_CAPCF, cap);

    const int naz = cli.get(16).as_int();
    if (naz == 0) 
    {
      const TString& com = cap2comune(cap, loc);
      curr.put(CLI_COMCF, com);
    }

    int alleg = 0;
    if (privato == 1)
      alleg = 6; 
    else
    {
      if (naz > 0)
        alleg = naz == 1 ? 5 : 9;
      else
      {
        if (piva.blank())
          alleg = real::is_natural(cofi) ? 7 : 6;
      }
    }
    curr.put(CLI_ALLEG, alleg);

    const TString8 abi = cli.get(17).as_string();
    const TString8 cab = cli.get(18).as_string();
    const TString80 numcc = cli.get(23).as_string();
    if (abi.len() == 5 && cab.len() == 5)
    {
      TToken_string key;
      key = "C";
      key.add(curr.get(CLI_CODCF));
      key.add("N");
      TRecord_array nsban(key, LF_CFBAN);
      TRectype& rec = nsban.row(1, true);
      rec.put(CFBAN_ABI, abi);
      rec.put(CFBAN_CAB, cab);
      rec.put(CFBAN_NUMCC, numcc);
        
      key.cut(0) << abi << cab;
      const TString& iban = cache().get("BNP", key, "S3");
      rec.put(CFBAN_IBAN, iban);

      const int err = nsban.rewrite();
      if (err != NOERR)
      {
        ragsoc.strip_double_spaces();
        str.cut(0) << TR("Impossibile aggiornare la banca di ") << ragsoc << TR(" alla riga ") << (cli.current_row()+1);
        log.log(1, str);
      }

      // Copialo anche sul cliente per tradizione
      curr.put(CLI_CODABI, abi);
      curr.put(CLI_CODCAB, cab);
      curr.put(CLI_NUMCC, numcc);
    }

    curr.put(CLI_CODPAG, cli.get(20).as_string());
    curr.put(CLI_MAIL,   cli.get(28).as_string());

    const int err = clifo.rewrite();
    if (err == NOERR)
    {
      clifos++;
    }
    else
    {
      ragsoc.strip_double_spaces();
      str.cut(0) << TR("Impossibile aggiornare l'anagrafica di ") << ragsoc << TR(" alla riga ") << (cli.current_row()+1);
      log.log(2, str);
      break;
    }

    if (cdc.full())
    {
      cfven.setkey(1);
      cfven.zero();
      cfven.put(CFV_TIPOCF, 'C');
      cfven.put(CFV_CODCF, curr.get(CLI_CODCF));
      if (cfven.read() != NOERR)
      {
        cfven.zero();
        cfven.put(CFV_TIPOCF, 'C');
        cfven.put(CFV_CODCF, curr.get(CLI_CODCF));
        cfven.write();
      }
      cfven.put(CFV_CODPRCF, cdc.right(3));
      cfven.rewrite();
    }

    done = pi.add_status();
  }

  log.log(0, str.cut(0));
  str << TR("Sono state importate/aggiornate ") << clifos << TR(" anagrafiche");
  log.log(0, str);
  log.log(0, str.cut(0));

  return done;
}

void TImport_app::aggiorna_referente(long codcf, const char* ospite) const
{
  TLocalisamfile clifo(LF_CLIFO);
  clifo.put(CLI_TIPOCF, 'C');
  clifo.put(CLI_CODCF, codcf);
  if (clifo.read(_isequal, _lock) == NOERR)
  {
    clifo.put(CLI_REFERENTE, ospite);
    clifo.rewrite();
  }
}

const TString& TImport_app::get_periodo(const TCBA_recset& fat) const
{
  TString80 periodo;
  fat.get(10).as_string(periodo);

  if (periodo[0] >= '0' && periodo[0] <= '9' && periodo.len() > 4)
  {
    const int mese = atoi(periodo.left(periodo.len()-4));
    if (mese >= 1 && mese < 12)
    {
      int anno = atoi(periodo.right(4));
      periodo = itom(mese);
      if (anno < 2000)
        anno = fat.get(9).as_int();
      periodo << ' ' << anno;
    }
  }

  return get_tmp_string() = periodo;
}

static bool save_doc(TDocumento& doc, long& ndocs, long& mindoc, long& maxdoc, TLog_report& log)
{
  const int err = doc.write();
  if (err == NOERR)
  {
    ndocs++;
    maxdoc = doc.get_long(DOC_NDOC);
    if (ndocs == 1)
      mindoc = maxdoc;
  }
  else
  {
    TString str;
    str.format("Impossibile registrare il documento %s/%ld", 
                (const char*)doc.get(DOC_CODNUM), maxdoc+1);
    log.log(2, str);
  }
  return err == 0;
}

static void save_rdoc_desc(const TString& desc, TRiga_documento& rdoc)
{
  if (desc.len() > 50)
  {
    TParagraph_string p(desc, 50);
    rdoc.put(RDOC_DESCR, p.get());
    const int sep = p.find(p.separator());
    if (sep > 0)
    {
      rdoc.put(RDOC_DESCLUNGA, "X");
      TString tmp = p.mid(sep+1);
      tmp.replace(p.separator(), ' ');
      tmp.strip_double_spaces();
      tmp.insert("\n");
      rdoc.put(RDOC_DESCEST, tmp);
    }
  }
  else
    rdoc.put(RDOC_DESCR, desc);
}

bool TImport_app::importa_fatture(const TFilename& fname, const TString& codnum, const TString& cdc, TLog_report& log) const
{
  TString str = TR("Importazione documenti");
  log.log(0, str);

  TCBA_recset fat;
  fat.load_file(fname);

  long ndocs = 0;
  long mindoc = 0;
  long maxdoc = 0;

  bool done = true;

  long codcf = 0;
  TString80 cofi_paiv;
  TString4 codpag;
  TString periodo;
  TToken_string ospiti(256, '@');

  TDocumento* doc = NULL;

  TProgress_monitor pi(fat.items(), str);
  for (bool ok = fat.move_first(); ok && done; ok = fat.move_next())
  {
    const char tipo = fat.get(8).as_string()[0];

    if (tipo == 'T')  
    {
      if (doc != NULL)
      {
        save_doc(*doc, ndocs, mindoc, maxdoc, log);
        delete doc;
        doc = NULL;
      }

      fat.get(11).as_string(codpag);
      fat.get(14).as_string(cofi_paiv);
      normalize_code(cofi_paiv);
      codcf = find_customer(cofi_paiv, codpag, EMPTY_STRING);
      if (codcf <= 0)
      {
        str.format("Impossibile associare un cliente al codice '%s' alla riga %ld", 
                   (const char*)cofi_paiv, fat.current_row()+1);
        log.log(2, str);
      }
      periodo = get_periodo(fat); // 10 + 9

      fat.get(15).as_string(ospiti);
      const char* ospite = ospiti.get(0);
      if (codcf > 0 && ospite && *ospite > ' ')
        aggiorna_referente(codcf, ospite);

      continue;
    }

    if (tipo != 'N' || codcf <= 0)
      continue; // Considera solo le righe articolo di clienti validi

    const TDate datadoc = fat.get_date(5);
    const long ndoc = fat.get(6).as_int();
    if (!datadoc.ok() || ndoc <= 0)
    {
      str.format("Data o numero documento nulli alla riga %ld", fat.current_row()+1);
      log.log(2, str);
      continue;
    }

    TCodice_articolo codart;
    codart << fat.get(12); codart.trim();
    codart << '.' << cdc.right(3);
    
    const TRectype& anamag = find_codart(codart);
    if (anamag.empty())
    {
      str.format("Articolo non valido '%s' alla riga %ld", (const char*)codart, fat.current_row()+1);
      log.log(1, str);
      continue;
    }

    str = codart; str << "|1";
    const TString4 um = cache().get(LF_UMART, str, UMART_UM);

    if (doc == NULL)
    {
      TString4 numerazione = codnum;
      TToken_string key;

      key << "C|" << codcf;
      TString4 tipo_doc = cache().get(LF_CFVEN, key, CFV_TIPODOCFAT);
      if (tipo_doc.full())
      {
        TString_array num_doc;
        const int nd = numerazioni_documenti(num_doc, tipo_doc);
        if (nd > 0 && num_doc.find(numerazione) < 0)
          numerazione = num_doc.row(0);
      }
      else
      {
        const TCodice_numerazione& cn = cached_numerazione(codnum);
        tipo_doc = "F01";
        if (cn.find_tipo_doc(tipo_doc) < 0)
          tipo_doc = cn.tipo_doc(0);      
      }

      doc = new TDocumento('D', datadoc.year(), numerazione, 0);
      doc->put(DOC_TIPODOC, tipo_doc);
      doc->put(DOC_STATO, 2);       // Stampato

      doc->put(DOC_TIPOCF, 'C');
      doc->put(DOC_CODCF, codcf);
      doc->put(DOC_DATADOC, datadoc);
      doc->put(DOC_DOC1, ndoc);
      doc->put(DOC_CODPAG, codpag);
      doc->put(DOC_STATO, 1);
      doc->put(DOC_CODCMS, cdc);

      key = "C";
      key.add(codcf);
      key.add("N");
      TRecord_array nsban(key, LF_CFBAN);
      if (nsban.rows() > 0)
      {
        const TRectype& rec = nsban.row(1);
        doc->put(DOC_CODABIP, rec.get(CFBAN_ABI));
        doc->put(DOC_CODCABP, rec.get(CFBAN_CAB));
      }
    
      key.add("V", 2);
      TRecord_array vsban(key, LF_CFBAN);
      if (vsban.rows() > 0)
      {
        const TRectype& rec = vsban.row(1);
        doc->put(DOC_CODABIA, rec.get(CFBAN_ABI));
        doc->put(DOC_CODCABA, rec.get(CFBAN_CAB));
        doc->put(DOC_IBAN,    rec.get(CFBAN_IBAN));
      }
    }
  
    TRiga_documento& rdoc = doc->new_row("01");
    rdoc.put(RDOC_CODART, codart);
    rdoc.put(RDOC_CODARTMAG, codart);
    rdoc.checked(true);

    rdoc.put(RDOC_UMQTA, um);
    rdoc.put(RDOC_QTA, UNO);
    rdoc.put(RDOC_PREZZO, fat.get(19).as_real());
    rdoc.put(RDOC_CODIVA, anamag.get(ANAMAG_CODIVA));

    TString desc = anamag.get(ANAMAG_DESCR);
    desc << ' ' << periodo;
    if (!ospiti.empty_items())
    {
      const char* sep = " ";
      FOR_EACH_TOKEN(ospiti, o) if (o && *o > ' ')
      {
        desc << sep << o;
        sep = ", ";
      }
    }
    save_rdoc_desc(desc, rdoc);

    desc = fat.get(23).as_string();
    if (desc.full())
    {
      TRiga_documento& rdesc = doc->new_row("05");
      save_rdoc_desc(desc, rdesc);
    }

    done = pi.add_status();
  }

  if (doc != NULL)
  {
    save_doc(*doc, ndocs, mindoc, maxdoc, log);
    delete doc;
    doc = NULL;
  }

  log.log(0, str.cut(0));
  str << TR("Sono stati importati ") << ndocs << TR(" documenti ") << codnum
      << TR(" dal ") << mindoc << TR(" al ") << maxdoc;
  log.log(0, str);

  return done;
}
  
void TImport_app::main_loop()
{
  open_files(LF_TAB, LF_TABCOM, LF_CLIFO, LF_CFVEN, LF_DOC, LF_RIGHEDOC, LF_ANAMAG, LF_UMART, 0);
  
  TImport_mask mask;
	while (mask.run() == K_ENTER)
	{
    TLog_report log;
    TFilename fname = mask.get(F_DIR);
    fname.add(mask.get(F_CLI));
    if (!fname.exist())
    {
      cantread_box(fname);
      continue;
    }
    const TString& cdc = mask.get(F_CDC);
    if (importa_clienti(fname, cdc, log))
    {
      const TString& codnum = mask.get(F_NUM);
      fname = mask.get(F_DIR);
      fname.add(mask.get(F_FAT));
      if (fname.exist())
        importa_fatture(fname, codnum, cdc, log);
      else
        cantread_box(fname);
    }
    else
    {
      log.log(2, TR("E' necessario correggere i problemi segnalati prima di importare le fatture"));
    }

    log.preview();
	}
}

int ps0430400(int argc, char* argv[])
{
  TImport_app a;
  a.run(argc, argv, TR("Importazione CBA"));
  return 0;
}