#include <xvt.h>
#include <variant.h>

///////////////////////////////////////////////////////////
// TVariant
///////////////////////////////////////////////////////////

const TVariant NULL_VARIANT;

void TVariant::set_null()
{
  if (_ptr != NULL)
  {
    switch (_type) 
    {
    case _alfafld: delete (TString*)_ptr; break;
    case _realfld: delete (real*)_ptr; break;
    default      : break;
    }
    _ptr = NULL;
  }
  _type = _nullfld;
}

void TVariant::set(const char* str)
{
  if (str != NULL)
  {
    if (_type == _alfafld)
      *((TString*)_ptr) = str;
    else
    {
      set_null();
      _type = _alfafld;
      _ptr = new TString(str);
    }
  }
  else
    set_null();
}

void TVariant::set(const real& r)
{
  if (_type == _realfld)
    *((real*)_ptr) = r;
  else
  {
    set_null();
    _type = _realfld;
    _ptr = new real(r);
  }
}

void TVariant::set(const TDate& d)
{
  if (_type != _datefld)
    set_null();
  _type = _datefld;
  _ptr = (void*)d.date2ansi();
}

void TVariant::set(const long n)
{
  if (_type != _longfld)
    set_null();
  _type = _longfld;
  _ptr = (void*)n;
}

bool TVariant::is_zero() const
{
  switch (_type)
  {
  case _datefld: 
  case _longfld: return _ptr == NULL;
  case _realfld: return as_real().is_zero();
  case _alfafld: return real::is_null(as_string());
  default: break;
  }
  return true;
}

bool TVariant::is_empty() const
{
  if (_type == _alfafld)
    return as_string().empty();
  return is_zero();
}

TDate TVariant::as_date() const
{
  if (_type == _alfafld)
    return TDate(as_string());  
  return TDate(as_int());
}

long TVariant::as_int() const
{
  long n = 0;
  switch(_type)
  {
  case _datefld: 
  case _longfld: n = (long)_ptr; break;
  case _realfld: n = as_real().integer(); break;
  case _alfafld: 
    {
      const TString& str = as_string();
      if (str[0] == '#')  
        sscanf(str, "#%X", &n);
      else
        n = atoi(str); 
    }
    break;
  default : break;
  }
  return n;
}

bool TVariant::as_bool() const
{
  bool ok = false;
  switch (_type)
  {
  case _nullfld:
    break;
  case _alfafld:
    {
      const TString& str = as_string();
      ok = str.full() && strchr("1TVXY", str[0]) != NULL;
    }
    break;
  default:
    ok = as_int() != 0;
    break;
  }
  return ok;
}

COLOR TVariant::as_color() const
{
  unsigned long rgb = as_int();
  unsigned char r = XVT_COLOR_GET_RED(rgb);
  unsigned char g = XVT_COLOR_GET_GREEN(rgb);
  unsigned char b = XVT_COLOR_GET_BLUE(rgb);
  return XVT_MAKE_COLOR(r, g, b);
}

real TVariant::as_real() const
{
  if (_type == _realfld)
    return *(real*)_ptr;
  switch(_type)
  {
  case _alfafld: return real(as_string()); break;
  case _longfld: return real(as_int()); break;
  default : break;
  }
  return ZERO;
}

bool TVariant::as_string(TString& tmp) const
{
  tmp.cut(0);
  switch(_type)
  {
  case _alfafld: tmp = *(TString*)_ptr;    break;
  case _datefld: tmp = as_date().string(); break;
  case _longfld: tmp << as_int();          break;
  case _realfld: tmp = as_real().string(); break;
  default: break;
  }
  return !is_null();
}

const TString& TVariant::as_string() const
{
  if (_type == _alfafld)
    return *(TString*)_ptr; else
  if (_type == _nullfld)
    return EMPTY_STRING;
  TString& tmp = get_tmp_string();
  as_string(tmp);
  return tmp;
}

