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


#include "../cg/cg2101.h"
#include "../cg/cglib02.h"
#include "../mg/mglib.h"
#include "ps0430500a.h"

#include "../ca/calib01.h"
#include "../ca/commesse.h"
#include "../ca/movana.h"
#include "../ca/rmovana.h"

///////////////////////////////////////////////////////////
// TRimfin_mask
///////////////////////////////////////////////////////////

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

public:
  TRimfin_mask() : TAutomask("ps0430500a") {}
};

bool TRimfin_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  return true;
}

///////////////////////////////////////////////////////////
// TRimfin_app
///////////////////////////////////////////////////////////

class TRimfin_app : public TSkeleton_application
{
private:
  virtual const char* extra_modules() const { return "cg|cm|mg"; }

protected:
  const TString& mag2cms(const TString& codmag) const;
  void add_rmov(const TImporto& importo, const TString& codmag, const TCausale& caus, TMovimentoPN& mov) const;
  void create_mov(const TString& codcaus, const TDate& datareg, TString_array& saldi, TLog_report& log) const;
  void create_movana(const TRectype& movhead, TString_array& a, TLog_report& log) const;

public:
  virtual void main_loop();
};

struct TScan_info : public TAssoc_array
{
  int _societa;
};

static bool scan_mag(const TRelation& rel, void* jolly)
{
  const TRectype& rec = rel.curr();

  const real qta = rec.get("R0");
  if (qta.sign() <= 0)
    return true;

  TString80 key = rec.get("CODTAB").left(3);
  const TRectype& tabmag = cache().get("MAG", key);
  const int societa = tabmag.get_int("I3");
  if (societa <= 0)  // Magazzino anonimo
    return true; 
  
  TScan_info& saldi = *(TScan_info*)jolly;
  if (saldi._societa > 0 && societa != saldi._societa)
    return true;     // Magazzino di altra societa

  const TDate data = rec.get("D0");
  const TString& codart = rec.get("CODTAB").mid(5, 20);
  const TArticolo_giacenza& art = cached_article_balances(codart);
  const real valore = art.ultimo_costo(data.year());
  if (valore.is_zero())
    return true;
  
  TBill bill; 
  bill.set(rec.get_int("I0"), rec.get_int("I1"), rec.get_long("I2"));
  key << '|' << bill.string();

  real* imp = (real*)saldi.objptr(key);
  if (imp == NULL)
  {
    imp = new real;
    saldi.add(key, imp);
  }
  *imp += qta * valore;

  return true;
}

void TRimfin_app::add_rmov(const TImporto& importo, const TString& codmag, const TCausale& caus, TMovimentoPN& mov) const
{
  if (!importo.is_zero() && codmag.full())
  {
    TRectype& rmv = mov.cg(mov.cg_items());
    rmv.put(RMV_SEZIONE, importo.sezione());
    rmv.put(RMV_IMPORTO, importo.valore());
    rmv.put(RMV_DESCR, cache().get("MAG", codmag, "S0"));

    TString80 cms = codmag; cms.right_just(20, '0');
    const TString& conto = cache().get(LF_COMMESSE, cms, COMMESSE_CODCONTO);
    if (conto.full())
    {
      rmv.put(RMV_GRUPPO,     conto.mid(0, 3));
      rmv.put(RMV_CONTO,      conto.mid(3, 3));
      rmv.put(RMV_SOTTOCONTO, conto.mid(6, 6));
    }
    else
    {
      TBill b; caus.bill(2, b);
      b.put(rmv);
    }
  }
}

void TRimfin_app::create_mov(const TString& codcaus, const TDate& datareg, TString_array& a, TLog_report& log) const
{
  const TCausale caus(codcaus);
  TEsercizi_contabili esercizi;

  TMovimentoPN mov;
  TRectype& head = mov.curr();

  head.put(MOV_DATAREG,  datareg);
  head.put(MOV_DATACOMP, datareg);
  head.put(MOV_CODCAUS,  codcaus);
  head.put(MOV_DESCR,    caus.descrizione());
  head.put(MOV_ANNOIVA,  datareg.year());
  head.put(MOV_ANNOES, esercizi.date2esc(datareg));
  
  TString4 curmag;
  TImporto tot;
  TBill bill; caus.bill(1, bill);

  TString msg; 

  if (bill.conto() <= 0)
  {
    msg.format("Non esiste il conto sulla prima riga della causale %s", (const char*)codcaus);
    log.log(2, msg);
    return;
  }

  FOR_EACH_ARRAY_ROW(a, i, r)
  {
    const TString4 codmag = r->get(0);
    if (codmag != curmag)
    {
      tot.normalize();
      add_rmov(tot, curmag, caus, mov);
      curmag = codmag;
      tot.reset();
    }

    const long sottoc = r->get_long(3);
    bill.set(bill.gruppo(), bill.conto(), sottoc);
    if (!bill.find())
    {
      msg.format("Non esiste il conto %d.%d.%ld", bill.gruppo(), bill.conto(), bill.sottoconto());
      log.log(1, msg);
      continue;
    }
    TRectype& rmv = mov.cg(mov.cg_items());
    bill.put(rmv);

    TImporto importo(caus.sezione(1), real(r->get(4)));
    importo.normalize();
    rmv.put(RMV_SEZIONE, importo.sezione());
    rmv.put(RMV_IMPORTO, importo.valore());

    tot -= importo;
  }
  tot.normalize();
  add_rmov(tot, curmag, caus, mov);

  if (mov.cg_items() >= 2)
  {
    const int err = mov.write();
    if (err == NOERR)
    {
      TSaldo_agg saldo;
      saldo.set_anno_es(head.get_int(MOV_ANNOES));
      saldo.set_data_ulmov(datareg);
      saldo.set_movimentato(true);
      saldo.set_movprovv(false);
      saldo.set_tipo_saldo(normale);
      for (int i = 0; i < mov.cg_items(); i++)
        saldo.aggiorna(mov.cg(i));
      saldo.registra();

      mov.last();

      msg.format(FR("E' stato creato il movimento contabile %ld"), head.get_long(MOV_NUMREG));
      log.log(0, msg);
    }
    else
    {
      msg.format(FR("Errore %d in creazione del movimento contabile"), err);
      log.log(2, msg);
    }
  }

  create_movana(head, a, log);
}

