#include <applicat.h>
#include <automask.h>
#include <defmask.h>

#include "lvlib.h"

#include  "lv3700a.h"
                                          ///////////////////////////////
                                          ////    TPACCHI_CONTATI    ////
                                          ///////////////////////////////

//Classe TPacchi_contati
class TPacchi_contati: public TObject
{  
  real           _qta;
  TString4       _um;
  int            _npacchi;
  TToken_string  _codpacchi;

public:  
  const real&          qta()       const;
  const TString&       um()        const;
  const int            npacchi()   const;
  const TToken_string& codpacchi() const;

  void set_qta(const real& qta);
  void set_um(const char* um);
  void set_npacchi(const int npacchi = 1);
  void set_codpacchi(const TToken_string& codpacchi);

  void add_qta(const real& qta);
  void add_pacchi(const int pacchi = 1);
  void add_codpacco(const TString& codpacco);
  void sub_codpacco(const TString& codpacco);
  
  TPacchi_contati(const real& qta = ZERO, const char* um = "", const int npacchi = 0, const char* codpacco = "");
};

//QTA: metodo che restituisce la quantita
const real& TPacchi_contati::qta() const
{
  return _qta;
}

//UM: metodo che restituisce l'unit� di misura
const TString& TPacchi_contati::um() const
{
  return _um;
}

//NPACCHI: metodo che restituisce il numero dei pacchi pistolati
const int TPacchi_contati::npacchi() const
{
  return _npacchi;
}

//CODPACCHI: metodo che restituisce i codici dei pacchi pistolati
const TToken_string& TPacchi_contati::codpacchi() const
{
  return _codpacchi;
}

//SET_QTA: metodo che setta la quantit�
void TPacchi_contati::set_qta(const real& qta)
{
  _qta = qta;
}

//SET_UM: metodo che setta l'unit� di misura
void TPacchi_contati::set_um(const char* um)
{
  _um = um;
}

//SET_NPACCHI: metodo che setta il numero di pacchi
void TPacchi_contati::set_npacchi(const int npacchi)
{
  _npacchi = npacchi;
}

//SET_CODPACCHI: metodo che setta i codici dei pacchi
void TPacchi_contati::set_codpacchi(const TToken_string& codpacchi)
{
  TToken_string tmp(codpacchi, '-');  
  _codpacchi = tmp;
}

//ADD_QTA_CON: metodo che aggiunge un quantitativo di roba alla quantit� consegnata
void TPacchi_contati::add_qta(const real& qta)
{
  _qta += qta;
}

//ADD_PACCHI: metodo che aggiunge un certo numero di pacchi a quelli consegnati (default = 1)
void TPacchi_contati::add_pacchi(const int pacchi)
{
  _npacchi += pacchi;
}

//ADD_CODPACCO: metodo che aggiunge un codice di un pacco alla TToken_string che li contiene
void TPacchi_contati::add_codpacco(const TString& codpacco)
{
  _codpacchi.add(codpacco);
}

//SUB_CODPACCO: metodo che toglie un codice di un pacco alla TToken_string che li contiene
void TPacchi_contati::sub_codpacco(const TString& codpacco)
{
  int pos = _codpacchi.get_pos(codpacco);
  _codpacchi.destroy(pos);
}

//metodo costruttore
TPacchi_contati::TPacchi_contati(const real& qta, const char* um, const int npacchi, const char* codpacco)
{
  set_qta(qta);
  set_um(um);
  set_npacchi(npacchi);
  
  TToken_string tmp(codpacco, '-');
  set_codpacchi(tmp);
}

                                          ////////////////////////////////
                                          ////    TRIGHE_INV_ARRAY    ////
                                          ////////////////////////////////

//Classe TRighe_inv_array
class TRighe_inv_array: public TAssoc_array
{
public:
  TPacchi_contati* quantita(TString& codart, bool create);
};

//QUANTITA: metodo che cerca nel TAssoc_array le quantit� della riga interessata in base ai parametri passati
//e lo crea in automatico se il parametro create vale "true"
TPacchi_contati* TRighe_inv_array::quantita(TString& codart, bool create)
{
  TPacchi_contati* pc = (TPacchi_contati*)objptr(codart);

  if(pc == NULL && create)
  {
    pc = new TPacchi_contati();
    add(codart, pc);
  }
  return pc;
}

                                          ///////////////////////////////////
                                          ////    TINVENTARIO_TER_MSK    ////
                                          ///////////////////////////////////


