#include <applicat.h>
#include <automask.h>
#include <config.h>
#include <execp.h>
#include <prefix.h>
#include <postman.h>
#include <progind.h>
#include <recarray.h>
#include <relation.h>
#include <sheet.h>
#include <utility.h>

#include "ba7300.h"

class TFile_selector : public TAutomask
{
  TArray_sheet* _sht;

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);        
          
public:
  TFile_selector();
  virtual ~TFile_selector();
};

bool TFile_selector::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case F_FILE:
    if (e == fe_button)
    { 
      const int nfile = atoi(o.get());
      FOR_EACH_SHEET_ROW_BACK((*_sht), i, row)
      {
        if (row->get_int(0) == nfile)
        {
          _sht->select(i);
          break;
        }
      }
      switch (_sht->run())
      {
      case K_ENTER:
        {                  
          TToken_string& row = _sht->row(-1);
          set(o.dlg(), row.get(0));
          e = fe_modify;
        }    
        break;
      default:  
        break; 
      }
    }
    if (e == fe_modify)
    {
      const TPrefix& pref = prefix();
      const int total = pref.items();
      const int nfile = atoi(o.get());
      FOR_EACH_SHEET_ROW_BACK((*_sht), i, row)
      {
        if (row->get_int(0) == nfile)
        {
          set(o.dlg()+1, row->get(1));
          break;
        }
      }
      
      TList_field& lb = (TList_field&)field(F_KEY);
      if (i < 0)
      {
        lb.replace_items("", "");
        return error_box("Inserire un numero di file valido");
      }

      TLocalisamfile f(nfile);
      const RecDes& rd = f.curr().rec_des();
      TToken_string codes, items;
      for (i = 1; i <= rd.NKeys; i++)
      {
        codes.add(i);
        items.add("Chiave "); items << i;
      }
      lb.replace_items(codes, items);
      lb.set("1");
    }
    break;
  case F_KEY:
    if (e == fe_modify)
    {                                   
      const int nkey = atoi(o.get());
      const int nfile = get_int(F_FILE);
      if (nfile >= LF_USER && nkey > 0)
      {
        TLocalisamfile f(nfile);
        const RecDes& rd = f.curr().rec_des();
        const KeyDes& kd = rd.Ky[nkey-1];
        TToken_string str("", '+');
        for (int i = 0; i < kd.NkFields; i++)
        {                        
          const int nf = kd.FieldSeq[i] % MaxFields;
          const RecFieldDes& rf = rd.Fd[nf];  
          str.add(rf.Name);
        }
        set(o.dlg()+1, str);
      }
    }
    break;
  default:
    break;
  }    
  return TRUE;
}


static int find_edit_apps(TConfig& cfg, void* jolly)
{        
  TBit_array& ba = *(TBit_array*)jolly;
  TAssoc_array& ass = cfg.list_variables();
  FOR_EACH_ASSOC_STRING(ass, obj, key, val)
  {
    if (strncmp(key, "Edit_", 5) == 0)
    {
      const int nfile = atoi(key+5);
      ba.set(nfile);
    }
  }
  return 0;
}
  
TFile_selector::TFile_selector() : TAutomask("ba7300a") 
{ 
  TIndwin iw(0, "Inizializzazione Editors...", FALSE, FALSE);

  _sht = new TArray_sheet(-1,-1,-6,-6,"Selezione archivio", "Numero@6R|Descrizione@50|Editor|E-Mail");
  const TPrefix& pref = prefix();
  const int total = pref.items();
  TString app, mail;    

  TBit_array edit_apps, mail_apps;
  
  TConfig ini("install.ini", "Main");
  ini.for_each_paragraph(find_edit_apps, &edit_apps);
  
  TConfig inidit(CONFIG_DITTA, "MailTransactions");
  TAuto_token_string filter;
  for (int f = 0; inidit.exist("Filter", f); f++)
  {
    filter = inidit.get("Filter", NULL, f);
    const int num = filter.get_int(1);
    mail_apps.set(num);
  }

  for (int i = LF_USER; i < total; i++)
  {           
    const bool can_edit = edit_apps[i];
    const bool can_mail = mail_apps[i];
    
    if (can_edit || can_mail)
    {
      app.cut(0);
      if (can_edit && i > LF_TAB)
      {
        TLocalisamfile f(i);
        f.get_relapp(app);
      }
      mail = can_mail ? "X" : "";
    
      TToken_string* row = new TToken_string;
      *row << i;
      row->add(pref.description(*row));
      row->add(app);
      row->add(mail);
      _sht->rows_array().add(row);
    }
  }
}
  
