
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2016 Matthias Koefferlein

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#include "tlVariant.h"
#include "tlInternational.h"
#include "tlString.h"

#include "gsiDecl.h"

#include <string.h>
#include <limits>

namespace tl
{

// --------------------------------------------------------------------
//  Helper for converting int128 from and to string

#if defined(HAVE_64BIT_COORD)

template <>
bool test_extractor_impl (tl::Extractor &ex, __int128 &v)
{
  __int128 x = 0;
  bool neg = ex.test("-");
  while (*ex <= '9' && *ex >= '0') {
    x = x * 10 + __int128 (*ex - '0');
    ++ex;
  }
  v = neg ? -x : x;
  return true;
}

template <>
void extractor_impl (tl::Extractor &ex, __int128 &v)
{
  if (! test_extractor_impl (ex, v)) {
    ex.error (tl::to_string (QObject::tr ("Expected a value specification")));
  }
}

std::string to_string (__int128 v)
{
  if (v < 0) {
    return std::string ("-") + to_string (-v);
  }
 
  std::string res;
  do {
    res += '0' + char (v % __int128 (10));
    v /= __int128 (10);
  } while (v > 0);
  std::reverse (res.begin (), res.end ());
  return res;
}

#endif

// --------------------------------------------------------------------
//  Complex type registration

static std::map <std::string, const VariantUserClassBase *> s_user_type_by_name;

static std::string translate_class_name (const std::string &lc_clsname)
{
  //  Note: for pre-0.23 versions to be able to read PCell's generated by 0.23 and further
  //  (see #601), we need to use the old "complex type" names, specifically "layer" instead of "layerinfo".
  if (lc_clsname == "layerinfo") {
    return "layer";
  } else {
    return lc_clsname;
  }
}

//  TODO: this can be done in register_instance too
//  (if is_const and reference is false, enter the translated name into the 
//  laer name table). We can then get rid of this function.
void initialize_variant_class_table ()
{
  s_user_type_by_name.clear ();

  for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
    //  Note: for backward compatibility we use lower case names
    std::string lc = tl::to_lower_case (c->name ());
    std::string lc_trans = translate_class_name (lc);
    s_user_type_by_name.insert (std::make_pair (lc, c->var_cls (false, false)));
    if (lc != lc_trans) {
      s_user_type_by_name.insert (std::make_pair (lc_trans, c->var_cls (false, false)));
    }
  }
}

const VariantUserClassBase *find_cls_by_name (const std::string &name) 
{
  tl_assert (! s_user_type_by_name.empty ());

  std::map <std::string, const VariantUserClassBase *>::const_iterator s = s_user_type_by_name.find (tl::to_lower_case (name));
  if (s == s_user_type_by_name.end ()) {
    return 0;
  }

  return s->second;
}

// --------------------------------------------------------------------
//  Implementation of tl::VariantUserClassBase

struct VariantUserClassTableKey 
{
  VariantUserClassTableKey (const std::type_info &t, bool r, bool c)
    : type (&t), reference (r), is_const (c)
  {
  }

  bool operator< (const VariantUserClassTableKey &k) const
  {
    if (reference != k.reference) {
      return reference < k.reference;
    }
    if (is_const != k.is_const) {
      return is_const < k.is_const;
    }
    if (*type != *k.type) {
      return type->before (*k.type);
    }
    return false;
  }

  bool operator== (const VariantUserClassTableKey &k) const
  {
    return reference == k.reference && is_const == k.is_const && *type == *k.type;
  }

  const std::type_info *type;
  bool reference;
  bool is_const;
};

static std::map<VariantUserClassTableKey, const tl::VariantUserClassBase *> *sp_class_table = 0;

const tl::VariantUserClassBase *VariantUserClassBase::instance (const std::type_info &type, bool reference, bool is_const)
{
  tl_assert (sp_class_table != 0);
  std::map<VariantUserClassTableKey, const tl::VariantUserClassBase *>::const_iterator c = sp_class_table->find (VariantUserClassTableKey (type, reference, is_const));
  tl_assert (c != sp_class_table->end ());
  return c->second;
}

void VariantUserClassBase::register_instance (const tl::VariantUserClassBase *inst, const std::type_info &type, bool reference, bool is_const)
{
  if (! sp_class_table) {
    sp_class_table = new std::map<VariantUserClassTableKey, const tl::VariantUserClassBase *> ();
  }
  (*sp_class_table)[VariantUserClassTableKey (type, reference, is_const)] = inst;
}

void VariantUserClassBase::unregister_instance (const tl::VariantUserClassBase *inst, const std::type_info &type, bool reference, bool is_const)
{
  if (sp_class_table) {
    std::map<VariantUserClassTableKey, const tl::VariantUserClassBase *>::iterator c = sp_class_table->find (VariantUserClassTableKey (type, reference, is_const));
    if (c != sp_class_table->end () && c->second == inst) {
      sp_class_table->erase (c);
    }
    if (sp_class_table->empty ()) {
      delete sp_class_table;
      sp_class_table = 0;
    }
  }
}

// --------------------------------------------------------------------
//  Implementation of tl::Variant

Variant::Variant () 
  : m_type (t_nil), m_string (0)
{
  // .. nothing yet ..
}

Variant::Variant (const QByteArray &qba) 
  : m_type (t_qbytearray), m_string (0)
{
  m_var.m_qbytearray = new QByteArray (qba);
}

Variant::Variant (const QString &qs) 
  : m_type (t_qstring), m_string (0)
{
  m_var.m_qstring = new QString (qs);
}

Variant::Variant (const std::string &s) 
  : m_type (t_stdstring), m_string (0)
{
  m_var.m_stdstring = new std::string (s);
}

Variant::Variant (const char *s) 
  : m_type (t_string)
{
  m_string = new char [strlen (s) + 1];
  strcpy (m_string, s);
}

Variant::Variant (double d)
  : m_type (t_double), m_string (0)
{
  m_var.m_double = d;
}

Variant::Variant (float d)
  : m_type (t_float), m_string (0)
{
  m_var.m_float = d;
}

Variant::Variant (bool b)
  : m_type (t_bool), m_string (0)
{
  m_var.m_bool = b;
}

Variant::Variant (char c)
  : m_type (t_char), m_string (0)
{
  m_var.m_char = c;
}

Variant::Variant (signed char c)
  : m_type (t_schar), m_string (0)
{
  m_var.m_schar = c;
}

