#include <relation.h>
#include <varrec.h> 

TVariable_field::TVariable_field(
                const char * name,
                const char * expr,
                TTypeexp type,
                int len)
               : _rec(NULL), _name(name), _e(NULL), _put_string(NULL),
                 _getfunc(NULL), _in_get(FALSE),_lenght(len)
{                         
  if (expr && *expr)
  {
    _e = new TExpression(expr, type);
    if (TFixed_string(expr).find('-') < 0) 
      _put_string = new TToken_string(expr, '+');
  }   
}

TVariable_field::TVariable_field(
                const char * name,        //
                VIRTUAL_GET_FUNCTION getfunc, 
                int len) //  
               : _rec(NULL), _name(name), _e(NULL), _put_string(NULL),
                 _getfunc(getfunc), _in_get(FALSE), _lenght(len)
{
  CHECK(_getfunc, "You must pass a valid VIRTUAL_GET_FUNCTION");
}

TVariable_field::TVariable_field(
                const char * name,      //
                const TExpression & expr,      //
                TTypeexp type,
                int len)          //  
               : _rec(NULL), _name(name), _e(NULL), _put_string(NULL),
                 _getfunc(NULL), _in_get(FALSE),_lenght(len)
{                            
  _e = (TExpression *) expr.dup();
}

TVariable_field::TVariable_field(const TVariable_field & f)
               : _rec(f._rec), _name(f._name), _e(NULL), _put_string(NULL),
                 _getfunc(f._getfunc), _in_get(FALSE),_lenght(f._lenght)
{                
  if (f._e)
    _e = (TExpression *) f._e->dup();
  if (f._put_string)
    _put_string = new TToken_string(*f._put_string);
}

TVariable_field::~TVariable_field()
{
  if (_e) delete _e;
  if (_put_string) delete _put_string;
}   

TObject* TVariable_field::dup() const
{
  TVariable_field* o = new TVariable_field(*this);
  return o;
}    

TString & TVariable_field::get() const
{
  TString & v = (TString &) _value;
  if (dirty())
  {
    if (_in_get)
      fatal_box("Recursive get in %s", (const char *) _name);
    ((TVariable_field *) this)->_in_get = TRUE;
    if (_e)        
    {
      CHECK(_rec, "NULL Record pointer with an expression");
      _rec->set_variables(_e);
      v = _e->as_string();
    }
    else                 
      if (_getfunc)
      {
        CHECK(_rec, "NULL Record pointer with a function");
        v = _getfunc(*_rec);
      }
    ((TVariable_field *) this)->_in_get = FALSE;
    ((TVariable_field *) this)->set_clean();
  }
  return v;
}
            
void TVariable_field::put(const char * val)
{      
  if (!_getfunc)
  {
    if (_e == NULL)
      _value = val;
    else
    {
      if (_e->type() == _strexpr && _rec && _put_string)
      { 
        const TString work_val(val);
        int start = 0;
       
        for (const char * s = _put_string->get(0); s && *s; s = _put_string->get())
        {
          TFieldref f(s, 0);       
          const int len = f.len(*_rec);
          f = work_val.mid(start, len);
          start += len;
        }
      }
    }
  }
}
            
void TVariable_field::zero() 
{ 
  _value.cut(0);
}

////////////////////////////////////////////////////////////
//      TVariable_rectype
////////////////////////////////////////////////////////////

TVariable_rectype::TVariable_rectype(int logicnum)
         : TRectype(logicnum), _memo_fld_to_load(false)

  
{
}

TVariable_rectype::TVariable_rectype(const TBaseisamfile* i)
         : TRectype(i), _memo_fld_to_load(false)

{
}

TVariable_rectype::TVariable_rectype(const TRectype& r)
         : TRectype(r), _memo_fld_to_load(false)
{
}


TVariable_rectype::TVariable_rectype(const TVariable_rectype& r)
         : TRectype((const TRectype &) r), _memo_fld_to_load(false) 
{
  _virtual_fields = r._virtual_fields; 
  set_memo_fld(r._memo_fld);
}

void TVariable_rectype::unknown_field(const char* name) const 
{
  if (!auto_virtual_fields())
    TRectype::unknown_field(name);
}
                    
void TVariable_rectype::write_memo(TIsam_handle file, const TRecnotype recno)
{   
  if (_memo_fld.not_empty())
  {
    TToken_string t(256, '\n'); 
    for (TVariable_field * vf = (TVariable_field *) _virtual_fields.first_item(); vf;
         vf = (TVariable_field *) _virtual_fields.succ_item()) 
    {              
      const TString& val = vf->get();
      if (val.full() && val != "0")
      {
        t.add(vf->name());
        t << "=" << val;
      }
    }         
    put(_memo_fld, t);
  }
  TRectype::write_memo(file, recno);
}

void TVariable_rectype::add_field(TVariable_field * f)
{                  
  CHECK(f, "Null field");
  CHECKS(!TRectype::exist(f->name()), "Field already exist ", f->name());
  f->set_rec(this);
  _virtual_fields.add(f->name(), f, TRUE);
}                                                              

