#include <applicat.h>
#include <expr.h>
#include <msksheet.h>
#include <progind.h>
#include <relation.h>
#include <sheet.h>
#include <stack.h>
#include <tabutil.h>
#include <urldefid.h>
#include <viswin.h>

#include "ve4.h"
#include "ve4100.h"

///////////////////////////////////////////////////////////
// TTable_expression
///////////////////////////////////////////////////////////

class TTable_expression : public TExpression
{ 
  static TArray _expr;
  static const TRelation* _rel;
  static TViswin* _win;
  static bool _errors;
  
protected:
  virtual bool print_error(const char* msg) const;
  virtual int parse_user_func(const char* name, int nparms) const;
  virtual void evaluate_user_func(int index, int nparms,
                                  TEval_stack& stack, TTypeexp curtype) const;
  virtual bool user_func_dirty() const;                                

  bool error_box(const char* fmt, ...) const;
  const TRelation& get_relation() const { CHECK(_rel, "NULL relation"); return *_rel; }
  
  // Cerca il record dato il file->campo.  Usata da get_field e put_field.
  TRectype* field2rec(TString& var) const;
  // Converte un nome di funzione in indice
  int name2index(const char* name) const;

public:
  static void load_table(bool force = FALSE);
  static void set_relation(const TRelation* rel) { _rel = rel; load_table(); }
  static void set_window(TViswin* win) { _win = win; }
  static void reset_errors() { _errors = FALSE; }
  static bool errors_found() { return _errors; }

  int str2file(const TString& s) const;
  const char* get_field(const char* f) const;
  void set_field(const char* field, const char* val) const;
  
  TTable_expression() { }
  virtual ~TTable_expression() { }
};
  
TArray           TTable_expression::_expr;  
const TRelation* TTable_expression::_rel = NULL;  
TViswin*         TTable_expression::_win = NULL;  
bool             TTable_expression::_errors = FALSE;  
  
void TTable_expression::load_table(bool force)
{ 
  if (_expr.items() == 0 || force)
  {   
    TString n(15), e(127);
    TTable expr("%FRM");
    for (expr.first(); !expr.eof(); expr.next())
    {
      n = expr.get("CODTAB");
      e = expr.get("S1");
      e << expr.get("S2");
      const TTypeexp t = expr.get_bool("B0") ? _strexpr : _numexpr;
      
      TTable_expression* te = new TTable_expression;
      THash_object* o = new THash_object(n, te);
      const int index = _expr.add(o);
      if (!te->set(e, t))
      {
        te->error_box("La funzione %s contiene un simbolo errato: %s",
                      (const char*)n, te->last_token());
        te->set("", t);  
      }  
    }
  }
}

bool TTable_expression::user_func_dirty() const
{
  return _errors || TExpression::user_func_dirty();
}

bool TTable_expression::print_error(const char* msg) const
{   
  if (_win)            
  {
    TString s(256);
    s << "@b$[r,w]" << msg;
    _win->add_line(s);
  }  
  else
    ::error_box("%s", msg);
  return _errors = TRUE;  
}

bool TTable_expression::error_box(const char* fmt, ...) const
{
  char msg[256];
  va_list argptr;
  va_start(argptr,fmt);
  vsprintf(msg, fmt, argptr);
  va_end(argptr);
  return print_error(msg);
}

int TTable_expression::str2file(const TString& str) const
{
  HIDDEN const char* const s = "AUDCR";
  HIDDEN const int logicnum[] = { LF_ANAMAG, LF_UMART, LF_DESLIN, LF_CONDV, LF_RCONDV };
  const char* p = strchr(s, toupper(str[0]));
  const int ln = p != NULL ? logicnum[p-s] : -1;
  return ln;
} 

TRectype* TTable_expression::field2rec(TString& var) const
{
  int logicnum = 0;
  var.upper();
  const int arrow = var.find("->");
  if (arrow > 0)
  {
    logicnum = str2file(var);
    var = var.mid(arrow+2);
  }  
  
  TRectype* rec = NULL;
  if (logicnum >= 0 && get_relation().exist(logicnum))
    rec = &get_relation().curr(logicnum);
  else
    error_box("Il campo '%s' non appartiene alla relazione.", (const char*)var);
  return rec;  
}

