#include <diction.h>
#include <dongle.h>
#include <prefix.h>
#include <progind.h>
#include <relation.h>
#include <sheet.h>
#include <toolfld.h>
#include <urldefid.h>

#include "ba1100.h"

// Utilities

// Ordina per importanza i campi di un tracciato record
static int sort_fields(const TObject** r1, const TObject** r2)
{
  TToken_string& s1 = (TToken_string&)**r1;
  TToken_string& s2 = (TToken_string&)**r2;
  int k1 = s1.get_char(2) == '1';
  int k2 = s2.get_char(2) == '1';
  int cmp = k2 - k1;
  if (cmp == 0)
  {
    k1 = s1.get_int(1);
    k2 = s2.get_int(1);
    cmp = k1 - k2;
  }
  return cmp;
}

// Riempie la lista dei campi del file corrente
static int fill_fields(int logicnum, TString_array& flds, bool keys_only = false)
{
  const RecDes& rd = prefix().get_recdes(logicnum);
  for (int i = 0; i < rd.NFields; i++)
  {
    TToken_string row;
    const RecFieldDes& fd = rd.Fd[i];
    row = fd.Name; // Nome
    row.add(i);    // Indice
    TString16 keys;  
    for (int k = 0; k < rd.NKeys; k++)
    {
      const KeyDes& kd = rd.Ky[k];
      for (int j = 0; j < kd.NkFields; j++)
      {
        const int n = kd.FieldSeq[j] % MaxFields;
        if (n == i)
          keys << (k+1) << ' '; // Chiavi di appartenenza
      }
    }
    row.add(keys);
    if (!keys_only || keys.full())
      flds.add(row);
  }
  flds.TArray::sort(sort_fields);
  return flds.items();
}

bool TEdit_file::browse_cursor(TCursor& cursor, const TFilename& fname)
{
  TRectype& curr = cursor.curr();
  TRectype svrec(cursor.curr());
  
  TToken_string head(256);
  TToken_string trac(256);
  
  trac.add("");
  head.add("@1");

  TString_array flds;
  fill_fields(curr.num(), flds);

  FOR_EACH_ARRAY_ROW(flds, i, row)
  {
    const TString16 n = row->get(0);
    trac.add(n);
    
    TString16 name(n); name.lower(); name[0] = toupper(name[0]);
    head.add(name);
    
    const TFieldtypes t = curr.type(n);
    int len = 0;
    switch (t)
    {
    case _datefld: len = 10; break;
    case _memofld: len = 50; break;
    default      : len = curr.length(n); break;
    }  
    head << '@' << max(len, (int)strlen(n));
    if (t == _realfld || t == _intfld || t == _longfld || t == _wordfld || t == _realfld) 
      head << 'R';
  }

  const bool superprassi = user() == ::dongle().administrator();
  
  TCursor_sheet sheet(&cursor, trac, fname, head, superprassi ? 6 : 0);
  KEY ch;
  while ((ch = sheet.run()) != K_ESC)
  {     
    if (!superprassi)
    {            
      error_box(FR("Funzione non ammessa per l'utente %s"), (const char*)user());
      ch = K_ESC;
    }
    
    if (ch == K_DEL && sheet.one_checked())
    {
      const long checked = sheet.checked();
      if (yesno_box(FR("Si desidera cancellare i %ld record indicati?"), checked))
      {
        const long items   = sheet.items();
        cursor.freeze(TRUE);
        TProgind pi(items, TR("Attendere..."), true, true);
        for (TRecnotype j = 0; j < items; j++)
        {
          if (!pi.setstatus(j))
            break;
          if (sheet.checked(j))
          {
            cursor = j;
            cursor.file().remove();
            sheet.uncheck(j);
          }
        }
        cursor.freeze(FALSE);
      }
      
      // Forza update del cursore
      cursor.set_filterfunction(NULL, TRUE);
      if (cursor.items() > 0)
        sheet.select(1);
      continue;
    }
    if (ch == K_ENTER || ch == K_DEL) 
    { 
      if (cursor.items() > 0)
        cursor = sheet.selected();
    }  
    
    switch(ch)
    {
    case K_ENTER:  // edit fields
      edit_record(curr);
      break;
    case K_DEL:    // delete record
      if (yesno_box(TR("Confermare la cancellazione del record")))
        cursor.file().remove();
      break;
    case K_INS:    // insert new record
      curr.zero();
      edit_record(curr);
      break;
    default:
      break;  
    }
    sheet.force_update();
  }
  return ch != K_ESC;
}