Variant::Variant (unsigned char c)
  : m_type (t_uchar), m_string (0)
{
  m_var.m_uchar = c;
}

Variant::Variant (short s)
  : m_type (t_short), m_string (0)
{
  m_var.m_short = s;
}

Variant::Variant (unsigned short s)
  : m_type (t_ushort), m_string (0)
{
  m_var.m_ushort = s;
}

Variant::Variant (int l)
  : m_type (t_int), m_string (0)
{
  m_var.m_int = l;
}

Variant::Variant (unsigned int l)
  : m_type (t_uint), m_string (0)
{
  m_var.m_uint = l;
}

Variant::Variant (long long l)
  : m_type (t_longlong), m_string (0)
{
  m_var.m_longlong = l;
}

Variant::Variant (unsigned long long l)
  : m_type (t_ulonglong), m_string (0)
{
  m_var.m_ulonglong = l;
}

#if defined(HAVE_64BIT_COORD)
Variant::Variant (__int128 l)
  : m_type (t_int128), m_string (0)
{
  m_var.m_int128 = l;
}
#endif

Variant::Variant (long l)
  : m_type (t_long), m_string (0)
{
  m_var.m_long = l;
}

Variant::Variant (unsigned long l)
  : m_type (t_ulong), m_string (0)
{
  m_var.m_ulong = l;
}

Variant::Variant (size_t l, bool /*dummy*/)
  : m_type (t_id), m_string (0)
{
  m_var.m_id = l;
}

Variant::Variant (const Variant &v)
  : m_type (t_nil), m_string (0)
{
  operator= (v);
}

Variant::~Variant ()
{
  reset ();
}

void
Variant::reset ()
{
  if (m_string) {
    delete [] m_string;
  }
  m_string = 0;
  if (m_type == t_list) {
    delete m_var.m_list;
  } else if (m_type == t_array) {
    delete m_var.m_array;
  } else if (m_type == t_qstring) {
    delete m_var.m_qstring;
  } else if (m_type == t_qbytearray) {
    delete m_var.m_qbytearray;
  } else if (m_type == t_stdstring) {
    delete m_var.m_stdstring;
  } else if (is_user ()) {
    if (m_var.mp_user.object) {
      m_var.mp_user.cls->destroy (m_var.mp_user.object);
    }
  }
  m_type = t_nil;
}

Variant &
Variant::operator= (const char *s)
{
  if (m_type == t_string && s == m_string) {
    //  we are assigning to ourselves
  } else {
    char *snew = new char [strlen (s) + 1];
    strcpy (snew, s);
    reset ();
    m_type = t_string;
    m_string = snew;
  }
  return *this;
}

Variant &
Variant::operator= (const std::string &s)
{
  if (m_type == t_stdstring && &s == m_var.m_stdstring) {
    //  we are assigning to ourselves
  } else {
    std::string *snew = new std::string (s);
    reset ();
    m_type = t_stdstring;
    m_var.m_stdstring = snew;
  }
  return *this;
}

Variant &
Variant::operator= (const QByteArray &qs)
{
  if (m_type == t_qbytearray && &qs == m_var.m_qbytearray) {
    //  we are assigning to ourselves
  } else {
    QByteArray *snew = new QByteArray (qs);
    reset ();
    m_type = t_qbytearray;
    m_var.m_qbytearray = snew;
  }
  return *this;
}

Variant &
Variant::operator= (const QString &qs)
{
  if (m_type == t_qstring && &qs == m_var.m_qstring) {
    //  we are assigning to ourselves
  } else {
    QString *snew = new QString (qs);
    reset ();
    m_type = t_qstring;
    m_var.m_qstring = snew;
  }
  return *this;
}

Variant &
Variant::operator= (double d)
{
  reset ();
  m_type = t_double;
  m_var.m_double = d;
  return *this;
}

Variant &
Variant::operator= (float d)
{
  reset ();
  m_type = t_float;
  m_var.m_float = d;
  return *this;
}

Variant &
Variant::operator= (bool b)
{
  reset ();
  m_type = t_bool;
  m_var.m_bool = b;
  return *this;
}

Variant &
Variant::operator= (signed char c)
{
  reset ();
  m_type = t_schar;
  m_var.m_schar = c;
  return *this;
}

Variant &
Variant::operator= (unsigned char c)
{
  reset ();
  m_type = t_uchar;
  m_var.m_uchar = c;
  return *this;
}

Variant &
Variant::operator= (char c)
{
  reset ();
  m_type = t_char;
  m_var.m_char = c;
  return *this;
}

Variant &
Variant::operator= (unsigned short s)
{
  reset ();
  m_type = t_ushort;
  m_var.m_ushort = s;
  return *this;
}

Variant &
Variant::operator= (short s)
{
  reset ();
  m_type = t_short;
  m_var.m_short = s;
  return *this;
}

Variant &
Variant::operator= (unsigned int l)
{
  reset ();
  m_type = t_uint;
  m_var.m_uint = l;
  return *this;
}

Variant &
Variant::operator= (int l)
{
  reset ();
  m_type = t_int;
  m_var.m_int = l;
  return *this;
}

Variant &
Variant::operator= (unsigned long l)
{
  reset ();
  m_type = t_ulong;
  m_var.m_ulong = l;
  return *this;
}

Variant &
Variant::operator= (long l)
{
  reset ();
  m_type = t_long;
  m_var.m_long = l;
  return *this;
}

Variant &
Variant::operator= (unsigned long long l)
{
  reset ();
  m_type = t_ulonglong;
  m_var.m_ulonglong = l;
  return *this;
}

Variant &
Variant::operator= (long long l)
{
  reset ();
  m_type = t_longlong;
  m_var.m_longlong = l;
  return *this;
}

#if defined(HAVE_64BIT_COORD)
Variant &
Variant::operator= (__int128 l)
{
  reset ();
  m_type = t_int128;
  m_var.m_int128 = l;
  return *this;
}
#endif