void TVariant::convert_to(TFieldtypes ft)
{
  if (_type != ft)
  {
    switch (ft)
    {
    case _nullfld    : set_null(); break;
    case _datefld    : set(as_date()); break;
    case _intfld     :
    case _intzerofld :
    case _longzerofld:
    case _wordfld    :
    case _longfld    : set(as_int()); break;
    case _boolfld    : set(as_bool()); break;
    case _realfld    : set(as_real()); break;
    default          : set(as_string()); break;
    }
  }
}

void TVariant::copy(const TVariant& var)
{
  switch (var._type)
  {
  case _datefld: set(var.as_date());   break;
  case _longfld: set(var.as_int());    break;
  case _realfld: set(var.as_real());   break;
  case _alfafld: set(var.as_string()); break;
  default      : set_null();           break;
  }
}

void TVariant::print_on(ostream& out) const
{
  out << as_string();
}

bool TVariant::is_kind_of(word cid) const
{
  return cid == CLASS_VARIANT || TSortable::is_kind_of(cid);
}

int TVariant::compare(const TSortable& s) const
{
  CHECK(s.is_kind_of(CLASS_VARIANT), "Illegal Variant comparison");
  const TVariant& var = (const TVariant&)s;
  int cmp = 0;
  switch (_type)
  {
  case _datefld: cmp = as_date() - var.as_date(); break;
  case _longfld: cmp = as_int() - var.as_int();   break;
  case _realfld: 
    {
      const real n = as_real() - var.as_real();
      cmp = n.sign(); 
    }
    break;
  case _alfafld: cmp = as_string().compare(var.as_string()); break;
  default : cmp = var.is_null() ? 0 : -1;
  }
  return cmp;
}

TVariant& TVariant::add(const TVariant& var)
{
  switch (_type)
  {
  case _datefld: set(as_date() + var.as_int()); break;
  case _longfld: set(as_int() + var.as_int()); break;
  case _alfafld: *(TString*)_ptr << var.as_string(); break;
  case _realfld: *(real*)_ptr += var.as_real(); break;
  default: copy(var); break;
  }
  return *this;
}

TVariant& TVariant::sub(const TVariant& var)
{
  switch (_type)
  {
  case _datefld: 
		if (var.type() == _datefld)
			set(as_date() - var.as_date());
		else
			set(as_date() - var.as_int()); 
		break;
  case _longfld:
    if (var.type() == _longfld)
    {
      set(as_int() - var.as_int());
      break;
    }
    // Fall down
  default:
    {
      real n = as_real();
      n -= var.as_real();
      set(n);
    }
    break;
  }
  return *this;
}

///////////////////////////////////////////////////////////
// TVariant_stack
///////////////////////////////////////////////////////////

TVariant& TVariant_stack::peek(int depth)
{
  const int sp = _sp-depth-1;
  return (sp >= 0 && sp < _var.items()) ? (TVariant&)_var[sp] : (TVariant &)NULL_VARIANT;
}

bool TVariant_stack::drop()
{
  if (_sp > 0)
  {
    _sp--;
    return true;
  }
  return false;
}

TVariant& TVariant_stack::pop()
{
  TVariant& var = peek(0);
  drop();
  return var;
}

void TVariant_stack::roll(int depth)
{
  const int sp = _sp-depth-1;
  if (sp >= 0)
  {
    TObject* var = _var.remove(sp, true);
    _var.insert(var, _sp-1);
  }
}

void TVariant_stack::push(const TVariant& var)
{
  if (_var.objptr(_sp) == NULL)
    _var.add(var, _sp);
  else
    (TVariant&)_var[_sp] = var;
  _sp++;
}

void TVariant_stack::push(long n)
{
  const TVariant var(n);
  push(var);
}

void TVariant_stack::push(const real& n)
{
  const TVariant var(n);
  push(var);
}

void TVariant_stack::push(const char* str)
{
  const TVariant var(str);
  push(var);
}

void TVariant_stack::reset()
{
  _sp = 0;
}

bool TVariant_stack::overflow() const
{
  return _sp > 2048;
}