bool TEdit_file::browse_file(int logicnum, const TFilename& name, const TString& tab)
{
  if (logicnum >= LF_USER)
  {
    TSystemisamfile test(logicnum);
    int err = test.open_ex();
    if (err != NOERR)
      return error_box(FR("Impossibile aprire il file %d: errore %d"), logicnum, err);
    test.close();
  }

  TRelation relation(logicnum);
  TCursor cursor(&relation);
  if (logicnum == LF_TAB || logicnum == LF_TABCOM || logicnum == LF_TABGEN) 
  {
    if (tab.len() == 3)  // Filtra la tabella interessata
    {
      TRectype filter(logicnum);
      filter.put("COD", tab);
      cursor.setregion(filter, filter);
    }  
  }  
  return browse_cursor(cursor, name);
}

bool TEdit_file::browse_file(TExternisamfile* file, const TFilename& name)
{
  TRelation relation(file);
  TCursor cursor(&relation);
  return browse_cursor(cursor, name);
}

class TEdit_mask : public TMask
{
public:
  TEdit_mask(int pages);
};

TEdit_mask::TEdit_mask(int pages) 
          : TMask(TR("Modifica record"), pages, 0, 0, 0, 0)
{ 
  add_button_tool(DLG_OK, TR("Registra"), BMP_SAVEREC).set_exit_key(K_ENTER);
  add_button_tool(DLG_DELREC, TR("Elimina"), BMP_DELREC).set_exit_key(K_DEL);
  add_button_tool(DLG_CANCEL, TR("Annulla"), BMP_CANCEL).set_exit_key(K_ESC);
  if (pages > 1) 
    set_default_tab_buttons();
}

void TEdit_file::edit_record(TRectype& rec)
{
  TString_array flds;
  const int recs = fill_fields(rec.num(), flds);
  const int fpp = 18; // Fields per page
  const int pages = (recs+fpp-1)/fpp;
  
  TEdit_mask m(pages);
  m.enable(DLG_DELREC, !rec.empty());
  
  int curpage = 0, currow = 1;
  short nid = 100;
  bool grouped = false;
  
  FOR_EACH_ARRAY_ROW(flds, i, row)
  {
    // add fields ONE by ONE
    const TString16 cp = row->get(0);
    TString s = cp; s.left_just(16);
    if (!grouped)
    {
      const bool primary = row->get_char(2) == '1';
      if (primary)
        s.insert("@b");
      else
      {
        m.add_groupbox(-1, curpage, PR("@bChiave primaria"), 2, 0, 70, ++currow);
        grouped = true;
      }
    }

    const int len = rec.length(cp);
    switch (rec.type(cp))
    {
    case _alfafld:
      m.add_string(nid++,curpage, s, 3, currow, len, "_", len > 50 ? 50 : len);
      break;
    case _intfld:
    case _longfld:
    case _wordfld:
    case _realfld:
      m.add_number(nid++, curpage, s, 3, currow, len, "", rec.ndec(cp));
      break;
    case _datefld:
      m.add_date(nid++, curpage, s, 3, currow, "");
      break;
    case _charfld:
      m.add_string(nid++, curpage, s, 3, currow, 1, "");
      break;
    case _boolfld:
      m.add_boolean(nid++, curpage, s, 3, currow, "");
      break;
    case _intzerofld:
    case _longzerofld:                                 
      m.add_number(nid++, curpage, s, 3, currow, len, "Z", 0);
      break;
    case _memofld:
      m.add_zoom( nid++, curpage, s, 3, currow, 50, "", 50 );
      break;
    default:
      break;
    }
    m.field(nid-1).set(rec.get(cp));
    
    if (((i+1) % fpp) == 0 || i == recs-1) 
    {
      curpage++; 
      currow = 1;
    }
    else 
      currow++;
  }

  const KEY k = m.run();
  if (k != K_ESC && k != K_QUIT)
  {
    TLocalisamfile file(rec.num());
    switch (k)
    {
    case K_ENTER:
      {
        const TRectype svrec(rec); // Salva record originale
        const TString svkey = svrec.key(1);
        FOR_EACH_ARRAY_ROW(flds, j, frow)
        {
          const char* cp = frow->get(0);
          rec.put(cp, m.get(100+j));
        }
        if (svkey != rec.key(1))
        {
          const int err = rec.write(file);
          if (err == NOERR && !svrec.empty())
            svrec.remove(file);
        }
        else
          rec.rewrite(file);
      }
      break;
    case K_DEL:
      if (!rec.empty() && yesno_box(TR("Confermare la cancellazione del record")))
        rec.remove(file);
      break;
    default:
      break;
    }
  }
}