Variant &
Variant::operator= (const Variant &v)
{
  if (this != &v) {

    reset ();

    m_type = v.m_type;
    if (m_type == t_double) {
      m_var.m_double = v.m_var.m_double;
    } else if (m_type == t_float) {
      m_var.m_float = v.m_var.m_float;
    } else if (m_type == t_bool) {
      m_var.m_bool = v.m_var.m_bool;
    } else if (m_type == t_uchar) {
      m_var.m_uchar = v.m_var.m_uchar;
    } else if (m_type == t_schar) {
      m_var.m_schar = v.m_var.m_schar;
    } else if (m_type == t_char) {
      m_var.m_char = v.m_var.m_char;
    } else if (m_type == t_ushort) {
      m_var.m_ushort = v.m_var.m_ushort;
    } else if (m_type == t_short) {
      m_var.m_short = v.m_var.m_short;
    } else if (m_type == t_uint) {
      m_var.m_uint = v.m_var.m_uint;
    } else if (m_type == t_int) {
      m_var.m_int = v.m_var.m_int;
    } else if (m_type == t_ulong) {
      m_var.m_ulong = v.m_var.m_ulong;
    } else if (m_type == t_long) {
      m_var.m_long = v.m_var.m_long;
    } else if (m_type == t_longlong) {
      m_var.m_longlong = v.m_var.m_longlong;
    } else if (m_type == t_ulonglong) {
      m_var.m_ulonglong = v.m_var.m_ulonglong;
#if defined(HAVE_64BIT_COORD)
    } else if (m_type == t_int128) {
      m_var.m_int128 = v.m_var.m_int128;
#endif
    } else if (m_type == t_id) {
      m_var.m_id = v.m_var.m_id;
    } else if (m_type == t_qstring) {
      m_var.m_qstring = new QString (*v.m_var.m_qstring);
    } else if (m_type == t_qbytearray) {
      m_var.m_qbytearray = new QByteArray (*v.m_var.m_qbytearray);
    } else if (m_type == t_stdstring) {
      m_var.m_stdstring = new std::string (*v.m_var.m_stdstring);
    } else if (m_type == t_string) {
      m_string = new char [strlen (v.m_string) + 1];
      strcpy (m_string, v.m_string);
    } else if (m_type == t_list) {
      m_var.m_list = new std::vector<tl::Variant> (*v.m_var.m_list);
    } else if (m_type == t_array) {
      m_var.m_array = new std::map<tl::Variant, tl::Variant> (*v.m_var.m_array);
    } else if (is_user ()) {
      m_var.mp_user.cls = v.m_var.mp_user.cls;
      if (v.m_var.mp_user.object) {
        m_var.mp_user.object = v.m_var.mp_user.cls->clone (v.m_var.mp_user.object);
      } else {
        m_var.mp_user.object = 0;
      }
    }

  } 
  return *this;
}

inline bool
is_integer_type (Variant::type type)
{
  switch (type) {
  case Variant::t_char:
  case Variant::t_schar:
  case Variant::t_short:
  case Variant::t_int:
  case Variant::t_long:
  case Variant::t_uchar:
  case Variant::t_ushort:
  case Variant::t_uint:
  case Variant::t_ulong:
  case Variant::t_longlong:
  case Variant::t_ulonglong:
#if defined(HAVE_64BIT_COORD)
  case Variant::t_int128:
#endif
    return true;
  default:
    return false;
  }
}

inline Variant::type 
normalized_type (Variant::type type)
{
  switch (type) {
  case Variant::t_float:
  case Variant::t_double:
    return Variant::t_double;
  case Variant::t_char:
  case Variant::t_schar:
  case Variant::t_short:
  case Variant::t_int:
  case Variant::t_long:
    return Variant::t_long;
  case Variant::t_uchar:
  case Variant::t_ushort:
  case Variant::t_uint:
  case Variant::t_ulong:
    return Variant::t_ulong;
  case Variant::t_stdstring:
  case Variant::t_string:
    return Variant::t_string;
  default:
  case Variant::t_longlong:
  case Variant::t_ulonglong:
#if defined(HAVE_64BIT_COORD)
  case Variant::t_int128:
#endif
  case Variant::t_bool:
  case Variant::t_nil:
  case Variant::t_qstring:
  case Variant::t_qbytearray:
    return type;
  }
}

inline std::pair<bool, Variant::type> 
normalized_type (Variant::type type1, Variant::type type2)
{
  type1 = normalized_type (type1);
  type2 = normalized_type (type2);

  if (type1 == type2) {
    return std::make_pair (true, type1);
  }

  if (type1 == Variant::t_double && is_integer_type (type2)) {
    //  use double as common representation
    return std::make_pair (true, Variant::t_double);
  } else if (type2 == Variant::t_double && is_integer_type (type1)) {
    //  use double as common representation
    return std::make_pair (true, Variant::t_double);
  } else {
    return std::make_pair (type1 == type2, type1);
  }
}


bool 
Variant::operator== (const tl::Variant &d) const
{
  std::pair<bool, type> tt = normalized_type (m_type, d.m_type);
  if (! tt.first) {
    return false;
  }
  type t = tt.second;

  if (t == t_nil) {
    return true;
  } else if (t == t_bool) {
    return m_var.m_bool == d.m_var.m_bool;
  } else if (t == t_ulong) {
    return to_ulong () == d.to_ulong ();
  } else if (t == t_long) {
    return to_long () == d.to_long ();
  } else if (t == t_ulonglong) {
    return to_ulonglong () == d.to_ulonglong ();
  } else if (t == t_longlong) {
    return to_longlong () == d.to_longlong ();
#if defined(HAVE_64BIT_COORD)
  } else if (t == t_int128) {
    return to_int128 () == d.to_int128 ();
#endif
  } else if (t == t_id) {
    return m_var.m_id == d.m_var.m_id;
  } else if (t == t_double) {
    return to_double () == d.to_double ();
  } else if (t == t_string) {
    return strcmp (to_string (), d.to_string ()) == 0;
  } else if (t == t_qstring) {
    return *m_var.m_qstring == *d.m_var.m_qstring;
  } else if (t == t_qbytearray) {
    return *m_var.m_qbytearray == *d.m_var.m_qbytearray;
  } else if (t == t_list) {
    return *m_var.m_list == *d.m_var.m_list;
  } else if (t == t_array) {
    return *m_var.m_array == *d.m_var.m_array;
  } else if (is_user ()) {
    return m_var.mp_user.cls == d.m_var.mp_user.cls && m_var.mp_user.cls->equal (m_var.mp_user.object, d.m_var.mp_user.object);
  } else {
    return false;
  }
}

