#include <varrec.h> 

#ifndef __RELATION_H
#include <relation.h>
#endif

TVariable_field::TVariable_field(
                const char * name,      //
                const char * expr,      //
                TTypeexp type)          //  
               : _rec(NULL), _name(name), _e(NULL) , _getfunc(NULL),
                 _put_string(NULL), _destroy_expression(TRUE),
                 _in_get(FALSE)
{                         
  if (expr && *expr)
  {
    _e = new TExpression(expr, type);
    if (TFixed_string(expr).find('-') == -1) _put_string = new TToken_string(expr, '+');
  }   
}

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

TVariable_field::TVariable_field(
                const char * name,      //
                TExpression * expr,      //
                TTypeexp type)          //  
               : _rec(NULL), _name(name), _e(expr) , _getfunc(NULL),
                 _put_string(NULL), _destroy_expression(FALSE),
                 _in_get(FALSE)
{
}

TVariable_field::TVariable_field(const TVariable_field & f)
               : _rec(f._rec), _name(f._name), _e(NULL) , _getfunc(f._getfunc),
                 _put_string(NULL), _destroy_expression(f._destroy_expression),
                 _in_get(FALSE)
{                
  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 (_destroy_expression && _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");
      const int items = _e->numvar();
      for  (int i = 0; i < items; i++)
        _e->setvar(i, _rec->get(_e->varname(i)));
      v = *_e;
    }
    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; 
  _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(isdef * file, const TRecnotype recno)
{   
  if (_memo_fld.not_empty())
  {
    TToken_string t(256, '\n'); 
    TString s(80);
    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.not_empty())
      {
        s = vf->name();
        s << "=" << val;
        t.add(s);
      }
    }         
    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_memo_fld( const char * fieldname)
{                                                       
  if (fieldname && *fieldname)
  {                 
    CHECK(exist(fieldname), "Unknown memo field");
    CHECK(type(fieldname) == _memofld, "Incorrect field type");
    _memo_fld = fieldname;
  }
  else 
   _memo_fld.cut(0);
}                         

void TVariable_rectype::init_memo( const TRecnotype recno)
{  
  TRectype::init_memo(recno); 
  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 255; 
  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 = strchr(s, '=');
      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_fields(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_fields);
}


// 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_fields);
  return *this;
}

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

  