//Classe TInventario_ter_msk
class TInventario_ter_msk: public TAutomask
{
  TRighe_inv_array  _ria;
  bool              _nuovo;
  int               _autoselect;

protected:
  bool precarica_righe();
  void registra();  
  void aggiorna_campi(const char* codart, const TPacchi_contati& pc, const real& qtapacco);
  void inventario_da_terminale();

  virtual void on_idle();  
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);

public:
  TInventario_ter_msk();  
};

//PRECARICA_RIGHE: metodo che carica in un TAssoc_array le righe fisiche del documento
bool TInventario_ter_msk::precarica_righe()
{
  //instanzio il documento inventario
  TDoc_key kinv(get_int(F_ANNO), ini_get_string(CONFIG_DITTA, "lv", "NUM_INV", NULL), get_long(F_NINV));
  TDoc_inventario inv(kinv);

  TLocalisamfile fdoc(LF_DOC);
  int err = inv.read(fdoc, _isequal, _testandlock);
  if(err != NOERR)
  {
    warning_box(TR("L'inventario gi� in uso"));
    return false;
  }

  if (inv.empty())
  {
    warning_box(TR("L'inventario cercato � inesistente"));
    return false;
  }

  //controllo lo stato del documento
  const TString4 stato = cache().get("%TIP", ini_get_string(CONFIG_DITTA, "lv", "TIPODOC_INV", NULL), "S2").mid(2,1);
  if (inv.get(DOC_STATO) == stato)
  {
    TString str;
    str << "L'inventario " << get_long(F_NINV) << " � gi� stato stampato e non pu� essere aggiornato";
    warning_box(str);

    reset(F_CHIAVE);

    return false;
  }

  TDate oggi(TODAY);

  //metto ogni riga del documento nel TAssoc_array che contiene gli oggetti TPacchi_contati
  //e che ha per chiave il codice articolo
  FOR_EACH_PHYSICAL_RDOC(inv, i, row)
  {
    TDoc_inventario_row rinv(*row);

    TString80 codart = rinv.codart();

    if (codart.blank())
      continue;

    TPacchi_contati* pc = _ria.quantita(codart, true);
    pc->set_qta(rinv.qta());
    pc->set_um(rinv.um());
    pc->set_npacchi(rinv.num_pacchi());
    pc->set_codpacchi(rinv.cod_pacchi());
  }
  return true;
}

//REGISTRA: metodo che salva il buono di prelievo cos� com'�
void TInventario_ter_msk::registra()
{
  //se sto salvando un documento nuovo, allora lo genero, 
  //altrimenti aggiorno quello esiete gi�
  TDoc_key kinv(get_date(F_DATAINV).year(), get(F_CODNUM), get_long(F_NINV));  
  if (_nuovo)
    kinv.set_ndoc(0);

  //instanzio il documento e salvo i dati di interesse sulle testate
  //se necessario
  TDoc_inventario inv(kinv);
  if(_nuovo)
  {
    TString8 codcau = cache().get("%TIP", ini_get_string(CONFIG_DITTA, "lv", "TIPODOC_INV", NULL), "S9");
    inv.put(DOC_TIPODOC, get(F_TPDOC));
    inv.put(DOC_DATADOC, get_date(F_DATAINV));
    inv.put(DOC_CAUSMAG, codcau);
    inv.set_difinv();
  }

  //per ogni oggetto dell'array cerco o creo una riga merce del documento
  FOR_EACH_ASSOC_OBJECT(_ria, obj, key, itm)
  {
    const TPacchi_contati& pc = *(TPacchi_contati*)itm;
    const TString80 codart(key);
    const TString& desart = cache().get(LF_ANAMAG, codart, "DESCR");

    TString8 magazzino;
    magazzino  << ini_get_string(CONFIG_DITTA, "lv", "CODMAG") << ini_get_string(CONFIG_DITTA, "lv", "CODMAGP");

    TDoc_inventario_row& rinv = inv.find_or_create_row(codart);
    
    rinv.set_codart(codart);
    rinv.set_desart(desart);
    rinv.set_qta(pc.qta());
    rinv.set_um(pc.um());
    rinv.set_num_pacchi(pc.npacchi());
    rinv.set_cod_pacchi(pc.codpacchi());  
    rinv.set_magazzino(magazzino);  //DA CONTROLLARE
    rinv.set_magazzinoc(magazzino); //DA CONTROLLARE
  }

  inv.write();
  if(_nuovo)
  {
    //aggiorno il numero documento con quello sicuramente corretto
    TToken_string tmp = inv.chiave();
    TDoc_key chiave(tmp);  
    set(F_NINV, chiave.ndoc());
    _nuovo = false;
  }
}