bool 
Variant::operator< (const tl::Variant &d) const
{
  std::pair<bool, type> tt = normalized_type (m_type, d.m_type);
  if (! tt.first) {
    return normalized_type (m_type) < normalized_type (d.m_type);
  }

  type t = tt.second;

  if (t == t_nil) {
    return false;
  } else if (t == t_bool) {
    return m_var.m_bool < d.m_var.m_bool;
  } else if (t == t_ulong) {
    return to_ulong () < d.to_ulong ();
  } else if (t == t_long) {
    return to_long () < d.to_long ();
  } else if (t == t_ulonglong) {
    return to_ulonglong () < d.to_ulonglong ();
  } else if (t == t_longlong) {
    return to_longlong () < d.to_longlong ();
#if defined(HAVE_64BIT_COORD)
  } else if (t == t_int128) {
    return to_int128 () < d.to_int128 ();
#endif
  } else if (t == t_id) {
    return m_var.m_id < d.m_var.m_id;
  } else if (t == t_double) {
    return to_double () < d.to_double ();
  } else if (t == t_string) {
    return strcmp (to_string (), d.to_string ()) < 0;
  } else if (t == t_qstring) {
    return *m_var.m_qstring < *d.m_var.m_qstring;
  } else if (t == t_qbytearray) {
    return *m_var.m_qbytearray < *d.m_var.m_qbytearray;
  } else if (t == t_list) {
    return *m_var.m_list < *d.m_var.m_list;
  } else if (t == t_array) {
    return *m_var.m_array < *d.m_var.m_array;
  } else if (is_user ()) {
    if (m_var.mp_user.cls != d.m_var.mp_user.cls) {
      //  TODO: there should be some class Id that can be used for comparison (that is more predictable)
      return m_var.mp_user.cls < d.m_var.mp_user.cls;
    }
    return m_var.mp_user.cls->less (m_var.mp_user.object, d.m_var.mp_user.object);
  } else {
    return false;
  }
}