const TString& TRimfin_app::mag2cms(const TString& codmag) const
{
  TString query; 
  query << "USE COMMESSE SELECT CODMAG=\"" << codmag << '"';
  TISAM_recordset cms(query);
  if (cms.move_first())
    return cms.get(COMMESSE_CODCMS).as_string();

  TString& tmp = get_tmp_string(20);
  tmp = codmag; tmp.right_just(20, '0');
  return tmp;
}

void TRimfin_app::create_movana(const TRectype& movhead, TString_array& a, TLog_report& log) const
{
  const TString4 codcaus = movhead.get(MOV_CODCAUS);
  const TCausale caus(codcaus);

  TAnal_mov mov;
  mov.put(MOVANA_DATAREG,  movhead.get(MOV_DATAREG));
  mov.put(MOVANA_DATACOMP, movhead.get(MOV_DATACOMP));
  mov.put(MOVANA_DATADOC,  movhead.get(MOV_DATADOC));
  mov.put(MOVANA_CODCAUS,  codcaus);
  mov.put(MOVANA_ANNOES,   movhead.get(MOV_ANNOES));
  mov.put(MOVANA_NUMREGCG, movhead.get(MOV_NUMREG));
  mov.put(MOVANA_DESCR,    movhead.get(MOV_DESCR));
  
  TImporto totdoc;
  TRectype rmv(LF_RMOVANA);
  TString80 codcms;
  TString msg;

  TBill bill; caus.bill(1, bill);
  if (bill.conto() <= 0)
  {
    //msg.format("Non esiste il conto sulla prima riga della causale %s", (const char*)codcaus);
    // log.log(2, msg);
    return;
  }

  int numrig = 0;
  FOR_EACH_ARRAY_ROW(a, i, r)
  {
    const long sottoc = r->get_long(3);
    bill.set(bill.gruppo(), bill.conto(), sottoc);
    if (!bill.find())
      continue;

    const TString4 codmag = r->get(0);
    codcms = mag2cms(codmag);

    TImporto importo(caus.sezione(1), real(r->get(4)));
    importo.normalize();
    rmv.put(RMOVANA_NUMRIG, ++numrig);
    rmv.put(RMOVANA_SEZIONE, importo.sezione());
    rmv.put(RMOVANA_IMPORTO, importo.valore());
    rmv.put(RMOVANA_CODCMS, codcms);
    rmv.put(RMOVANA_DESCR, cache().get(LF_COMMESSE, codcms, COMMESSE_DESCRIZ));

    totdoc += importo;

   rmv.put(RMOVANA_CODCONTO, bill.string(0x8));

    mov.body(LF_RMOVANA).add_row(rmv);
  }

  totdoc.normalize();
  mov.put(MOVANA_SEZIONE, totdoc.sezione());
  mov.put(MOVANA_TOTDOC, totdoc.valore());

  TLocalisamfile movana(LF_MOVANA);
  const int err = mov.write(movana);

  if (err == NOERR)
  {
    movana.last();
    msg.format(FR("E' stato creato il movimento analitico %ld"), movana.get_long(MOVANA_NUMREG));
    log.log(0, msg);
  }
  else
  {
    msg.format(FR("Errore %d in creazione del movimento analitico"), err);
    log.log(2, msg);
  }

}

  
void TRimfin_app::main_loop()
{
  TRimfin_mask m;
  while (m.run() == K_ENTER)
  {
    TString query;
    query << "USE &MGMAG SELECT I0=50";
    TISAM_recordset recset(query);

    TScan_info saldi;
    saldi._societa = m.get_int(F_SOCIETA);
    recset.cursor()->scan(scan_mag, &saldi, title());
    if (!saldi.empty())
    {
      TString_array a;
      FOR_EACH_ASSOC_OBJECT(saldi, h, k, o)
      {
        TToken_string* r = new TToken_string(k);
        r->add(((real*)o)->string(0, 2));
        a.add(r);
      }
      a.sort();

      TLog_report log; log.kill_duplicates();

      log.log(0, TR("Rimanenze finali"));
      const TDate data_fin = m.get(F_DATAREG_FIN);
      const TString& caus_fin = m.get(F_CODCAUS_FIN);
      create_mov(caus_fin, data_fin, a, log);

      log.log(0, "");
      log.log(0, TR("Rimanenze iniziali"));
      const TDate data_ini = m.get(F_DATAREG_INI);
      const TString& caus_ini = m.get(F_CODCAUS_INI);
      create_mov(caus_ini, data_ini, a, log);

      log.preview();
    }
    else
      warning_box(TR("Non esistono giacenze di articoli con conto acquisti"));
  }
}

int ps0430500(int argc, char* argv[])
{
  TRimfin_app a;
  a.run(argc, argv, TR("Generazione rimanenze"));
  return 0;
}