TFile_selector::~TFile_selector()
{ 
  delete _sht;
}
  
///////////////////////////////////////////////////////////
// TBrowser_sheet
///////////////////////////////////////////////////////////

class TBrowser_sheet : public TCursor_sheet
{ 
public:
  TBrowser_sheet(TCursor* c, const char* f, const char* t, const char* h);
};

TBrowser_sheet::TBrowser_sheet(TCursor* c, const char* f, const char* t, const char* h)
              : TCursor_sheet(c, f, t, h, 0x3, 1)
{   
  TToken_string tag = "Selezione"; 
  set_tab_buttons(tag);
}

///////////////////////////////////////////////////////////
// TRecord_selector
///////////////////////////////////////////////////////////

class TRecord_selector : public TAutomask
{ 
  TRelation* _relation;
  TCursor* _cursor;
  TToken_string _fields, _head;

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);        
  bool add_special_field(short id, const RecFieldDes& rf, int x, int y);
  
  void add_browse_field(const RecFieldDes& rf);
  int create_fields(const char* title, const RecDes& rd, int nkey, short id, int starty);

public:
  TCursor& cursor() { return *_cursor; }
  TToken_string& fields() { return _fields; }

  TRecord_selector(const TFile_selector& fm);
  virtual ~TRecord_selector();
};

bool TRecord_selector::on_field_event(TOperable_field& o, TField_event e, long jolly)
{     
  if (o.dlg() >= 201 && e == fe_button)
  {                             
    const char* title = _cursor->file().description();
    TBrowser_sheet cs(_cursor, _fields, title, _head);
    
    const short first_id = o.dlg() >= 301 ? 301 : 201;
    TRectype& rec = _cursor->curr();
    rec.zero();
    if (_fields.not_empty())
    {
      short id = first_id;
      FOR_EACH_TOKEN(_fields, tok)
      {
        if (id2pos(id) > 0)
          rec.put(tok, get(id++));
        else
          break;  
      }
      const TRecnotype pos = _cursor->read();
      cs.select(pos);
    }
    
    const KEY k = cs.run();
    switch (k)
    {
    case K_ENTER:
      {
        short id = first_id;
        FOR_EACH_TOKEN(_fields, tok)
        {
          if (id2pos(id) > 0)
            set(id++, rec.get(tok));
          else
            break;  
        }
      }
      break;
    case K_INS:
      {     
        TString str;
        if (_cursor->file().get_relapp(str))
        {           
          TExternal_app app(str);
          app.run();
        }
        else
          error_box("Il file non � associato ad un editor");
      }
      break;
    case K_CTRL+'G':
      {   
        const char* const inifile = "trans.ini";
        TConfig ini(inifile, "Transaction");  
        ini.remove_all();
        ini.set("Action", "Modify");
        TString str; str << _cursor->file().num();
        ini.set_paragraph(str);
        ini.remove_all();
        TToken_string row = cs.row(-1);
        TString16 field;
        for (int i = 0; ; i++)
        {
          _fields.get(i, field);
          if (field.not_empty())
            ini.set(field, row.get(i));
          else
            break;  
        }
        ini.set_paragraph("Transaction"); // Forza il flush
        if (_cursor->file().get_relapp(str))
        {           
          str << " /i" << inifile;
          TExternal_app app(str);
          app.run();
        }
        else
          error_box("Il file non � associato ad un editor");
      }    
      break;
    default:
      break;  
    }  
  }
  return TRUE;
}