bool 
Variant::can_convert_to_float () const
{
  switch (m_type) {
  case t_float:
  case t_char:
  case t_uchar:
  case t_schar:
  case t_short:
  case t_ushort:
  case t_int:
  case t_uint:
  case t_long:
  case t_ulong:
  case t_longlong:
  case t_ulonglong:
#if defined(HAVE_64BIT_COORD)
  case t_int128:
#endif
  case t_bool:
  case t_nil:
    return true;
  case t_double:
    return m_var.m_double < std::numeric_limits<float>::max () && m_var.m_double > std::numeric_limits<float>::min ();
  case t_qstring:
  case t_qbytearray:
  case t_stdstring:
  case t_string:
    {
      tl::Extractor ex (to_string ());
      double d;
      return ex.try_read (d) && ex.at_end ();
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_double () const
{
  switch (m_type) {
  case t_double:
  case t_float:
  case t_char:
  case t_uchar:
  case t_schar:
  case t_short:
  case t_ushort:
  case t_int:
  case t_uint:
  case t_long:
  case t_ulong:
  case t_longlong:
  case t_ulonglong:
#if defined(HAVE_64BIT_COORD)
  case t_int128:
#endif
  case t_bool:
  case t_nil:
    return true;
  case t_qstring:
  case t_qbytearray:
  case t_stdstring:
  case t_string:
    {
      tl::Extractor ex (to_string ());
      double d;
      return ex.try_read (d) && ex.at_end ();
    }
  default:
    return false;
  }
}

#if defined(HAVE_64BIT_COORD)
bool 
Variant::can_convert_to_int128 () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= std::numeric_limits<__int128>::max () && m_var.m_double >= std::numeric_limits<__int128>::min ();
  case t_float:
    return m_var.m_float <= float (std::numeric_limits<__int128>::max ()) && m_var.m_float >= float (std::numeric_limits<__int128>::min ());
  case t_longlong:
  case t_long:
  case t_int128:
  case t_char:
  case t_schar:
  case t_short:
  case t_int:
  case t_bool:
  case t_uchar:
  case t_ushort:
  case t_uint:
  case t_nil:
    return true;
  case t_string:
  case t_qstring:
  case t_qbytearray:
  case t_stdstring:
    //  TODO: there is no range checking currently
    return true;
  default:
    return false;
  }
}
#endif

bool 
Variant::can_convert_to_ulonglong () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= std::numeric_limits<unsigned long long>::max () && m_var.m_double >= std::numeric_limits<unsigned long long>::min ();
  case t_float:
    return m_var.m_float <= float (std::numeric_limits<unsigned long long>::max ()) && m_var.m_float >= float (std::numeric_limits<unsigned long long>::min ());
  case t_longlong:
    return m_var.m_longlong >= 0;
#if defined(HAVE_64BIT_COORD)
  case t_int128:
    return m_var.m_int128 <= __int128 (std::numeric_limits<unsigned long long>::max ()) && m_var.m_int128 >= __int128 (std::numeric_limits<unsigned long long>::min ());
#endif
  case t_ulonglong:
  case t_ulong:
  case t_bool:
  case t_uchar:
  case t_ushort:
  case t_uint:
  case t_nil:
    return true;
  case t_long:
    return m_var.m_long >= 0;
  case t_char:
    return m_var.m_char >= 0;
  case t_schar:
    return m_var.m_schar >= 0;
  case t_short:
    return m_var.m_short >= 0;
  case t_int:
    return m_var.m_int >= 0;
  case t_string:
  case t_qstring:
  case t_qbytearray:
  case t_stdstring:
    {
      tl::Extractor ex (to_string ());
      unsigned long long ll;
      return ex.try_read (ll) && ex.at_end ();
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_longlong () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= std::numeric_limits<long long>::max () && m_var.m_double >= std::numeric_limits<long long>::min ();
  case t_float:
    return m_var.m_float <= float (std::numeric_limits<long long>::max ()) && m_var.m_float >= float (std::numeric_limits<long long>::min ());
#if defined(HAVE_64BIT_COORD)
  case t_int128:
    return m_var.m_int128 <= __int128 (std::numeric_limits<long long>::max ()) && m_var.m_int128 >= __int128 (std::numeric_limits<long long>::min ());
#endif
  case t_ulonglong:
    return m_var.m_ulonglong <= (unsigned long long) std::numeric_limits<long long>::max ();
  case t_longlong:
  case t_ulong:
  case t_long:
  case t_bool:
  case t_char:
  case t_uchar:
  case t_schar:
  case t_short:
  case t_ushort:
  case t_int:
  case t_uint:
  case t_nil:
    return true;
  case t_string:
  case t_qstring:
  case t_qbytearray:
  case t_stdstring:
    {
      tl::Extractor ex (to_string ());
      long long ll;
      return ex.try_read (ll) && ex.at_end ();
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_ulong () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= std::numeric_limits<unsigned long>::max () && m_var.m_double >= std::numeric_limits<unsigned long>::min ();
  case t_float:
    return m_var.m_float <= std::numeric_limits<unsigned long>::max () && m_var.m_float >= std::numeric_limits<unsigned long>::min ();
#if defined(HAVE_64BIT_COORD)
  case t_int128:
    return m_var.m_int128 <= __int128 (std::numeric_limits<unsigned long long>::max ()) && m_var.m_int128 >= __int128 (std::numeric_limits<unsigned long long>::min ());
#endif
  case t_longlong:
    return m_var.m_longlong >= 0 && m_var.m_longlong < (long long) std::numeric_limits<unsigned long>::max ();
  case t_ulonglong:
    return m_var.m_ulonglong < (unsigned long long) std::numeric_limits<unsigned long>::max ();
  case t_ulong:
  case t_bool:
  case t_uchar:
  case t_ushort:
  case t_uint:
  case t_nil:
    return true;
  case t_long:
    return m_var.m_long >= 0;
  case t_char:
    return m_var.m_char >= 0;
  case t_schar:
    return m_var.m_schar >= 0;
  case t_short:
    return m_var.m_short >= 0;
  case t_int:
    return m_var.m_int >= 0;
  case t_string:
  case t_qstring:
  case t_qbytearray:
  case t_stdstring:
    {
      tl::Extractor ex (to_string ());
      unsigned long l;
      return ex.try_read (l) && ex.at_end ();
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_long () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= std::numeric_limits<long>::max () && m_var.m_double >= std::numeric_limits<long>::min ();
  case t_float:
    return m_var.m_float <= std::numeric_limits<long>::max () && m_var.m_float >= std::numeric_limits<long>::min ();
#if defined(HAVE_64BIT_COORD)
  case t_int128:
    return m_var.m_int128 <= __int128 (std::numeric_limits<long>::max ()) && m_var.m_int128 >= __int128 (std::numeric_limits<long>::min ());
#endif
  case t_ulonglong:
    return m_var.m_ulonglong <= (unsigned long long) std::numeric_limits<long>::max ();
  case t_longlong:
    return m_var.m_longlong >= (long long) std::numeric_limits<long>::min () && m_var.m_longlong <= (long long) std::numeric_limits<long>::max ();
  case t_ulong:
    return m_var.m_ulong <= (unsigned long) std::numeric_limits<long>::max ();
  case t_long:
  case t_bool:
  case t_char:
  case t_uchar:
  case t_schar:
  case t_short:
  case t_ushort:
  case t_int:
  case t_uint:
  case t_nil:
    return true;
  case t_string:
  case t_qstring:
  case t_qbytearray:
  case t_stdstring:
    {
      tl::Extractor ex (to_string ());
      long l;
      return ex.try_read (l) && ex.at_end ();
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_int () const
{
  return can_convert_to_long () && (to_long () <= (long) std::numeric_limits<int>::max () && to_long () >= (long) std::numeric_limits<int>::min ());
}

bool 
Variant::can_convert_to_uint () const
{
  return can_convert_to_long () && (to_long () <= (long) std::numeric_limits<unsigned int>::max () && to_long () >= (long) std::numeric_limits<unsigned int>::min ());
}

bool 
Variant::can_convert_to_short () const
{
  return can_convert_to_long () && (to_long () <= (long) std::numeric_limits<short>::max () && to_long () >= (long) std::numeric_limits<short>::min ());
}

bool 
Variant::can_convert_to_ushort () const
{
  return can_convert_to_long () && (to_long () <= (long) std::numeric_limits<unsigned short>::max () && to_long () >= (long) std::numeric_limits<unsigned short>::min ());
}

bool 
Variant::can_convert_to_char () const
{
  return can_convert_to_long () && (to_long () <= (long) std::numeric_limits<char>::max () && to_long () >= (long) std::numeric_limits<char>::min ());
}

bool 
Variant::can_convert_to_schar () const
{
  return can_convert_to_long () && (to_short () <= (long) std::numeric_limits<signed char>::max () && to_short () >= (long) std::numeric_limits<signed char>::min ());
}

bool 
Variant::can_convert_to_uchar () const
{
  return can_convert_to_long () && (to_short () <= (long) std::numeric_limits<unsigned char>::max () && to_short () >= (long) std::numeric_limits<unsigned char>::min ());
}

QByteArray
Variant::to_qbytearray () const
{
  if (m_type == t_qbytearray) {
    return *m_var.m_qbytearray;
  } else if (m_type == t_qstring) {
    return m_var.m_qstring->toUtf8 ();
  } else if (m_type == t_stdstring) {
    return QByteArray (m_var.m_stdstring->c_str (), int (m_var.m_stdstring->size ()));
  } else {
    //  TODO: maybe some other conversion makes sense? I.e. byte representation of int?
    std::string s = to_string ();
    return QByteArray (s.c_str (), int (s.size ()));
  }
}

QString
Variant::to_qstring () const
{
  if (m_type == t_qstring) {
    return *m_var.m_qstring;
  } else if (m_type == t_qbytearray) {
    return QString::fromUtf8 (*m_var.m_qbytearray);
  } else {
    return tl::to_qstring (to_string ());
  }
}

std::string
Variant::to_stdstring () const
{
  if (m_type == t_stdstring) {
    return *m_var.m_stdstring;
  } else if (m_type == t_qstring) {
    return tl::to_string (*m_var.m_qstring);
  } else if (m_type == t_qbytearray) {
    return std::string (m_var.m_qbytearray->constData (), m_var.m_qbytearray->size ());
  } else {
    return std::string (to_string ());
  }
}

const char *
Variant::to_string () const
{
  if (m_type == t_stdstring) {

    return m_var.m_stdstring->c_str ();

  } else if (m_type == t_qbytearray) {

    //  TODO: content may be longer - const char * terminates at first 0 character
    return m_var.m_qbytearray->constData ();

  // conversion needed
  } else if (! m_string) {

    std::string r;

    if (m_type == t_nil) {
      r = "nil";
    } else if (m_type == t_double) {
      r = tl::to_string (m_var.m_double);
    } else if (m_type == t_float) {
      r = tl::to_string (m_var.m_float);
    } else if (m_type == t_char) {
      r = tl::to_string ((int) m_var.m_char);
    } else if (m_type == t_schar) {
      r = tl::to_string ((int) m_var.m_schar);
    } else if (m_type == t_uchar) {
      r = tl::to_string ((int) m_var.m_uchar);
    } else if (m_type == t_short) {
      r = tl::to_string ((int) m_var.m_short);
    } else if (m_type == t_ushort) {
      r = tl::to_string ((int) m_var.m_ushort);
    } else if (m_type == t_int) {
      r = tl::to_string (m_var.m_int);
    } else if (m_type == t_uint) {
      r = tl::to_string (m_var.m_uint);
    } else if (m_type == t_long) {
      r = tl::to_string (m_var.m_long);
    } else if (m_type == t_ulong) {
      r = tl::to_string (m_var.m_ulong);
    } else if (m_type == t_longlong) {
      r = tl::to_string (m_var.m_longlong);
    } else if (m_type == t_ulonglong) {
      r = tl::to_string (m_var.m_ulonglong);
#if defined(HAVE_64BIT_COORD)
    } else if (m_type == t_int128) {
      r = tl::to_string (m_var.m_int128);
#endif
    } else if (m_type == t_bool) {
      r = tl::to_string (m_var.m_bool);
    } else if (m_type == t_qstring) {
      r = tl::to_string (*m_var.m_qstring);
    } else if (m_type == t_qbytearray) {
      r = std::string (m_var.m_qbytearray->constData (), m_var.m_qbytearray->size ());
    } else if (m_type == t_list) {
      for (std::vector<tl::Variant>::const_iterator v = m_var.m_list->begin (); v != m_var.m_list->end (); ++v) {
        if (v != m_var.m_list->begin ()) {
          r += ",";
        }
        r += v->to_string ();
      }
    } else if (m_type == t_array) {
      for (const_array_iterator v = m_var.m_array->begin (); v != m_var.m_array->end (); ++v) {
        if (v != m_var.m_array->begin ()) {
          r += ",";
        }
        r += v->first.to_string ();
        r += "=>";
        r += v->second.to_string ();
      }
    } else if (m_type == t_id)  {
      r = "[id" + tl::to_string (m_var.m_id) + "]";
    } else if (is_user ()) {
      r = m_var.mp_user.cls->to_string (m_var.mp_user.object);
    } else {
      r = "[unknown]";
    }

    m_string = new char [r.size () + 1];
    strcpy (m_string, r.c_str ());

  }

  return m_string;
}

bool
Variant::to_bool () const
{
  if (m_type == t_nil) {
    return false;
  } else if (m_type == t_bool) {
    return m_var.m_bool;
  } else {
    return true;
  }
}

#if defined(HAVE_64BIT_COORD)
__int128
Variant::to_int128 () const
{
  if (m_type == t_nil) {
    return 0;
  } else if (m_type == t_int128) {
    return m_var.m_int128;
  } else if (m_type == t_double) {
    return (__int128) (m_var.m_double);
  } else if (m_type == t_float) {
    return (__int128) (m_var.m_float);
  } else if (m_type == t_uchar) {
    return m_var.m_uchar;
  } else if (m_type == t_schar) {
    return m_var.m_schar;
  } else if (m_type == t_char) {
    return m_var.m_char;
  } else if (m_type == t_ushort) {
    return m_var.m_ushort;
  } else if (m_type == t_short) {
    return m_var.m_short;
  } else if (m_type == t_uint) {
    return m_var.m_uint;
  } else if (m_type == t_int) {
    return m_var.m_int;
  } else if (m_type == t_ulong) {
    return m_var.m_ulong;
  } else if (m_type == t_long) {
    return m_var.m_long;
  } else if (m_type == t_ulonglong) {
    return m_var.m_ulonglong;
  } else if (m_type == t_longlong) {
    return m_var.m_longlong;
  } else if (m_type == t_bool) {
    return m_var.m_bool;
  } else if (m_type == t_stdstring) {
    tl::Extractor ex (m_var.m_stdstring->c_str ());
    __int128 l = 0;
    ex.read (l);
    return l;
  } else if (m_type == t_qbytearray) {
    tl::Extractor ex (m_var.m_qbytearray->constData ());
    __int128 l = 0;
    ex.read (l);
    return l;
  } else if (m_type == t_string || m_type == t_qstring) {
    std::string s (to_string ());
    tl::Extractor ex (s.c_str ());
    __int128 l = 0;
    ex.read (l);
    return l;
  } else {
    return 0;
  }
}
#endif

unsigned long long
Variant::to_ulonglong () const
{
  if (m_type == t_nil) {
    return 0;
  } else if (m_type == t_double) {
    return (unsigned long long) (m_var.m_double);
  } else if (m_type == t_float) {
    return (unsigned long long) (m_var.m_float);
  } else if (m_type == t_uchar) {
    return m_var.m_uchar;
  } else if (m_type == t_schar) {
    return m_var.m_schar;
  } else if (m_type == t_char) {
    return m_var.m_char;
  } else if (m_type == t_ushort) {
    return m_var.m_ushort;
  } else if (m_type == t_short) {
    return m_var.m_short;
  } else if (m_type == t_uint) {
    return m_var.m_uint;
  } else if (m_type == t_int) {
    return m_var.m_int;
  } else if (m_type == t_ulong) {
    return m_var.m_ulong;
  } else if (m_type == t_long) {
    return m_var.m_long;
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    return m_var.m_int128;
#endif
  } else if (m_type == t_ulonglong) {
    return m_var.m_ulonglong;
  } else if (m_type == t_longlong) {
    return m_var.m_longlong;
  } else if (m_type == t_bool) {
    return m_var.m_bool;
  } else if (m_type == t_stdstring) {
    unsigned long long l = 0;
    tl::from_string (*m_var.m_stdstring, l);
    return l;
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray) {
    unsigned long long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else {
    return 0;
  }
}

long long
Variant::to_longlong () const
{
  if (m_type == t_nil) {
    return 0;
  } else if (m_type == t_double) {
    return (long long) (m_var.m_double);
  } else if (m_type == t_float) {
    return (long long) (m_var.m_float);
  } else if (m_type == t_uchar) {
    return m_var.m_uchar;
  } else if (m_type == t_schar) {
    return m_var.m_schar;
  } else if (m_type == t_char) {
    return m_var.m_char;
  } else if (m_type == t_ushort) {
    return m_var.m_ushort;
  } else if (m_type == t_short) {
    return m_var.m_short;
  } else if (m_type == t_uint) {
    return m_var.m_uint;
  } else if (m_type == t_int) {
    return m_var.m_int;
  } else if (m_type == t_ulong) {
    return m_var.m_ulong;
  } else if (m_type == t_long) {
    return m_var.m_long;
  } else if (m_type == t_ulonglong) {
    return m_var.m_ulonglong;
  } else if (m_type == t_longlong) {
    return m_var.m_longlong;
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    return m_var.m_int128;
#endif
  } else if (m_type == t_bool) {
    return m_var.m_bool;
  } else if (m_type == t_stdstring) {
    long long l = 0;
    tl::from_string (*m_var.m_stdstring, l);
    return l;
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray) {
    long long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else {
    return 0;
  }
}

unsigned long
Variant::to_ulong () const
{
  if (m_type == t_nil) {
    return 0;
  } else if (m_type == t_double) {
    return (unsigned long) (m_var.m_double);
  } else if (m_type == t_float) {
    return (unsigned long) (m_var.m_float);
  } else if (m_type == t_uchar) {
    return m_var.m_uchar;
  } else if (m_type == t_schar) {
    return m_var.m_schar;
  } else if (m_type == t_char) {
    return m_var.m_char;
  } else if (m_type == t_ushort) {
    return m_var.m_ushort;
  } else if (m_type == t_short) {
    return m_var.m_short;
  } else if (m_type == t_uint) {
    return m_var.m_uint;
  } else if (m_type == t_int) {
    return m_var.m_int;
  } else if (m_type == t_ulong) {
    return m_var.m_ulong;
  } else if (m_type == t_long) {
    return m_var.m_long;
  } else if (m_type == t_ulonglong) {
    return m_var.m_ulonglong;
  } else if (m_type == t_longlong) {
    return m_var.m_longlong;
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    return m_var.m_int128;
#endif
  } else if (m_type == t_bool) {
    return m_var.m_bool;
  } else if (m_type == t_stdstring) {
    unsigned long l = 0;
    tl::from_string (*m_var.m_stdstring, l);
    return l;
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray) {
    unsigned long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else {
    return 0;
  }
}

long 
Variant::to_long () const
{
  if (m_type == t_nil) {
    return 0;
  } else if (m_type == t_double) {
    return (long) (m_var.m_double);
  } else if (m_type == t_float) {
    return (long) (m_var.m_float);
  } else if (m_type == t_uchar) {
    return m_var.m_uchar;
  } else if (m_type == t_schar) {
    return m_var.m_schar;
  } else if (m_type == t_char) {
    return m_var.m_char;
  } else if (m_type == t_ushort) {
    return m_var.m_ushort;
  } else if (m_type == t_short) {
    return m_var.m_short;
  } else if (m_type == t_uint) {
    return m_var.m_uint;
  } else if (m_type == t_int) {
    return m_var.m_int;
  } else if (m_type == t_ulong) {
    return m_var.m_ulong;
  } else if (m_type == t_long) {
    return m_var.m_long;
  } else if (m_type == t_ulonglong) {
    return m_var.m_ulonglong;
  } else if (m_type == t_longlong) {
    return m_var.m_longlong;
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    return m_var.m_int128;
#endif
  } else if (m_type == t_bool) {
    return m_var.m_bool;
  } else if (m_type == t_stdstring) {
    long l = 0;
    tl::from_string (*m_var.m_stdstring, l);
    return l;
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray) {
    long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else {
    return 0;
  }
}

int 
Variant::to_int () const
{
  return (int) to_long ();
}

unsigned int 
Variant::to_uint () const
{
  return (unsigned int) to_ulong ();
}

short 
Variant::to_short () const
{
  return (short) to_long ();
}

unsigned short 
Variant::to_ushort () const
{
  return (unsigned short) to_ulong ();
}

char 
Variant::to_char () const
{
  return (char) to_long ();
}

signed char 
Variant::to_schar () const
{
  return (signed char) to_long ();
}

unsigned char 
Variant::to_uchar () const
{
  return (unsigned char) to_ulong ();
}

size_t 
Variant::to_id () const
{
  if (m_type == t_id) {
    return m_var.m_id;
  } else {
    return 0;
  }
}

double 
Variant::to_double () const
{
  if (m_type == t_nil) {
    return 0;
  } else if (m_type == t_double) {
    return m_var.m_double;
  } else if (m_type == t_float) {
    return m_var.m_float;
  } else if (m_type == t_uchar) {
    return m_var.m_uchar;
  } else if (m_type == t_schar) {
    return m_var.m_schar;
  } else if (m_type == t_char) {
    return m_var.m_char;
  } else if (m_type == t_ushort) {
    return m_var.m_ushort;
  } else if (m_type == t_short) {
    return m_var.m_short;
  } else if (m_type == t_uint) {
    return m_var.m_uint;
  } else if (m_type == t_int) {
    return m_var.m_int;
  } else if (m_type == t_ulong) {
    return m_var.m_ulong;
  } else if (m_type == t_long) {
    return m_var.m_long;
  } else if (m_type == t_ulonglong) {
    return m_var.m_ulonglong;
  } else if (m_type == t_longlong) {
    return m_var.m_longlong;
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    return m_var.m_int128;
#endif
  } else if (m_type == t_bool) {
    return m_var.m_bool;
  } else if (m_type == t_stdstring) {
    double d = 0;
    tl::from_string (*m_var.m_stdstring, d);
    return d;
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray) {
    double d = 0;
    tl::from_string (to_string (), d);
    return d;
  } else {
    return 0;
  }
}

float 
Variant::to_float () const
{
  return to_double ();
}

const void *
Variant::native_ptr () const
{
  if (is_user ()) {
    return m_var.mp_user.object;
  }

  switch (m_type) {
  case t_double:
    return &m_var.m_double;
  case t_float:
    return &m_var.m_float;
  case t_ulonglong:
    return &m_var.m_ulonglong;
  case t_longlong:
    return &m_var.m_longlong;
#if defined(HAVE_64BIT_COORD)
  case t_int128:
    return &m_var.m_int128;
#endif
  case t_ulong:
    return &m_var.m_ulong;
  case t_long:
    return &m_var.m_long;
  case t_bool:
    return &m_var.m_bool;
  case t_char:
    return &m_var.m_char;
  case t_uchar:
    return &m_var.m_uchar;
  case t_schar:
    return &m_var.m_schar;
  case t_short:
    return &m_var.m_short;
  case t_ushort:
    return &m_var.m_ushort;
  case t_int:
    return &m_var.m_int;
  case t_uint:
    return &m_var.m_uint;
  case t_string:
    return m_string;
  case t_qstring:
    return m_var.m_qstring;
  case t_qbytearray:
    return m_var.m_qbytearray;
  case t_stdstring:
    return m_var.m_stdstring;
  case t_array:
    return m_var.m_array;
  case t_list:
    return m_var.m_list;
  case t_nil:
  default:
    return 0;
  }
}

tl::Variant
Variant::empty_list ()
{
  static std::vector<tl::Variant> empty_list;
  return tl::Variant (empty_list.begin (), empty_list.end ());
}

tl::Variant
Variant::empty_array ()
{
  tl::Variant e;
  e.set_array ();
  return e;
}

tl::Variant *
tl::Variant::find (const tl::Variant &k)
{
  if (m_type != t_array) {
    return 0;
  } else {
    array_iterator a = m_var.m_array->find (k);
    if (a != m_var.m_array->end ()) {
      return &a->second;
    } else {
      return 0;
    }
  }
}

/**
 *  @brief Returns the value for the given key or 0 if the variant is not an array or does not contain the key
 */
const tl::Variant *
tl::Variant::find (const tl::Variant &k) const
{
  if (m_type != t_array) {
    return 0;
  } else {
    const_array_iterator a = m_var.m_array->find (k);
    if (a != m_var.m_array->end ()) {
      return &a->second;
    } else {
      return 0;
    }
  }
}

std::string 
Variant::to_parsable_string () const
{
  if (is_long ()) {
    return "#" + tl::to_string (to_long ());
#if defined(HAVE_64BIT_COORD)
  } else if (is_int128 ()) {
    return "#ll" + tl::to_string (to_int128 ());
#endif
  } else if (is_longlong ()) {
    return "#l" + tl::to_string (to_longlong ());
  } else if (is_ulong ()) {
    return "#u" + tl::to_string (to_ulong ());
  } else if (is_ulonglong ()) {
    return "#lu" + tl::to_string (to_ulonglong ());
  } else if (is_double ()) {
    return "##" + tl::to_string (to_double ());
  } else if (is_bool ()) {
    return m_var.m_bool ? "true" : "false";
  } else if (is_nil ()) {
    return "nil";
  } else if (is_stdstring ()) {
    return tl::to_quoted_string (*m_var.m_stdstring);
  } else if (is_cstring () || is_qstring () || is_qbytearray ()) {
    return tl::to_quoted_string (to_string ());
  } else if (is_list ()) {
    std::string r = "(";
    for (tl::Variant::const_iterator l = begin (); l != end (); ++l) {
      if (l != begin ()) {
        r += ",";
      }
      r += l->to_parsable_string ();
    }
    r += ")";
    return r;
  } else if (is_array ()) {
    std::string r = "{";
    for (tl::Variant::const_array_iterator l = begin_array (); l != end_array (); ++l) {
      if (l != begin_array ()) {
        r += ",";
      }
      r += l->first.to_parsable_string ();
      r += "=>";
      r += l->second.to_parsable_string ();
    }
    r += "}";
    return r;
  } else if (is_id ()) {
    return "[id" + tl::to_string (m_var.m_id) + "]";
  } else if (is_user ()) {
    if (m_var.mp_user.cls) {
      //  for downward compatibility we use lower case name + do a translation
      return "[" + translate_class_name (tl::to_lower_case (m_var.mp_user.cls->name ())) + ":" + m_var.mp_user.cls->to_string (m_var.mp_user.object) + "]";
    } else {
      return "[unknown_user_type]";
    }
  } else {
    return "";
  }
}

void 
tl::Variant::swap (tl::Variant &other)
{
  std::swap (m_type, other.m_type);
  std::swap (m_string, other.m_string);

  ValueHolder a = m_var;
  m_var = other.m_var;
  other.m_var = a;
}

// ----------------------------------------------------------------------------------
//  Extractor implementation

template <>
KLAYOUT_DLL void extractor_impl (tl::Extractor &ex, tl::Variant &v)
{
  if (! test_extractor_impl (ex, v)) {
    ex.error (tl::to_string (QObject::tr ("Expected a value specification")));
  }
}

template <>
KLAYOUT_DLL bool test_extractor_impl (tl::Extractor &ex, tl::Variant &v)
{
  std::string s;

  if (ex.test ("##")) {

    double x = 0;
    ex.read (x);
    v = x;
    return true;

#if defined(HAVE_64BIT_COORD)
  } else if (ex.test ("#ll")) {

    __int128 x = 0;
    ex.read (x);
    v = x;
    return true;

#endif

  } else if (ex.test ("#lu")) {

    unsigned long long x = 0;
    ex.read (x);
    v = x;
    return true;

  } else if (ex.test ("#l")) {

    long long x = 0;
    ex.read (x);
    v = x;
    return true;

  } else if (ex.test ("#u")) {

    unsigned long x = 0;
    ex.read (x);
    v = x;
    return true;

  } else if (ex.test ("#")) {

    long x = 0;
    ex.read (x);
    v = x;
    return true;

  } else if (ex.test ("nil")) {

    v = tl::Variant ();
    return true;

  } else if (ex.test ("false")) {

    v = false;
    return true;

  } else if (ex.test ("true")) {

    v = true;
    return true;

  } else if (ex.test ("[")) {

    std::string cls;
    ex.read_word_or_quoted (cls);

    const VariantUserClassBase *ccls = find_cls_by_name (cls);
    if (ccls) {
      void *obj = ccls->create ();
      v.set_user (0, obj, ccls);
      ex.test (":");
      ccls->read (obj, ex);
    }

    ex.test ("]");
    return true;

  } else if (ex.test ("(")) {

    std::vector<tl::Variant> values;
    if (! ex.test (")")) {
      while (true) {
        values.push_back (tl::Variant ());
        ex.read (values.back ());
        if (ex.test (",")) {
          //  .. continue
        } else {
          ex.expect (")");
          break;
        }
      }
    }
    v = tl::Variant (values.begin (), values.end ());
    return true;

  } else if (ex.test ("{")) {

    v = tl::Variant::empty_array ();
    if (! ex.test ("}")) {
      while (true) {
        tl::Variant k, x;
        ex.read (k);
        if (ex.test ("=>")) {
          ex.read (x);
        }
        v.insert (k, x);
        if (ex.test (",")) {
          //  .. continue
        } else {
          ex.expect ("}");
          break;
        }
      }
    }
    return true;

  } else if (ex.try_read_word_or_quoted (s)) {

    v = tl::Variant (s);
    return true;

  } else {

    return false;

  }
}

} // namespace tl