const char* TTable_expression::get_field(const char* f) const
{
  TString var(f);
  const TRectype* rec = field2rec(var);
  const char* val = "";
  if (rec != NULL)
  {              
    if (rec->exist(var))
      val = rec->get(var);
    else
      error_box("Il campo '%s' non appartiene al file %d.", (const char*)var, rec->num());
  }  
  return val;  
} 

void TTable_expression::set_field(const char* f, const char* v) const
{
  TString var(f);
  TRectype* rec = field2rec(var);
  if (rec != NULL)
    rec->put(var, v);
} 

int TTable_expression::name2index(const char* name) const
{                               
  if (_expr.items() == 0)
    load_table();
  
  TString code(name); code.upper();
  for (int index = _expr.items()-1; index >= 0; index--)
  {
    const THash_object& h = (const THash_object&)_expr[index];
    if (h.key() == code)
      break;
  }            
  
  if (index < 0)
  {
    TTable expr("%FRM");
    expr.put("CODTAB", code);
    if (expr.read() == NOERR)
    {
      TString e(127);
      e = expr.get("S1"); e << expr.get("S2");
      const TTypeexp t = expr.get_bool("B0") ? _numexpr : _strexpr;
      TTable_expression* te = new TTable_expression;
      if (te->set(e, t))
      {
        THash_object* o = new THash_object(code, te);
        index = _expr.add(o);
      }
      else
      {
        delete te;
        index = -2;
      }  
    }  
  }   
  return index;
}

int TTable_expression::parse_user_func(const char* name, int nparms) const
{                 
  const int index = name2index(name);
  switch(index)
  {
  case -1:
    error_box("Funzione non riconosciuta: %s.", name);
    break;
  case -2:
    error_box("La funzione %s contiene un simbolo errato: %s.",
              name, last_token());
    break;
  default:
    break;                
  }
  return index;
}

void TTable_expression::evaluate_user_func(int index, int nparms, 
                                           TEval_stack& stack, TTypeexp curtype) const
{                        
  THash_object* ho = (THash_object*)_expr.objptr(index);
  TTable_expression& expr = (TTable_expression&)ho->obj();
  
  // Assegna i valori a tutte le variabili dell'espressione
  for (int v = expr.numvar()-1; v >= 0; v--)
  {
    const char* var = expr.varname(v);
    if (var[0] == '#')  // Parametro della funzione
    {
      const int n = atoi(var+1);
      if (n > 0 && n <= nparms)
      {
        const TObject& o = stack.peek(nparms-n);
        if (o.class_id() == CLASS_STRING)
          expr.setvar(var, (const TString&)o);
        else  
          expr.setvar(var, (const real&)o);
      }
      else
        error_box("Parametro non valido: %s", (const char*)var);
    }
    else
    { 
      const char* val = get_field(var);
      if (curtype == _numexpr)                             
        expr.setvar(var, real(val));
      else
        expr.setvar(var, val);
    }
  }
  
  // Toglie i parametri dallo stack
  for (int p = nparms; p > 0; p--)
    stack.pop();
  
  // Mette il risultato sullo stack
  if (curtype == _numexpr)
    stack.push((const real&)expr);
  else  
    stack.push((const TString&)expr);
}

///////////////////////////////////////////////////////////
// Maschera ricalcolo di magazzino
///////////////////////////////////////////////////////////

class TRicalcolo_mask : public TMask
{              
  TArray _rel, _des;
  int _cur_file;  
  TToken_string _key_expr;
  
  TRelation* _relation; 
  TCursor* _cursor;
  
protected:  
  bool test_formula(TMask_field& f) const;