bool TRecord_selector::add_special_field(short id, const RecFieldDes& rf, int x, int y)
{              
  TString16 prompt = rf.Name;
  prompt.lower(); 
  prompt[0] = toupper(prompt[0]);
  prompt.left_just(14);
  
  const int logicnum = get_int(F_FILE);

  if (logicnum == LF_DOC && strcmp(rf.Name, "PROVV") == 0)
  {             
    add_list(id, 0, prompt, x, y, 12, "", "D|P", "Definitivo|Provvisiorio");
    return TRUE;
  }
  if (strcmp(rf.Name, "TIPOCF") == 0)
  {             
    add_list(id, 0, prompt, x, y, 9, "", "C|F", "Cliente|Fornitore");
    return TRUE;
  }
  return FALSE;
}

int TRecord_selector::create_fields(const char* title, const RecDes& rd, int nkey, short id, int starty)
{ 
  TString prompt;
  int i;  

  int x = 2;
  
  const KeyDes& kd = rd.Ky[nkey];
  const int nf = kd.NkFields;
  
  int rows = 1;
  for (i = 0; i < nf; i++)
  {                        
    const int nf = kd.FieldSeq[i] % MaxFields;
    const RecFieldDes& rf = rd.Fd[nf];  
    if (x + rf.Len + 16 > 76)
    {
      x = 2;
      rows++;
    }
    x += (rf.Len+16) > 38 ? 76 : 38;
  }  
  
  add_groupbox(-1, 0, title, 1, starty, 80, rows+2);
  
  int y = starty+1;
  x = 2;
  for (i = 0; i < nf; i++)
  {                        
    const int nf = kd.FieldSeq[i] % MaxFields;
    const RecFieldDes& rf = rd.Fd[nf];  
    prompt = rf.Name;
    prompt.lower(); 
    prompt[0] = toupper(prompt[0]);
    prompt.left_just(14);
    
    if (x + rf.Len + 16 > 76)
    {
      x = 2;
      y++;
    }

    switch (rf.TypeF)
    {     
    case _intfld:
    case _longfld:
    case _realfld:
    case _wordfld:
      add_number(id+i, 0, prompt, x, y, rf.Len, "BU");
      break;
    case _intzerofld:
    case _longzerofld:
      add_number(id+i, 0, prompt, x, y, rf.Len, "BUZ");
      break;
    case _datefld:
      add_date(id+i, 0, prompt, x, y, "B");
      break;
    case _boolfld:
      add_boolean(id+i, 0, prompt, x, y);
      break;
    case _alfafld:
    case _charfld:
    default:
      if (!add_special_field(id+i, rf, x, y))
        add_string(id+i, 0, prompt, x, y, rf.Len, "BU");
      break;
    }
    
    x += (rf.Len+16) > 38 ? 76 : 38;
  }
  return y+1;
}

void TRecord_selector::add_browse_field(const RecFieldDes& rf)
{
  if (_fields.get_pos(rf.Name) < 0)
  {
    TString16 prompt = rf.Name;
    _fields.add(prompt);
    prompt.lower(); 
    prompt[0] = toupper(prompt[0]);
        
    const int len = min(rf.Len, 50);
    if (len > prompt.len())
      prompt << '@' << len;
    _head.add(prompt);
  }  
}