void TVariable_rectype::remove_field(const char * fieldname)
{ 
  if (fieldname)
  {                      
    if (_virtual_fields.is_key(fieldname))
      _virtual_fields.remove(fieldname);
  }
  else 
    _virtual_fields.destroy();
}                                   

void TVariable_rectype::set_variables(TExpression * e) const
{ 
  const int items = e->numvar();
  for  (int i = 0; i < items; i++)
    e->setvar(i, get(e->varname(i)));
}  

void TVariable_rectype::set_memo_fld( const char * fieldname)
{                                                       
  if (fieldname && *fieldname && type(fieldname) == _memofld)
    _memo_fld = fieldname;
  else 
    _memo_fld.cut(0);
}                         

void TVariable_rectype::init_memo(TRecnotype recno, TIsam_handle file)
{  
  TRectype::init_memo(recno, file); 
  if (recno != RECORD_NON_FISICO)
    _memo_fld_to_load = _memo_fld.not_empty();
}

TObject* TVariable_rectype::dup() const
{
  TVariable_rectype* o = new TVariable_rectype(*this);
  return o;
}

TFieldtypes TVariable_rectype::type(const char* fieldname) const
{
  if (_virtual_fields.objptr(fieldname))
    return _alfafld; 
  return TRectype::type(fieldname);
}


int TVariable_rectype::length(const char* fieldname) const
{
  if (_virtual_fields.objptr(fieldname))
    return  ((TVariable_field *)_virtual_fields.objptr(fieldname))->lenght();
  return TRectype::length(fieldname);
}


int TVariable_rectype::ndec(const char* fieldname) const
{
  if (_virtual_fields.objptr(fieldname))
    return 0; 
  return TRectype::ndec(fieldname);
}


bool TVariable_rectype::exist(const char* fieldname) const
{                   
  if (_virtual_fields.is_key(fieldname))
    return true;
  return TRectype::exist(fieldname);
}

const TString & TVariable_rectype::get_str(const char* fieldname) const
{   
  TVariable_field * f = (TVariable_field *) _virtual_fields.objptr(fieldname); 
  
  if (_memo_fld_to_load && f == NULL)
  {   
    ((TVariable_rectype *) this)->_memo_fld_to_load = false;
    TToken_string values(get(_memo_fld), '\n');
    for (const char * s = values.get(0); s && *s; s = values.get())
    {
      char * val = (char*)strchr(s, '='); //antica porcata
      if (val)
      {      
        *val = '\0';
        val++;
        if (*val)
        {
          TVariable_field * v = (TVariable_field *) _virtual_fields.objptr(s);
          
          if (v == NULL)
          { 
            v = new TVariable_field(s);
            ((TVariable_rectype *) this)->add_field(v);
          }
          v->force_value(val);
        }
      }
    }
    f = (TVariable_field *) _virtual_fields.objptr(fieldname);
  }
  if (f)  
    return f->get();
  else
    return TRectype::get_str(fieldname);
}

void TVariable_rectype::put_str(const char* fieldname, const char* val)
{                                            
  TVariable_field * f = (TVariable_field *) _virtual_fields.objptr(fieldname);

  if (f == NULL && auto_virtual_fields() && !TRectype::exist(fieldname))
  {
    _virtual_fields.add(fieldname, TVariable_field(fieldname));
    f = (TVariable_field *) _virtual_fields.objptr(fieldname);
  }
  if (f)
    f->put(val);
  else
    TRectype::put_str(fieldname, val);
}

void TVariable_rectype::zero(const char* fieldname)
  
{
  TVariable_field * f = (TVariable_field *) _virtual_fields.objptr(fieldname);
  if (f)
    f->zero();
  else
    TRectype::zero(fieldname);
}


HIDDEN void zero_virtual_field(const TObject & o)
{
  TVariable_field & v = (TVariable_field &) o;
  v.zero();
}                              

void TVariable_rectype::zero(char c)
{
  TRectype::zero(c);
  _virtual_fields.for_each(zero_virtual_field);
}


// Certified 99%
TVariable_rectype & TVariable_rectype::operator =(const TVariable_rectype & rec)
  
{
  TRectype::operator =(rec);
  _virtual_fields = rec._virtual_fields;
  for (TVariable_field * f = first_variable_field();
       f != NULL; f = succ_variable_field()) 
    f->set_rec(this);
  return *this;
}

// Certified 99%
TRectype & TVariable_rectype::operator =(const TRectype & rec)
  
{
  TRectype::operator =(rec);
  _virtual_fields.for_each(zero_virtual_field);
  return *this;
}

// Certified 99%
TRectype & TVariable_rectype::operator =(const char* rec)
{
  TRectype::operator =(rec);
  _virtual_fields.for_each(zero_virtual_field);
  return *this;
}

///////////////////////////////////////////////////////////
// TExtrectype
///////////////////////////////////////////////////////////

TExtrectype::TExtrectype(const TTrec& r) : TVariable_rectype(r.num())
  
{
  delete _rec;
  _length = r.len();
  _rec = new char [ _length ];
  _rd = r.rec();
  if(has_memo())
    init_memo(RECORD_NON_FISICO );
  zero();
}

TExtrectype::~TExtrectype()
{ }