  static bool file_handler   (TMask_field& f, KEY k);
  static bool key_handler    (TMask_field& f, KEY k);
  static bool filter_handler (TMask_field& f, KEY k);
  static bool field_handler  (TMask_field& f, KEY k);
  static bool fromto_handler (TMask_field& f, KEY k);
  static bool formula_handler(TMask_field& f, KEY k);
  
  bool gestione_um(char tipo) const;
  TRelation_description& get_des() const;
  TRelation&      get_rel() const;
  const TRectype& get_rec() const;
  const RecDes*   get_rec_des() const;
  TToken_string&  get_key_expr(int k);     
  
  TRelation& create_relation();
  TCursor& create_cursor();

public:
  bool elabora();
  
  TRicalcolo_mask();
  virtual ~TRicalcolo_mask() { }
};

TRicalcolo_mask::TRicalcolo_mask()
               : TMask("ve4100"), _cur_file(0), _relation(NULL), _cursor(NULL)
{
  const int lf[] = { LF_ANAMAG, LF_UMART, LF_DESLIN, LF_CONDV, LF_RCONDV, 0 };
  for (int i = 0; lf[i]; i++)                   
  {                                   
    TRelation* rel = new TRelation(lf[i]);
    _rel.add(rel);
    _des.add(new TRelation_description(*rel));
  }     
  
  set_handler(F_FILE, file_handler);
  set_handler(F_KEY, key_handler);
  set_handler(F_FILTER, filter_handler);
  
  TSheet_field& fields = (TSheet_field&)field(F_FIELDS);
  TMask& fsm = fields.sheet_mask();
  fsm.set_handler(F_FIELD, field_handler);
  fsm.set_handler(F_FROM, fromto_handler);
  fsm.set_handler(F_TO, fromto_handler);
  
  TSheet_field& outputs = (TSheet_field&)field(F_OUTPUTS);
  TMask& osm = outputs.sheet_mask();
  osm.set_handler(F_FIELD, field_handler);
  osm.set_handler(F_FORMULA, formula_handler);
  outputs.row(-1);    // Aggiunge comunque una riga vuota;
  osm.force_update();
} 

TRelation_description& TRicalcolo_mask::get_des() const
{
  return (TRelation_description&)_des[_cur_file];
}

TRelation& TRicalcolo_mask::get_rel() const
{
  return (TRelation&)_rel[_cur_file];
}

const TRectype& TRicalcolo_mask::get_rec() const
{
  return get_rel().lfile().curr();
}

const RecDes* TRicalcolo_mask::get_rec_des() const
{     
  return get_rec().rec_des();
}

TToken_string& TRicalcolo_mask::get_key_expr(int key)
{    
  _key_expr.cut(0);
  if (key > 0)
  {
    const RecDes* rd = get_rec_des();
    const KeyDes& kd = rd->Ky[key-1];
    for (int i = 0; i < kd.NkFields; i++)
    {
      const int nf = kd.FieldSeq[i] % MaxFields;
      const RecFieldDes& rf = rd->Fd[nf];  
      _key_expr.add(rf.Name);
    }
  }  
  return _key_expr;
}

bool TRicalcolo_mask::gestione_um(char tipo) const
{
  const char* const lco = "LCO";
  for (int i = 0; i < 3 && toupper(tipo) != lco[i]; i++);
  CHECK(i < 3, "Gestione unita' di misura errata");
  TConfig ve(CONFIG_DITTA, "ve");
  return ve.get_bool("GESUM", NULL, i+1);
}