TRecord_selector::TRecord_selector(const TFile_selector& fm) : TAutomask("ba7300a")
{
  for (short id = F_FILE; id <= F_SENDMAIL; id++)
  {
    set(id, fm.get(id)); 
    if (id < F_SENDMAIL)
      field(id).disable();
  }   
  
  const int logicnum = get_int(F_FILE);
  const int nkey = get_int(F_KEY)-1;
  _relation = new TRelation(logicnum);
  _cursor = new TCursor(_relation, "", nkey+1);
  
  const RecDes& rd = _relation->curr().rec_des();   
  const KeyDes& kd = rd.Ky[nkey];
  
  int i;
  for (i = 0; i < kd.NkFields; i++)
  {
    const int nf = kd.FieldSeq[i] % MaxFields;
    const RecFieldDes& rf = rd.Fd[nf];  
    add_browse_field(rf);
  }

  for (int k = 0; k < rd.NKeys; k++) if (k != nkey)
  {
    const KeyDes& kd = rd.Ky[k];
    for (i = 0; i < kd.NkFields; i++)
    {
      const int nf = kd.FieldSeq[i] % MaxFields;
      const RecFieldDes& rf = rd.Fd[nf];  
      add_browse_field(rf);
    }
  }
  
  for (i = 0; i < rd.NFields && _head.items() < 6; i++)
  {
    const RecFieldDes& rf = rd.Fd[i];  
    add_browse_field(rf);
  }
  
  int y = 5;
  y = create_fields("@bDal record", rd, nkey, 201, y);
  create_fields("@bAl record", rd, nkey, 301, y+1);
  set_handlers();
}

TRecord_selector::~TRecord_selector()
{
  delete _cursor;
  delete _relation;
}

///////////////////////////////////////////////////////////
// MailFlood app
///////////////////////////////////////////////////////////

class TMail_flood_app : public TSkeleton_application
{ 
  virtual void main_loop();

protected:
  void send_records(TCursor& cur);
  void send_mails(TCursor& cur);
  void fill_rows(const TRectype& parent, int logicnum, TConfig& ini) const;

public:
  void fill_key(const TRectype& curr, TConfig& ini) const;
  void fill_transaction(const TRectype& curr, TConfig& ini) const;
  void send_selected(TRecord_selector& m);
};

inline TMail_flood_app& app() { return (TMail_flood_app&)main_app(); }

///////////////////////////////////////////////////////////
// Main 
///////////////////////////////////////////////////////////

void TMail_flood_app::fill_key(const TRectype& curr, TConfig& ini) const
{
  TString16 para; para << curr.num();
  ini.set_paragraph(para);
  ini.remove_all();
  
  const RecDes& rd = curr.rec_des();   
  const KeyDes& kd = rd.Ky[0];
  for (int i = 0; i < kd.NkFields; i++)
  {
    const int nf = kd.FieldSeq[i] % MaxFields;
    const RecFieldDes& rf = rd.Fd[nf];  
    const char* field = rf.Name;
    ini.set(field, curr.get(field));
  }
  
}

void TMail_flood_app::send_records(TCursor& cur)
{                   
  TFilename dir; dir.tempdir();
  dir.add("transpos");
  make_dir(dir);
  
  const TRectype& curr = cur.curr();
  
  cur.freeze();
  const long items = cur.items();
  TProgind pi(items, "Generazione transazioni...", FALSE, TRUE);
  
  TString16 para; para << curr.num(); // Paragrafo con i campi
  TFilename ininame;         
  
  for (cur = 0; cur.pos() < items; ++cur)
  {
    pi.addstatus(1);
    ininame = dir;
    ininame << SLASH << "tr" << cur.pos() << ".ini";
    TConfig ini(ininame, "Transaction");
    ini.set("Action", "Link");
    ini.set("Mode", "A");
    fill_key(curr, ini);
  }
  
  TString appname; 
  if (cur.file().get_relapp(appname))
  {                        
    appname << " /i" << dir << SLASH << "tr*.ini";
    TExternal_app app(appname);
    app.run();
  }
  
  for (long i = 0; i < items; i++)
  {
    ininame = dir;
    ininame << SLASH << "tr" << i << ".ini";
    ::remove(ininame);
  }
  xvt_fsys_rmdir(dir);
}