//AGGIORNA_CAMPI: metodo che aggiorna i campi della maschera
void TInventario_ter_msk::aggiorna_campi(const char* codart, const TPacchi_contati& pc, const real& qtapacco)
{
  set(F_CODART, codart);
  set(F_PACCHI, pc.npacchi());
  set(F_QTACON, pc.qta());
  set(F_QTAPACCO, qtapacco);
}

//INVENTARIO_DA_TERMINALE: metodo che somma alla riga corretta un pacco (quello pistolato) e lo assegna a un cliente,
//sottraendolo dal magazzino del pulito
void TInventario_ter_msk::inventario_da_terminale()
{
  //leggo il codice del pacco pistolato
  const TString80 codpacco = get(F_BARCODE);

  if (codpacco.full())
  {
    //se posso instanzio la riga pacco
    const TRiga_pacco rp(codpacco);

    if (rp.empty())
    {
      warning_box(TR("Il pacco non esiste a magazzino"));
      warning_box(TR("Non � stato possibile sommare il pacco a nessuna riga del buono"));
    }
    else
    {
      //leggo i dati di interesse dal pacco
      if(rp.is_associato())
      {
        warning_box(TR("Il pacco pistolato � un pacco a cliente"));
        return;
      }

      TToken_string tmp = rp.rigabolla();
      if(tmp.get_long(3) > 0)
      {
        warning_box(TR("Il pacco pistolato � gi� associato a un buono"));
        return;
      }

      if(tmp.get_long(3) < 0)
      {
        warning_box(TR("Il pacco pistolato non � disponibile per l'inventario"));
        return;
      }


      TString80 codart = rp.articolo();
      const real qtapacco = rp.quantita();

      //se � la prima volta, creo il nuovo TPacchi_contati,
      //altrimenti aggiorno quello che gi� esiste
      if(!_ria.is_key(codart))
      {        
        TPacchi_contati& pc = *(_ria.quantita(codart, true));

        pc.set_qta(qtapacco);

        //recupero l'unit� di misura principale di quest'articolo
        TToken_string key;
        key.add(codart);
        key.add(1);
        const TString4 um = cache().get(LF_UMART, key, UMART_UM);
        pc.set_um(um);
        pc.set_npacchi();

        key.cut(0);
        key.add(codpacco);
        pc.set_codpacchi(key);

        aggiorna_campi(codart, pc, qtapacco);
      }
      else
      {
        TPacchi_contati& pc = *(TPacchi_contati*)_ria.objptr(codart);

        TToken_string codpacchi(pc.codpacchi());
        int pos = codpacchi.get_pos(codpacco);

        if(pos >= 0)
        {
          if(yesno_box(TR("Il pacco risulta gi� nell'inventario; si desidera annullarlo?")))
          {
            pc.add_qta(-qtapacco);
            pc.add_pacchi(-1);
            pc.sub_codpacco(codpacco);
          }
          else
            return;
        }
        
        pc.add_qta(qtapacco);
        pc.add_pacchi();
        pc.add_codpacco(codpacco);

        aggiorna_campi(codart, pc, qtapacco);
      }      
    }
  }      
}