TRelation& TRicalcolo_mask::create_relation()
{
  const int logicnum = get_rec().num();   // Numero logico del file principale
  if (_relation) 
    delete _relation;
  _relation = new TRelation(logicnum);
  
  const TString& unita_misura = get(F_UNITA_MISURA);
  const TString& lingua       = get(F_LINGUA);
  
  const TString& tipo_lco     = get(F_TIPO_LCO);  // Listini, contratti, offerte
  const TString& cat_ven      = get(F_CAT_VEN);
  const TString& tipo_cf      = get(F_TIPO_CF);
  const TString& codice_cf    = get(F_CODICE_CF);
  const TString& codice_lco   = get(F_CODICE_LCO);
  
  TString condv_expr;
  if (cat_ven.not_empty() && codice_cf.not_empty() && codice_lco.not_empty())
  {
    condv_expr << "TIPO=\""      << tipo_lco << '"'; 
    condv_expr << "|CATVEN=\""   << cat_ven << '"';
    condv_expr << "|TIPOCF=\""   << tipo_cf << '"';
    condv_expr << "|CODCF=\""    << codice_cf << '"';
    condv_expr << "|COD=\""      << codice_lco << '"';
  }
  
  TString expr(48); 
  
  switch (logicnum)
  {       
  case LF_ANAMAG:
    {
      expr = "CODART=CODART";
      if (unita_misura.not_empty())
      {
        expr << "|UM=\"" << unita_misura << '"'; 
        _relation->add(LF_UMART, expr, 2);
      }
      else  
        _relation->add(LF_UMART, expr, 1);

      if (lingua.not_empty())
      {
        expr = "CODART=CODART";
        expr << "|CODLIN=\"" << lingua << '"'; 
        _relation->add(LF_DESLIN, expr, 2);
      }  
      
      if (condv_expr.not_empty())
      {
        _relation->add(LF_CONDV, condv_expr, 1);
        
        if (unita_misura.not_empty())
        {
          expr = condv_expr;
          expr << "|TIPORIGA=\"A\"";
          expr << "|CODRIGA=CODART";
          expr << "|UM=\"" << unita_misura << '"';
          _relation->add(LF_RCONDV, expr, 1);
        }
      }
    }  
    break;   
  case LF_UMART:
    {
      _relation->add(LF_ANAMAG, "CODART=CODART", 1);

      if (lingua.not_empty())
      {
        expr = "CODART=CODART";
        expr << "|CODLIN=\"" << lingua << '"'; 
        _relation->add(LF_DESLIN, expr, 2);
      }  
      
      if (condv_expr.not_empty())
      {
        _relation->add(LF_CONDV, condv_expr, 1);
        expr = condv_expr;
        expr << "|TIPORIGA=\"A\"";
        expr << "|CODRIGA=CODART";
        expr << "|UM=UM";
        _relation->add(LF_RCONDV, expr, 1);
      }
    }  
    break;   
  case LF_DESLIN:
    {
      _relation->add(LF_ANAMAG, "CODART=CODART", 1);

      expr = "CODART=CODART";
      if (unita_misura.not_empty())
      {
        expr << "|UM=\"" << unita_misura << '"'; 
        _relation->add(LF_UMART, expr, 2);
      }
      else  
        _relation->add(LF_UMART, expr, 1);
      
      if (condv_expr.not_empty())
      {
        _relation->add(LF_CONDV, condv_expr, 1);

        if (unita_misura.not_empty())
        {                            
          expr = condv_expr;
          expr << "|TIPORIGA=\"A\"";
          expr << "|CODRIGA=CODART";
          expr << "|UM=\"" << unita_misura << '"';
        }
      }
    }  
    break;   
  case LF_RCONDV:
    {
      _relation->add(LF_ANAMAG, "CODART=CODRIGA", 1);
      
      if (gestione_um(tipo_lco[0]))
        _relation->add(LF_UMART, "CODART=CODRIGA|UM=UM", 2);

      if (lingua.not_empty())
      {
        expr = "CODART=CODRIGA";
        expr << "|CODLIN=\"" << lingua << '"'; 
        _relation->add(LF_DESLIN, expr, 2);
      }  
      
      _relation->add(LF_RCONDV, "TIPO=TIPO|CATVEN=CATVEN|TIPOCF=TIPOCF|CODCF=CODCF|COD=COD", 1);    
    }
    break;
  default:
    break;
  }
  return *_relation;
}