void TMail_flood_app::fill_rows(const TRectype& parent, int logicnum, TConfig& ini) const
{
  TLocalisamfile rowsfile(logicnum);  // Carico tracciato record!
  TRectype& rec = rowsfile.curr();
  
  TString16 numfield;
  
  const RecDes& rd = rec.rec_des();   
  const KeyDes& kd = rd.Ky[0];
  for (int i = 0; i < kd.NkFields; i++)
  {
    const int nf = kd.FieldSeq[i] % MaxFields;
    const RecFieldDes& rf = rd.Fd[nf];  
    const char* field = rf.Name;
    if (i < kd.NkFields-1)
      rec.put(field, parent.get(field));  // Copia nella riga la chiave 1 della testata
    else
      numfield = field;                   // Memorizza campo numeratore
  }

  TRecord_array arr(rec, numfield);
  for (int r = arr.first_row(); r > 0 && r <= arr.rows(); r = arr.succ_row(r))  
  {
    const TRectype& row = arr[r];
    TString16 para; para << row.num() << ',' << r;
    ini.set_paragraph(para);
  
    for (int i = row.items()-1; i >= 0; i--)
    {
      const char* name = row.fieldname(i);
      if (strcmp(name, "G1") != 0)
      {
        TFieldref fr(name, 0);
        fr.write(ini, para, row.get(name));
      }
    }
  }
}

void TMail_flood_app::fill_transaction(const TRectype& rec, TConfig& ini) const
{
  TString16 para; para << rec.num();
  ini.set_paragraph(para);
  
  for (int i = rec.items()-1; i >= 0; i--)
  {
    const char* name = rec.fieldname(i);
    TFieldref fr(name, 0);
    fr.write(ini, para, rec.get(name));
  }
  
  switch (rec.num())
  {              
  case LF_ANAMAG: 
    fill_rows(rec, LF_UMART, ini); 
    break;
  case LF_DIST: 
    fill_rows(rec, LF_RDIST, ini); 
    break;
  case LF_DOC: 
    fill_rows(rec, LF_RIGHEDOC, ini); 
    break;
  case LF_MOV: 
    fill_rows(rec, LF_RMOV, ini); 
    fill_rows(rec, LF_RMOVIVA, ini); 
    break;
  default: break;
  };
}

void TMail_flood_app::send_mails(TCursor& cur)
{                   
  const TRectype& rec = cur.curr();

  TFilename ininame; ininame.tempdir();
  ininame.add("trans.ini");

  cur.freeze();
  const long items = cur.items();
  TProgind pi(items, "Spedizione transazioni...", TRUE, TRUE);
  
  const long ditta = main_app().get_firm();

  for (cur = 0; cur.pos() < items; ++cur)
  {
    pi.addstatus(1);  
    if (pi.iscancelled())
      break;
    const bool ok = ::can_dispatch_transaction(rec);
    if (ok)
    {
      TConfig ini(ininame, "Transaction");
      ini.set("Action", "Modify");
      ini.set("Firm", ditta);
      ini.set("Mode", "A");
      fill_transaction(rec, ini);
    } 
    if (ok)
    {
      ::dispatch_transaction(rec, ininame);
      ::remove(ininame);
    }
  }
}

void TMail_flood_app::send_selected(TRecord_selector& m)
{
  TCursor cur = m.cursor();
  TRectype recfrom = cur.curr();
  TRectype recto = cur.curr();
  
  recfrom.zero();
  recto.zero();
  for (int i = 0; ; i++)
  {
    if (m.id2pos(201+i) < 0)
      break;
    const char* field = m.fields().get(i);
    recfrom.put(field, m.get(201+i));
    recto.put(field, m.get(301+i));
  }
  
  cur.setregion(recfrom, recto);
  const long items = cur.items();
  if (items > 0)
  {
    if (yesno_box("Confermare l'invio di %ld transazioni", items))
    {
      if (m.get_bool(F_SENDMAIL))    
        send_mails(cur);
      else
        send_records(cur);
    }
  }
  else
    warning_box("Nessun record da inviare");
}

void TMail_flood_app::main_loop()
{
  TFile_selector fm;
  while (fm.run() == K_ENTER)
  {
    TRecord_selector rm(fm);
    while (rm.run() == K_ENTER)
      send_selected(rm);
  }
}

int ba7300(int argc, char* argv[])
{  
  TMail_flood_app ma;
  ma.run(argc, argv, "Sincronicity");
  
  return 0;
}