//ON_FIELD_EVENT: metodo che gestisce gli eventi sui campi della maschera
bool TInventario_ter_msk::on_field_event(TOperable_field& f, TField_event e, long jolly)
{
  //a seconda del bottone premuto esegui un metodo diverso
  switch (f.dlg())
  {    
  case DLG_NEWREC:
    //se viene premuto nuovo, cerco il prossimo numero di inventario
    //e mostro/nascondo i campi corretti
    if (e == fe_button)
    {
      _nuovo = true;

      int ninv = 0;

      TString query;
      query << "USE DOC\n"
            << "FROM PROVV=\"D\" ANNO=" << get_int(F_ANNO) << " CODNUM=\"" << get(F_CODNUM) << "\"\n"
            << "TO PROVV=\"D\" ANNO="   << get_int(F_ANNO) << " CODNUM=\"" << get(F_CODNUM) << "\"";
      TISAM_recordset inventari(query);
      if (inventari.move_last())
        ninv = inventari.get(DOC_NDOC).as_int();
      ninv++;

      set(F_NINV, ninv);  disable(F_NINV);

      show(F_BARCODE);
      show(F_CODART);
      show(F_PACCHI);
      show(F_QTAPACCO);
      show(F_QTACON);

      hide(F_CHIAVE);
      hide(F_ANNO);
      hide(F_DATAINV);

      enable(DLG_SAVEREC);
      enable(DLG_DELREC);
      enable(DLG_CANCEL);

      field(F_BARCODE).set_focus();
      _autoselect = 1;
      return false;
    }
    break;
  case DLG_SAVEREC:
    //se viene premuto salva, registro l'inventario
    if (e == fe_button)
    {
      registra();
      return false;
    }
    break;
  case DLG_DELREC:
    //se viene premutop cancella, elimino l'inventario corrente, poi mostro/nascondo i campi corretti
    if (e == fe_button && yesno_box(TR("Si vuole veramente cancellare l'inventario corrente?")))
    {
      //se non � nuovo, lo rimuovo anche dagli archivi
      if (!_nuovo)
      {
        TLocalisamfile doc(LF_DOC);
        TDoc_key kinv(get_int(F_ANNO), get(F_CODNUM), get_long(F_NINV));
        
        TDoc_inventario inv(kinv);
        inv.destroy_rows();
        inv.remove(doc);
      }
      _ria.destroy();
      
      reset(F_BARCODE);     hide(F_BARCODE);
      reset(F_CODART);      hide(F_CODART);
      reset(F_PACCHI);      hide(F_PACCHI);
      reset(F_QTAPACCO);    hide(F_QTAPACCO);
      reset(F_QTACON);      hide(F_QTACON);

      enable(F_NINV);       reset(F_NINV);
      show(F_CHIAVE);       reset(F_CHIAVE);
      show(F_ANNO);
      show(F_DATAINV);      reset(F_DATAINV);

      disable(DLG_SAVEREC);
      disable(DLG_DELREC);
      disable(DLG_CANCEL);
    }
    break;
  case DLG_CANCEL:
    //se viene premuto annulla, lascio perdere quello che stavo facendo
    //e torno alla situazione iniziale
    if (e == fe_button)
    {
      TDoc_key kinv(get_int(F_ANNO), ini_get_string(CONFIG_DITTA, "lv", "NUM_INV", NULL), get_long(F_NINV));
      TDoc_inventario inv(kinv);
      TLocalisamfile fdoc(LF_DOC);
      inv.read(fdoc);

      reset(F_BARCODE);     hide(F_BARCODE);
      reset(F_CODART);      hide(F_CODART);
      reset(F_PACCHI);      hide(F_PACCHI);
      reset(F_QTAPACCO);    hide(F_QTAPACCO);
      reset(F_QTACON);      hide(F_QTACON);

      enable(F_NINV);       reset(F_NINV);
      show(F_CHIAVE);       reset(F_CHIAVE);
      show(F_ANNO);
      show(F_DATAINV);      reset(F_DATAINV);

      disable(DLG_SAVEREC);
      disable(DLG_DELREC);
      disable(DLG_CANCEL);

      _ria.destroy();

      return false;
    }
    break;
  case F_CHIAVE:
    {
      //se viene riempito il campo chiave, cerca di caricare un documento
      if (e == fe_modify)
      {
        TString kinv = f.get();        
        if (kinv.full())
        {
          set(F_ANNO, atol(kinv.left(4)));
          set(F_NINV, atol(kinv.mid(4)));
          field(F_NINV).check();

          if(!precarica_righe())
            return false;

          hide(F_ANNO);
          hide(F_DATAINV);          

          show(F_BARCODE);
          show(F_CODART);
          show(F_PACCHI);
          show(F_QTAPACCO);
          show(F_QTACON);

          enable(DLG_SAVEREC);
          enable(DLG_DELREC);
          enable(DLG_CANCEL);

          disable(F_NINV);

          _autoselect = 1;
          field(F_BARCODE).set_focus();
          f.hide();
        }
      }
    }
    break;
  case F_NINV:
    {
      //se viene riempito il campo NINV, cerca di caricare un documento
      if (e == fe_modify && f.get_long() != 0 && get(F_CHIAVE).empty())
      {
        field(F_NINV).check();
        _nuovo = false;

        if(!precarica_righe())
          return false;

        hide(F_ANNO);
        hide(F_DATAINV);          

        show(F_BARCODE);
        show(F_CODART);
        show(F_PACCHI);
        show(F_QTAPACCO);
        show(F_QTACON);

        enable(DLG_SAVEREC);
        enable(DLG_DELREC);
        enable(DLG_CANCEL);

        f.disable();

        _autoselect = 1;
        field(F_BARCODE).set_focus();
      }
    }
    break;
  case F_BARCODE:
    {
      //se viene riempito il campo barcode, lanco la procedura dell'inventario
      if (e == fe_modify && f.get().full())
      {
        _autoselect = 1;
        inventario_da_terminale();        
      }
    }
    break;
  default:break;
  }
  return true;
}