TCursor& TRicalcolo_mask::create_cursor()
{
  TString filter = get(F_FILTER); filter.strip("\n");
    
  int key = get_int(F_KEY);
    
  TRectype start(get_rec()), stop(get_rec());
  start.zero(); stop.zero(); 
    
  TToken_string& key_des = get_key_expr(key);
    
  TSheet_field& fs = (TSheet_field&)field(F_FIELDS);
  TString16 field;
  TString val;
  
  // I primi campi corrispondono alla chiave selezionata?
  bool key_seq_from = key > 0;  
  bool key_seq_to = key > 0;
  
  for (int r = 0; r < fs.items(); r++)
  { 
    TToken_string& row = fs.row(r);
    field = row.get(0);
    if (field.not_empty())
    {
      val = row.get();           // Limite inferiore
      if (!val.blank())
      {
        if (key_seq_from && key_des.get_pos(field) == r) 
          start.put(field, val);
        else
        {
          key_seq_from = FALSE;
          if (filter.not_empty()) filter << "&&";
          filter << '(' << field << ">=\"" <<  val << "\")";
        }  
      }
      else
        key_seq_from = FALSE;  

      val = row.get();           // Limite superiore
      if (!val.blank())
      {
        if (key_seq_to && key_des.get_pos(field) == r) 
          stop.put(field, val);
        else
        {
          key_seq_to = FALSE;
          if (filter.not_empty()) filter << "&&";
          filter << '(' << field << "<=\"" <<  val << "\")";
        }  
      }
      else
        key_seq_to = FALSE;  
    }  
    else 
      key_seq_from = key_seq_to = FALSE;
  }
    
  if (key <= 0) 
    key = 1;   
  
  if (_cursor)
    delete _cursor;
  _cursor = new TCursor(&create_relation(), filter, key, &start, &stop);
  return *_cursor;
}

bool TRicalcolo_mask::file_handler(TMask_field& f, KEY k)
{
  TRicalcolo_mask& m = (TRicalcolo_mask&)f.mask();
  if (k == K_SPACE && m.is_running())
  {
    const int n = atoi(f.get());
    if (n != m._cur_file)
    {
      m._cur_file = n;
      const RecDes* rd = m.get_rec_des();
      const int keys = rd->NKeys;

      TToken_string codes(16);
      TToken_string values(80);
      TString16 tmp;
      codes.add(0);
      values.add("Nessuna");
      for (int k = 1; k <= keys; k++)
      { 
        codes.add(k);
        tmp.format("Chiave %d", k);
        values.add(tmp);
      }

      TList_field& lk = (TList_field&)m.field(F_KEY);
      lk.replace_items(codes, values);
    }  
  }
  return TRUE;
} 

bool TRicalcolo_mask::key_handler(TMask_field& f, KEY k)
{             
  if (k == K_SPACE)
  {                           
    TRicalcolo_mask& m = (TRicalcolo_mask&)f.mask();
    TSheet_field& sheet = (TSheet_field&)m.field(F_FIELDS);
    if (m.is_running() || sheet.items() == 0)
    {
      const int cur_key = atoi(f.get());
      TToken_string& key_expr = m.get_key_expr(cur_key);
      const bool empty = key_expr.empty_items();
      int first_enabled = 0;
    
      TString_array& sa = sheet.rows_array();
      sa.destroy();                     
      if (empty)
      {
        for (int i = 4; i >= 0; i--)
          sa.add("");
      }  
      else
      {  
        TRelation_description& des = m.get_des();
        for (TString field = key_expr.get(0); !field.blank(); field = key_expr.get())
        {
          const int r = sa.add(field);
          sa.row(r).add(des.get_field_description(field), F_DESCR-F_FIELD);
        }  
        if (des.file_num() == LF_CONDV || des.file_num() == LF_RCONDV)
        {
          first_enabled = 4;
          TToken_string& row = sa.row(0);
          const TString& lco = m.get(F_TIPO_LCO);
          row.add(lco, 1);
          row.add(lco, 2);
        }  
      } 
      
      for (int r = sa.items()-1; r >= 0; r--)
        sheet.enable_cell(0, -1, r >= first_enabled);   
      
      sheet.force_update();
    }  
  }
  return TRUE;
}

bool TRicalcolo_mask::test_formula(TMask_field& f) const
{
  TString filter(f.get()); filter.strip("\n");
  TTable_expression expr;
  bool ok = expr.set(filter, _strexpr);
  if (ok)
  {    
    TString var;
    for (int v = expr.numvar()-1; v >= 0; v--)
    {
      int logicnum = 0;
      var = expr.varname(v); var.upper();
      const int arrow = var.find("->");
      if (arrow >= 0)
      {
        logicnum = expr.str2file(var);
        var = var.mid(arrow+2);
      }             
      if (logicnum >= 0)
      {
        if (logicnum == 0)
          ok = get_rec().exist(var);
        else
        {  
          TLocalisamfile lf(logicnum);
          ok = lf.curr().exist(var);
        }  
      }
      else
        ok = FALSE;  
      if (!ok)
      {       
        f.error_box("Il campo '%s' non esiste!", (const char*)expr.varname(v));
        break;
      }       
    }
  }
  else
    f.error_box("Simbolo non riconosciuto: '%s'", expr.last_token());
  return ok;  
}

bool TRicalcolo_mask::filter_handler(TMask_field& f, KEY k)
{  
  bool ok = TRUE;
  if (f.to_check(k))
  { 
    TRicalcolo_mask& m = (TRicalcolo_mask&)f.mask();
    ok = m.test_formula(f);
  }  
  return ok;
}

bool TRicalcolo_mask::field_handler(TMask_field& f, KEY k)
{     
  bool ok = TRUE;      
  switch (k)
  {       
  case K_TAB:
  case K_ENTER:
    if (!f.empty() && f.to_check(k))
    {                         
      const TSheet_field& s = *f.mask().get_sheet();
      TRicalcolo_mask& rm = (TRicalcolo_mask&)s.mask();
      const TRectype& rec = rm.get_rec();        
      const TString16 field = f.get();
      if (!rec.exist(field))
        ok = f.error_box("Il campo '%s' non esiste.", (const char*)field);
      if (ok && s.dlg() == F_OUTPUTS)  
      {
        TToken_string& ke = rm.get_key_expr(1);
        if (ke.get_pos(field) >= 0)
          ok = f.error_box("Non e' possibile modificare un campo chiave.");
      }
    }
    break;
  case K_F9:
    {                                                      
      TMask& m = f.mask();
      TRicalcolo_mask& rm = (TRicalcolo_mask&)m.get_sheet()->mask();
      TRelation_description& rd = rm.get_des();
      if (rd.choose_field(f.get()))
      {
        f.set(rd.field_name());
        m.set(F_DESCR, rd.field_desc());
      }  
    }
    break;
  default:
    break;
  }
  return ok;
}

HIDDEN void swap_tokens(TToken_string& str, int p1, int p2)
{
  const TString16 s1 = str.get(p1);
  const TString16 s2 = str.get(p2);
  str.add(s1, p2);
  str.add(s2, p1);
}