void TInventario_ter_msk:: on_idle()
{
  TMask::on_idle();
  if (_autoselect >= 0 && get(F_BARCODE).full())
  {
    reset(F_BARCODE);
    field(F_BARCODE).set_focus();
    _autoselect = -1;
  }
}

TInventario_ter_msk::TInventario_ter_msk():TAutomask("lv3700a") 
{
  //precarico i campi fissi
  set(F_CODNUM, ini_get_string(CONFIG_DITTA, "lv", "NUM_INV", NULL));
  set(F_TPDOC,  ini_get_string(CONFIG_DITTA, "lv", "TIPODOC_INV", NULL));

  const TRectype& tpdoc = cache().get("%NUM", ini_get_string(CONFIG_DITTA, "lv", "NUM_INV", NULL));  
  set(F_DESCR, tpdoc.get("S0"));

  TDate data(TODAY);
  TEsercizi_contabili es;
  int annoes = es.date2esc(data);
  set(F_ANNO, annoes);
  
  hide(F_CODCF);
  hide(F_RAGSOC);
  hide(F_BARCODE);
  hide(F_CODART);
  hide(F_PACCHI);
  hide(F_QTAPACCO);
  hide(F_QTACON);

  disable(DLG_SAVEREC);
  disable(DLG_DELREC);
  disable(DLG_CANCEL);
}

                                 ///////////////////////////////////
                                 ////    TINVENTARIO_TER_APP    ////
                                 ///////////////////////////////////

//classe TInventario_ter_app
class TInventario_ter_app : public TSkeleton_application
{
  TInventario_ter_msk*  _msk;
protected:
  virtual bool create();
  virtual bool destroy();

public:
  bool transfer();
  virtual void main_loop();  
};

//CREATE: metodo costruttore
bool TInventario_ter_app::create()
{
  _msk = new TInventario_ter_msk();
  open_files(LF_DOC, LF_RIGHEDOC);
  return TSkeleton_application::create();
}

//DESTROY: metodo distruttore
bool TInventario_ter_app::destroy()
{	
  delete _msk;
  return TApplication::destroy();
}

//TRANSFER: metodo che scorre i campi nome e, se sono pieni, richiama il metodo
//ELABORA_FILE(), che effettivamente fa l'elaborazione
bool TInventario_ter_app::transfer()
{   
  return true;
}

void TInventario_ter_app::main_loop()
{
  while (_msk->run() == K_ENTER)
    transfer();
 }

int lv3700(int argc, char *argv[])
{
  TInventario_ter_app a;
  a.run (argc, argv, "Evasione Buoni di Prelievo");
  return TRUE;
}