bool TRicalcolo_mask::fromto_handler(TMask_field& f, KEY k)
{
  bool ok = TRUE;
  if (f.to_check(k))
  {                        
    const TMask& m = f.mask();
    const TString& field = m.get(F_FIELD);
    if (field.not_empty())
    {
      TRicalcolo_mask& rm = (TRicalcolo_mask&)m.get_sheet()->mask();
      const TRectype& rec = rm.get_rec();        
      const int max = rec.length(field);
      if (f.get().len() > max)
      {
        TString msg(32); msg = "Inserire ";
        if (max > 1) msg << max; else msg << "un";
        msg << " caratter" << (max > 1 ? 'i' : 'e') << " al massimo.";
        ok = f.error_box(msg); 
      }                   
    }  
  }
  else
  {
    if (k == K_F9)
    {             
      const TMask& m = f.mask();
      TSheet_field& s = *m.get_sheet();
      TRicalcolo_mask& rm = (TRicalcolo_mask&)s.mask();
      TRelation& rel = rm.get_rel();       
      TRectype& rec = rel.curr();
      TRelation_description& des = rm.get_des();
      
      const TString title = des.file_desc();
      
      TToken_string fields(256), head(256);
      TString tmp;
      for (int i = 0; i < rec.items(); i++)
      { 
        tmp = rec.fieldname(i);
        fields.add(tmp);  
        
        int lun = rec.length(tmp);
        bool right = FALSE;
        switch(rec.type(tmp))
        {          
        case _alfafld:
        case _memofld:            
          if (lun > 50) lun = 50;
          break;
        case _charfld:
        case _boolfld:
          break;       
        case _datefld:
          lun = 10;
          break;  
        default:
          right = TRUE;
          break;
        }
        tmp.lower(); tmp[0] = toupper(tmp[0]);
        if (lun > tmp.len())
        {
          tmp << '@' << lun;
          if (right) tmp << 'R';
        }  
        else
        {
          if (right)
            tmp << "@R";
        }    
        head.add(tmp);
      }        
      
      const int key = rm.get_int(F_KEY);
      if (key > 0)
      {
        TToken_string& key_des = rm.get_key_expr(key);
        int pos = 0;
        for (TString c = key_des.get(0); c.not_empty(); c = key_des.get(), pos++)
        {
          const int p = fields.get_pos(c);
          if (p != pos)
          {
            swap_tokens(fields, p, pos);
            swap_tokens(head, p, pos);
          }  
        }
      }
      
      TEdit_field& e = (TEdit_field&)f;
 
      TToken_string siblings;
      siblings.add(e.dlg());
      tmp = fields.get(0);
      tmp = des.get_field_description(tmp);
      if (tmp.len() > 30)
      {
        tmp.cut(27);
        tmp << "...";
      }  
      siblings.add(tmp);

      TCursor cur(&rel, "", key > 0 ? key : 1);
      TBrowse_sheet sheet(&cur, fields, title, head, 0, &e, siblings);
      if (sheet.run() == K_ENTER)
      {
        const TString16 field = s.row(s.selected()).get(0);
        if (field.not_empty())
        {                    
          const TRectype& rec = cur.curr();
          if (rec.exist(field))
          {
            cur = sheet.selected();
            f.set(rec.get(field));
          }  
        }  
      }
    }
  }  
  return ok;   
}

bool TRicalcolo_mask::formula_handler(TMask_field& f, KEY k)
{  
  bool ok = TRUE;
  if (f.to_check(k))
  { 
    TRicalcolo_mask& m = (TRicalcolo_mask&)f.mask().get_sheet()->mask();
    ok = m.test_formula(f);
  }  
  return ok;
}

///////////////////////////////////////////////////////////
// Ciclo principale di elaborazione
///////////////////////////////////////////////////////////

HIDDEN bool rewrite_cursor(TCursor& cur)
{
  bool ok = cur.lock(_lock) == NOERR;
  if (ok)
  {
    ok = cur.relation()->rewrite() == NOERR;   
    cur.lock(_unlock);
  }  
  if (!ok)
    error_box("Impossibile aggiornare il record %ld.", cur.pos());
  return ok;  
}

bool TRicalcolo_mask::elabora()
{
  TString16 var;                   // Nome di campo
  TString val(80);                 // Valore di campo
  TString line(128);               // Linea di testo

  TIndwin iw(48, "Inizializzazione ...", TRUE, FALSE, 48);
  begin_wait();

  TCursor& cur = create_cursor();  // Crea e filtra il cursore principale
  
  TSheet_field& osf = (TSheet_field&)field(F_OUTPUTS);
  TArray expr;
  for (int r = 0; r < osf.items(); r++)  
  {                         
    TToken_string& riga = osf.row(r);
    var = riga.get(0);              
    if (var.not_empty())
    {
      TTypeexp tipo = _strexpr;
      switch(cur.curr().type(var))
      {
        case _alfafld:
        case _datefld:
        case _memofld:
          tipo = _strexpr;
          break;
        default:  
          tipo = _numexpr;
          break;     
      }    
      line = riga.get(2);
      TTable_expression* e = new TTable_expression;
      e->set(line, tipo);
      expr.add(e);
    }
  }  
  TToken_string& key_expr = get_key_expr(cur.key());     
    
  TViswin vw(NULL, "Ricalcolo valori", FALSE, TRUE, FALSE, 0, 0, 0, 0, FALSE);

  iw.close_modal();
  end_wait();
  
  if (iw.iscancelled())           
    return FALSE;

  TTable_expression::set_relation(cur.relation());
  TTable_expression::set_window(&vw);
  TTable_expression::reset_errors();

  vw.maximize();
  vw.open_modal();
  
  bool ok = TRUE;
  for (cur = 0; cur.ok(); ++cur)           
  { 
    line.format("#@b%7ld@r - ", cur.pos()+1);
    const TRectype& curr = cur.curr();
    for (const char* f = key_expr.get(0); f; f = key_expr.get())
      line << curr.get(f) << ' ';
    vw.add_line(line);
    
    for (int r = 0; r < osf.items(); r++)  
    {
      var = osf.row(r).get(0);              
      if (var.not_empty())
      {
        TTable_expression& e = (TTable_expression&)expr[r];
        for (int v = e.numvar()-1; v >= 0; v--)
        {
          var = e.varname(v);
          val = e.get_field(var);
          if (e.type() == _numexpr)                             
            e.setvar(var, real(val));
          else
            e.setvar(var, val);
        }
        var = osf.row(r).get(0);
        val = e;
        
        if (e.errors_found())
          ok = FALSE;
        else  
          cur.curr().put(var, val);
  
        line.format("- %-10s = %s", (const char*)var, (const char*)val);  
        vw.add_line(line);
      }  
    }
  }
  
  TTable_expression::set_relation(NULL);
  TTable_expression::set_window(NULL);

  vw.close_print();
  vw.run();
  vw.close_modal();
  
  if (get_bool(F_SAVE))
  {      
    TString msg(80);
    if (!ok)
      msg = "Sono stati riscontrati degli errori di calcolo:\n";
    msg << "Si desidera aggiornare effettivamente l'archivio?";
    ok = yesno_box(msg);
    if (ok)
    {
      TTextfile& txt = vw.text();
      
      TProgind piw(txt.lines(), "Aggiornamento archivio", FALSE, TRUE, 48);
      
      long pos = 0;
      cur = pos;
      for (long r = 0; r < txt.lines(); r++)
      {   
        piw.addstatus(1);
        line = txt.line(r);
        if (sscanf(line, "#%ld", &pos) == 1)
        {                      
          pos--;  
          if (pos > 0)
            rewrite_cursor(cur);
          cur = pos;  // Avanza il cursore
        }
        else
        {
          if (sscanf(line, "- %s = %s", (char*)(const char*)var, (char*)(const char*)val) == 2)
            cur.curr().put(var, val);
        }
      }
      if (pos > 0)
        rewrite_cursor(cur);
    }
  }
  return TRUE;           
}    


///////////////////////////////////////////////////////////
// Ricalcolo di magazzino
///////////////////////////////////////////////////////////

class TRicalcola_application : public TApplication
{   
protected:
  virtual bool menu(MENU_TAG);
  virtual bool create();
  
public:
  TRicalcola_application() { }
  virtual ~TRicalcola_application() { }
};

bool TRicalcola_application::create()
{
  dispatch_e_menu(MENU_ITEM(1));
  return TRUE;
}

bool TRicalcola_application::menu(MENU_TAG)
{
  TRicalcolo_mask m;
  while (m.run() == K_ENTER)
    m.elabora();

  return FALSE;
}

int ve4100(int argc, char* argv[])
{              
  TRicalcola_application r;
  r.run(argc, argv, "Ricalcolo valori");
  return 0;
}
