
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2026 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 "tlHash.h"

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

#if defined(HAVE_QT)

# include <QVariant>
# include <QStringList>

//  the Qt classes supported by QVariant:
# include <QBitArray>
# include <QBitmap>
# include <QBrush>
# include <QByteArray>
# include <QChar>
# include <QColor>
# include <QCursor>
# include <QDate>
# include <QDateTime>
# if QT_VERSION >= 0x040600
#   include <QEasingCurve>
# endif
# include <QFont>
# include <QVariantHash>
# include <QIcon>
# include <QImage>
# include <QKeySequence>
# include <QLine>
# include <QLineF>
# include <QVariantList>
# include <QLocale>
# include <QTransform>
# include <QMatrix4x4>
# include <QPalette>
# include <QPen>
# include <QPixmap>
# include <QPoint>
# include <QPointF>
# include <QPolygon>
# include <QQuaternion>
# include <QRect>
# include <QRectF>
# include <QRegExp>
# include <QRegion>
# include <QSize>
# include <QSizeF>
# include <QSizePolicy>
# include <QString>
# include <QStringList>
# include <QTextFormat>
# include <QTextLength>
# include <QTime>
# include <QUrl>
# include <QVector2D>
# include <QVector3D>
# include <QVector4D>

#endif

namespace tl
{

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

#if defined(HAVE_64BIT_COORD)

template <>
TL_PUBLIC 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 <>
TL_PUBLIC void extractor_impl (tl::Extractor &ex, __int128 &v)
{
  if (! test_extractor_impl (ex, v)) {
    ex.error (tl::to_string (tr ("Expected a value specification")));
  }
}

TL_PUBLIC 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

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

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

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

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

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

static std::vector<const VariantUserClassBase *> *sp_classes = 0;
static std::map<const VariantUserClassBase *, size_t> *sp_class_to_index = 0; 
static std::map<std::pair<std::string, bool>, size_t> *sp_class_index_by_name = 0;
static std::map<VariantUserClassTableKey, size_t> *sp_class_table = 0;
static std::map <std::string, const VariantUserClassBase *> s_user_type_by_name;

void
VariantUserClassBase::clear_class_table ()
{
  s_user_type_by_name.clear ();
}

std::string 
VariantUserClassBase::translate_class_name (const std::string &lc_clsname)
{
  //  Note: for pre-0.23 versions to be able to read PCells 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;
  }
}

void 
VariantUserClassBase::register_user_class (const std::string &name, const VariantUserClassBase *cls)
{
  s_user_type_by_name.insert (std::make_pair (name, cls));
}

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

  auto 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;
}

//  type-info based interface

const tl::VariantUserClassBase *VariantUserClassBase::instance (const std::type_info &type, bool is_const)
{
  tl_assert (sp_class_table != 0);

  const tl::VariantUserClassBase *inst = 0;

  auto c = sp_class_table->find (VariantUserClassTableKey (type, is_const));
  if (c == sp_class_table->end ()) {

    //  fallback: lookup by name and update if required - sometimes, there may be two different type info objects for the same thing
    auto c2i = sp_class_index_by_name->find (std::make_pair (std::string (type.name ()), is_const));
    tl_assert (c2i != sp_class_index_by_name->end ());
    sp_class_table->insert (std::make_pair (VariantUserClassTableKey (type, is_const), c2i->second));
    inst = sp_classes->operator[] (c2i->second);

  } else {
    inst = sp_classes->operator[] (c->second);
  }

  tl_assert (inst != 0);
  return inst;
}

void VariantUserClassBase::register_instance (const tl::VariantUserClassBase *inst, const std::type_info &type, bool is_const)
{
  if (! sp_class_table) {
    sp_class_table = new std::map<VariantUserClassTableKey, size_t> ();
  }
  if (! sp_classes) {
    sp_classes = new std::vector<const tl::VariantUserClassBase *> ();
  }
  if (! sp_class_index_by_name) {
    sp_class_index_by_name = new std::map<std::pair<std::string, bool>, size_t> ();
  }
  if (! sp_class_to_index) {
    sp_class_to_index = new std::map<const VariantUserClassBase *, size_t> ();
  }

  size_t index = sp_class_to_index->insert (std::make_pair (inst, sp_classes->size ())).first->second;
  if (index == sp_classes->size ()) {
    sp_classes->push_back (inst);
  }
  
  (*sp_class_table)[VariantUserClassTableKey (type, is_const)] = index;
  (*sp_class_index_by_name) [std::make_pair (std::string (type.name ()), is_const)] = index;
}

void VariantUserClassBase::unregister_instance (const tl::VariantUserClassBase *inst, const std::type_info &type, bool is_const)
{
  if (sp_class_table) {
    auto c = sp_class_table->find (VariantUserClassTableKey (type, is_const));
    if (c != sp_class_table->end ()) {
      if (sp_classes && c->second < sp_classes->size ()) {
        sp_classes->operator[] (c->second) = 0;
        while (!sp_classes->empty () && sp_classes->back () == 0) {
          sp_classes->pop_back ();
        }
      }
      sp_class_table->erase (c);
    }
  }

  if (sp_class_index_by_name) {
    auto cn = sp_class_index_by_name->find (std::make_pair (std::string (type.name ()), is_const));
    if (cn != sp_class_index_by_name->end ()) {
      sp_class_index_by_name->erase (cn);
    }
  }

  if (sp_class_to_index) {
    sp_class_to_index->erase (inst);
  }

  if (sp_class_table && sp_class_table->empty ()) {
    delete sp_class_table;
    sp_class_table = 0;
  }

  if (sp_classes && sp_classes->empty ()) {
    delete sp_classes;
    sp_classes = 0;
  }

  if (sp_class_index_by_name && sp_class_index_by_name->empty ()) {
    delete sp_class_index_by_name;
    sp_class_index_by_name = 0;
  }

  if (sp_class_to_index && sp_class_to_index->empty ()) {
    delete sp_class_to_index;
    sp_class_to_index = 0;
  }
}

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

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

Variant::Variant (const std::vector<char> &ba)
  : m_type (t_bytearray), m_string (0)
{
  m_var.m_bytearray = new std::vector<char> (ba);
}

Variant::Variant (std::vector<char> &&ba)
  : m_type (t_bytearray), m_string (0)
{
  m_var.m_bytearray = new std::vector<char> (ba);
}

#if defined(HAVE_QT)

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

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

Variant::Variant (const QVariant &v)
  : m_type (t_nil), m_string (0)
{
  switch (v.type ()) {
  case QVariant::Invalid:
    break;
  case QVariant::Bool:
    operator= (v.toBool ());
    break;
  case QVariant::ByteArray:
    operator= (v.toByteArray ());
    break;
  case QVariant::Double:
    operator= (v.toDouble ());
    break;
  case QVariant::Hash:
    {
      QHash<QString, QVariant> m = v.toHash ();
      set_array ();
      for (QHash<QString, QVariant>::const_iterator i = m.begin (); i != m.end (); ++i) {
        insert (tl::Variant (i.key ()), tl::Variant (i.value ()));
      }
    }
    break;
  case QVariant::Int:
    operator= (v.toInt ());
    break;
  case QVariant::List:
    {
      QList<QVariant> vl = v.toList ();
      set_list ();
      for (QList<QVariant>::const_iterator i = vl.begin (); i != vl.end (); ++i) {
        push (tl::Variant (*i));
      }
    }
    break;
  case QVariant::LongLong:
    operator= (v.toLongLong ());
    break;
  case QVariant::Map:
    {
      QMap<QString, QVariant> m = v.toMap ();
      set_array ();
      for (QMap<QString, QVariant>::const_iterator i = m.begin (); i != m.end (); ++i) {
        insert (tl::Variant (i.key ()), tl::Variant (i.value ()));
      }
    }
    break;
  case QVariant::StringList:
    {
      QStringList sl = v.toStringList ();
      set_list ();
      for (QStringList::const_iterator s = sl.begin (); s != sl.end (); ++s) {
        push (tl::Variant (*s));
      }
    }
    break;
  case QVariant::UInt:
    operator= (v.toUInt ());
    break;
  case QVariant::ULongLong:
    operator= (v.toULongLong ());
    break;
  //  special types supported by QVariant too:
  case QVariant::BitArray:
    operator= (tl::Variant (v.value<QBitArray> ()));
    break;
  case QVariant::Bitmap:
    operator= (tl::Variant (v.value<QBitmap> ()));
    break;
  case QVariant::Brush:
    operator= (tl::Variant (v.value<QBrush> ()));
    break;
  case QVariant::Color:
    operator= (tl::Variant (v.value<QColor> ()));
    break;
  case QVariant::Cursor:
    operator= (tl::Variant (v.value<QCursor> ()));
    break;
  case QVariant::Date:
    operator= (tl::Variant (v.value<QDate> ()));
    break;
  case QVariant::DateTime:
    operator= (tl::Variant (v.value<QDateTime> ()));
    break;
#if QT_VERSION >= 0x040700
  case QVariant::EasingCurve:
    operator= (tl::Variant (v.value<QEasingCurve> ()));
    break;
#endif
  case QVariant::Font:
    operator= (tl::Variant (v.value<QFont> ()));
    break;
  case QVariant::Icon:
    operator= (tl::Variant (v.value<QIcon> ()));
    break;
  case QVariant::Image:
    operator= (tl::Variant (v.value<QImage> ()));
    break;
  case QVariant::KeySequence:
    operator= (tl::Variant (v.value<QKeySequence> ()));
    break;
  case QVariant::Line:
    operator= (tl::Variant (v.value<QLine> ()));
    break;
  case QVariant::LineF:
    operator= (tl::Variant (v.value<QLineF> ()));
    break;
  case QVariant::Locale:
    operator= (tl::Variant (v.value<QLocale> ()));
    break;
  case QVariant::Transform:
    operator= (tl::Variant (v.value<QTransform> ()));
    break;
  case QVariant::Matrix4x4:
    operator= (tl::Variant (v.value<QMatrix4x4> ()));
    break;
  case QVariant::Palette:
    operator= (tl::Variant (v.value<QPalette> ()));
    break;
  case QVariant::Pen:
    operator= (tl::Variant (v.value<QPen> ()));
    break;
  case QVariant::Pixmap:
    operator= (tl::Variant (v.value<QPixmap> ()));
    break;
  case QVariant::Point:
    operator= (tl::Variant (v.value<QPoint> ()));
    break;
  case QVariant::PointF:
    operator= (tl::Variant (v.value<QPointF> ()));
    break;
  case QVariant::Polygon:
    operator= (tl::Variant (v.value<QPolygon> ()));
    break;
  case QVariant::Quaternion:
    operator= (tl::Variant (v.value<QQuaternion> ()));
    break;
  case QVariant::Rect:
    operator= (tl::Variant (v.value<QRect> ()));
    break;
  case QVariant::RectF:
    operator= (tl::Variant (v.value<QRectF> ()));
    break;
#if QT_VERSION < 0x60000
  case QVariant::RegExp:
    operator= (tl::Variant (v.value<QRegExp> ()));
    break;
#endif
  case QVariant::Region:
    operator= (tl::Variant (v.value<QRegion> ()));
    break;
  case QVariant::Size:
    operator= (tl::Variant (v.value<QSize> ()));
    break;
  case QVariant::SizeF:
    operator= (tl::Variant (v.value<QSizeF> ()));
    break;
  case QVariant::SizePolicy:
    operator= (tl::Variant (v.value<QSizePolicy> ()));
    break;
  case QVariant::TextFormat:
    operator= (tl::Variant (v.value<QTextFormat> ()));
    break;
  case QVariant::TextLength:
    operator= (tl::Variant (v.value<QTextLength> ()));
    break;
  case QVariant::Time:
    operator= (tl::Variant (v.value<QTime> ()));
    break;
  case QVariant::Url:
    operator= (tl::Variant (v.value<QUrl> ()));
    break;
  case QVariant::Vector2D:
    operator= (tl::Variant (v.value<QVector2D> ()));
    break;
  case QVariant::Vector3D:
    operator= (tl::Variant (v.value<QVector3D> ()));
    break;
  case QVariant::Vector4D:
    operator= (tl::Variant (v.value<QVector4D> ()));
    break;
  default:
  case QVariant::String:
    operator= (v.toString ());
    break;
  }
}

#endif

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

Variant::Variant (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 (s != 0 ? t_string : t_nil)
{
  if (s) {
    m_string = new char [strlen (s) + 1];
    strcpy (m_string, s);
  } else {
    m_string = 0;
  }
}

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 (Variant &&v)
  : m_type (t_nil), m_string (0)
{
  swap (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_bytearray) {
    delete m_var.m_bytearray;
#if defined(HAVE_QT)
  } else if (m_type == t_qstring) {
    delete m_var.m_qstring;
  } else if (m_type == t_qbytearray) {
    delete m_var.m_qbytearray;
#endif
  } else if (m_type == t_stdstring) {
    delete m_var.m_stdstring;
  } else if (m_type == t_user_ref) {
    WeakOrSharedPtr *ptr = reinterpret_cast<WeakOrSharedPtr *> (m_var.mp_user_ref.ptr);
    ptr->~WeakOrSharedPtr();
  } else if (m_type == t_user) {
    if (m_var.mp_user.object && m_var.mp_user.shared) {
      m_var.mp_user.cls->destroy (m_var.mp_user.object);
    }
  }
  m_type = t_nil;
}

Variant &
Variant::operator= (const char *s)
{
  if (! s) {
    reset ();
  } else 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= (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 std::vector<char> &s)
{
  if (m_type == t_bytearray && &s == m_var.m_bytearray) {
    //  we are assigning to ourselves
  } else {
    std::vector<char> *snew = new std::vector<char> (s);
    reset ();
    m_type = t_bytearray;
    m_var.m_bytearray = snew;
  }
  return *this;
}

Variant &
Variant::operator= (std::vector<char> &&s)
{
  if (m_type == t_bytearray && &s == m_var.m_bytearray) {
    //  we are assigning to ourselves
  } else {
    std::vector<char> *snew = new std::vector<char> (s);
    reset ();
    m_type = t_bytearray;
    m_var.m_bytearray = snew;
  }
  return *this;
}

#if defined(HAVE_QT)

Variant &
Variant::operator= (const QByteArray &qs)
{
  if (qs.isNull ()) {
    reset ();
  } else 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 (qs.isNull ()) {
    reset ();
  } else 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;
}

#endif

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= (Variant &&v)
{
  swap (v);
  return *this;
}

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

    //  Clearing *this through swap delays the destruction of
    //  this's content. This is important if we assign a list member
    //  of this to this itself (this happens in tl::Expression).
    tl::Variant vv;
    vv.swap (*this);

    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_bytearray) {
      m_var.m_bytearray = new std::vector<char> (*v.m_var.m_bytearray);
#if defined(HAVE_QT)
    } 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);
#endif
    } 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 (m_type == t_user) {
      m_var.mp_user.cls = v.m_var.mp_user.cls;
      if (v.m_var.mp_user.object) {
        if (v.m_var.mp_user.shared) {
          m_var.mp_user.object = v.m_var.mp_user.cls->clone (v.m_var.mp_user.object);
          m_var.mp_user.shared = true;
        } else {
          m_var.mp_user.object = v.m_var.mp_user.object;
          m_var.mp_user.shared = false;
        }
      } else {
        m_var.mp_user.object = 0;
      }
    } else if (m_type == t_user_ref) {
      m_var.mp_user_ref.cls = v.m_var.mp_user_ref.cls;
      const WeakOrSharedPtr *ptr = reinterpret_cast<const WeakOrSharedPtr *> (v.m_var.mp_user_ref.ptr);
      new (m_var.mp_user_ref.ptr) WeakOrSharedPtr (*ptr);
    }

  } 
  return *this;
}

size_t
Variant::hash () const
{
  size_t h = 0;

  if (m_type == t_double) {
    h = tl::hfunc (m_var.m_double);
  } else if (m_type == t_float) {
    h = tl::hfunc (m_var.m_float);
  } else if (m_type == t_bool) {
    h = tl::hfunc (m_var.m_bool);
  } else if (m_type == t_uchar) {
    h = tl::hfunc (m_var.m_uchar);
  } else if (m_type == t_schar) {
    h = tl::hfunc (m_var.m_schar);
  } else if (m_type == t_char) {
    h = tl::hfunc (m_var.m_char);
  } else if (m_type == t_ushort) {
    h = tl::hfunc (m_var.m_ushort);
  } else if (m_type == t_short) {
    h = tl::hfunc (m_var.m_short);
  } else if (m_type == t_uint) {
    h = tl::hfunc (m_var.m_uint);
  } else if (m_type == t_int) {
    h = tl::hfunc (m_var.m_int);
  } else if (m_type == t_ulong) {
    h = tl::hfunc (m_var.m_ulong);
  } else if (m_type == t_long) {
    h = tl::hfunc (m_var.m_long);
  } else if (m_type == t_longlong) {
    h = tl::hfunc (m_var.m_longlong);
  } else if (m_type == t_ulonglong) {
    h = tl::hfunc (m_var.m_ulonglong);
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    h = tl::hfunc (m_var.m_int128);
#endif
  } else if (m_type == t_id) {
    h = tl::hfunc (m_var.m_id);
  } else if (m_type == t_bytearray) {
    h = tl::hfunc (*m_var.m_bytearray);
#if defined(HAVE_QT)
  } else if (m_type == t_qstring) {
    h = tl::hfunc (*m_var.m_qstring);
  } else if (m_type == t_qbytearray) {
    h = tl::hfunc (*m_var.m_qbytearray);
#endif
  } else if (m_type == t_stdstring) {
    h = tl::hfunc (*m_var.m_stdstring);
  } else if (m_type == t_string) {
    for (const char *cp = m_string; *cp; ++cp) {
      h = tl::hfunc (*cp, h);
    }
  } else if (m_type == t_list) {
    h = tl::hfunc (*m_var.m_list);
  } else if (m_type == t_array) {
    h = tl::hfunc (*m_var.m_array);
  } else if (m_type == t_user) {
    //  NOTE: this involves pointers ...
    h = tl::hfunc (m_var.mp_user.object, tl::hfunc (m_var.mp_user.cls, 0));
  } else if (m_type == t_user_ref) {
    const WeakOrSharedPtr *ptr = reinterpret_cast<const WeakOrSharedPtr *> (m_var.mp_user_ref.ptr);
    h = tl::hfunc (ptr->get (), tl::hfunc (m_var.mp_user_ref.cls, 0));
  }

  return h;
}

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 bool
is_string_type (Variant::type type)
{
  switch (type) {
  case Variant::t_stdstring:
  case Variant::t_string:
    return true;
  default:
    return false;
  }
}

/**
 *  @brief normalized_type
 */
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:
    //  NOTE: char can be signed or unsigned
    if (std::numeric_limits<char>::min () < 0) {
      return Variant::t_longlong;
    } else {
      return Variant::t_ulonglong;
    }
  case Variant::t_schar:
  case Variant::t_short:
  case Variant::t_int:
  case Variant::t_long:
  case Variant::t_longlong:
    return Variant::t_longlong;
  case Variant::t_uchar:
  case Variant::t_ushort:
  case Variant::t_uint:
  case Variant::t_ulong:
  case Variant::t_ulonglong:
    return Variant::t_ulonglong;
  case Variant::t_stdstring:
  case Variant::t_string:
    return Variant::t_string;
  case Variant::t_bytearray:
    return Variant::t_bytearray;
  default:
#if defined(HAVE_64BIT_COORD)
  case Variant::t_int128:
#endif
  case Variant::t_bool:
  case Variant::t_nil:
#if defined(HAVE_QT)
  case Variant::t_qstring:
  case Variant::t_qbytearray:
#endif
    return type;
  }
}

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

  if (is_integer_type (type1)) {
    type1 = Variant::t_double;
  }
  if (is_integer_type (type2)) {
    type2 = Variant::t_double;
  }

  //  Ruby sometimes produces byte arrays instead of strings, so use those
  //  as common type for strings and byte arrays
  if (is_string_type (type1) && type2 == Variant::t_bytearray) {
    type1 = Variant::t_bytearray;
  } else if (is_string_type (type2) && type1 == Variant::t_bytearray) {
    type2 = Variant::t_bytearray;
  }

  return std::make_pair (type1, type2);
}

/**
 *  @brief A more rigid definition of "normalized_type" with two arguments
 *
 *  The former version allows casting floats to integers while this version
 *  treats floats differently.
 *
 *  This version is employed in Variant::less and Variant::equal.
 */
inline std::pair<bool, Variant::type>
normalized_type_rigid (Variant::type type1, Variant::type type2)
{
  type1 = normalized_type (type1);
  type2 = normalized_type (type2);

  //  Ruby sometimes produces byte arrays instead of strings, so use those
  //  as common type for strings and byte arrays
  if (is_string_type (type1) && type2 == Variant::t_bytearray) {
    type1 = Variant::t_bytearray;
  } else if (is_string_type (type2) && type1 == Variant::t_bytearray) {
    type2 = Variant::t_bytearray;
  }

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

static const double epsilon = 1e-13;

//  NOTE: in order to be able to use Variant for std::map or std::set
//  keys we have to establish a weak order. This means we need to
//  consider NAN and INFINITY too.

static int numeric_class (double x)
{
  if (std::isnan (x)) {
    return 2;
  } else {
    return std::isinf (x) ? (x < 0 ? -1 : 1) : 0;
  }
}

static inline bool fequal (double a, double b)
{
  if (numeric_class (a) != 0 || numeric_class (b) != 0) {
    return numeric_class (a) == numeric_class (b);
  } else {
    double avg = 0.5 * (fabs (a) + fabs (b));
    return fabs (a - b) <= epsilon * avg;
  }
}

static inline bool fless (double a, double b)
{
  if (fequal (a, b)) {
    return false;
  } else if (numeric_class (a) != 0 || numeric_class (b) != 0) {
    return numeric_class (a) < numeric_class (b);
  } else {
    return a < b;
  }
}

bool 
Variant::equal_core (const tl::Variant &d, type t) const
{
  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 fequal (to_double (), d.to_double ());
  } else if (t == t_string) {
    return strcmp (to_string (), d.to_string ()) == 0;
  } else if (t == t_bytearray) {
    if (m_type == t_bytearray && d.m_type == t_bytearray) {
      return *m_var.m_bytearray == *d.m_var.m_bytearray;
    } else {
      return to_bytearray () == d.to_bytearray ();
    }
#if defined(HAVE_QT)
  } 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;
#endif
  } 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 (t == t_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 if (t == t_user_ref) {
    const tl::Object *self = reinterpret_cast<const WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ();
    const tl::Object *other = reinterpret_cast<const WeakOrSharedPtr *> (d.m_var.mp_user_ref.ptr)->get ();
    return m_var.mp_user_ref.cls == d.m_var.mp_user_ref.cls && m_var.mp_user_ref.cls->equal (m_var.mp_user_ref.cls->deref_proxy_const (self), m_var.mp_user_ref.cls->deref_proxy_const (other));
  } else {
    return false;
  }
}

bool 
Variant::less_core (const tl::Variant &d, type t) const
{
  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 fless (to_double (), d.to_double ());
  } else if (t == t_string) {
    return strcmp (to_string (), d.to_string ()) < 0;
  } else if (t == t_bytearray) {
    if (m_type == t_bytearray && d.m_type == t_bytearray) {
      return *m_var.m_bytearray < *d.m_var.m_bytearray;
    } else {
      return to_bytearray () < d.to_bytearray ();
    }
#if defined(HAVE_QT)
  } 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;
#endif
  } 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 (t == t_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 if (t == t_user_ref) {
    if (m_var.mp_user_ref.cls != d.m_var.mp_user_ref.cls) {
      //  TODO: there should be some class Id that can be used for comparison (that is more predictable)
      return m_var.mp_user_ref.cls < d.m_var.mp_user_ref.cls;
    }
    const tl::Object *self = reinterpret_cast<const WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ();
    const tl::Object *other = reinterpret_cast<const WeakOrSharedPtr *> (d.m_var.mp_user_ref.ptr)->get ();
    return m_var.mp_user_ref.cls->less (m_var.mp_user_ref.cls->deref_proxy_const (self), m_var.mp_user_ref.cls->deref_proxy_const (other));
  } else {
    return false;
  }
}

bool
Variant::operator== (const tl::Variant &d) const
{
  auto tt = normalized_types (m_type, d.m_type);
  if (tt.first != tt.second) {
    return false;
  } else {
    return equal_core (d, tt.first);
  }
}

bool
Variant::operator< (const tl::Variant &d) const
{
  auto tt = normalized_types (m_type, d.m_type);
  if (tt.first != tt.second) {
    return tt.first < tt.second;
  } else {
    return less_core (d, tt.first);
  }
}

bool
Variant::equal (const Variant &d) const
{
  std::pair<bool, type> tt = normalized_type_rigid (m_type, d.m_type);
  if (! tt.first) {
    return false;
  } else {
    return equal_core (d, tt.second);
  }
}

bool
Variant::less (const Variant &d) const
{
  std::pair<bool, type> tt = normalized_type_rigid (m_type, d.m_type);
  if (! tt.first) {
    return normalized_type (m_type) < normalized_type (d.m_type);
  } else {
    return less_core (d, tt.second);
  }
}

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 ();
#if defined(HAVE_QT)
  case t_qstring:
  case t_qbytearray:
#endif
  case t_bytearray:
  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;
#if defined(HAVE_QT)
  case t_qstring:
  case t_qbytearray:
#endif
  case t_bytearray:
  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:
#if defined(HAVE_QT)
  case t_qstring:
  case t_qbytearray:
#endif
  case t_bytearray:
  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 <= double (std::numeric_limits<unsigned long long>::max ()) && m_var.m_double >= 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:
#if defined(HAVE_QT)
  case t_qstring:
  case t_qbytearray:
#endif
  case t_stdstring:
  case t_bytearray:
    {
      tl::Extractor ex (to_string ());
      try {
        unsigned long long ll;
        return ex.try_read (ll) && ex.at_end ();
      } catch (...) {
        return false;
      }
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_longlong () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= double (std::numeric_limits<long long>::max ()) && m_var.m_double >= 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:
#if defined(HAVE_QT)
  case t_qstring:
  case t_qbytearray:
#endif
  case t_stdstring:
  case t_bytearray:
    {
      tl::Extractor ex (to_string ());
      try {
        long long ll;
        return ex.try_read (ll) && ex.at_end ();
      } catch (...) {
        return false;
      }
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_ulong () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= double (std::numeric_limits<unsigned long>::max ()) && m_var.m_double >= double (std::numeric_limits<unsigned long>::min ());
  case t_float:
    return m_var.m_float <= float (std::numeric_limits<unsigned long>::max ()) && m_var.m_float >= 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 && (unsigned long long) m_var.m_longlong <= (unsigned 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:
#if defined(HAVE_QT)
  case t_qstring:
  case t_qbytearray:
#endif
  case t_stdstring:
  case t_bytearray:
    {
      tl::Extractor ex (to_string ());
      try {
        unsigned long l;
        return ex.try_read (l) && ex.at_end ();
      } catch (...) {
        return false;
      }
    }
  default:
    return false;
  }
}

bool 
Variant::can_convert_to_long () const
{
  switch (m_type) {
  case t_double:
    return m_var.m_double <= double (std::numeric_limits<long>::max ()) && m_var.m_double >= double (std::numeric_limits<long>::min ());
  case t_float:
    return m_var.m_float <= float (std::numeric_limits<long>::max ()) && m_var.m_float >= 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:
#if defined(HAVE_QT)
  case t_qstring:
  case t_qbytearray:
#endif
  case t_stdstring:
  case t_bytearray:
    {
      tl::Extractor ex (to_string ());
      try {
        long l;
        return ex.try_read (l) && ex.at_end ();
      } catch (...) {
        return false;
      }
    }
  default:
    return false;
  }
}

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

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

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 ());
}

#if defined(HAVE_QT)

QByteArray
Variant::to_qbytearray () const
{
  if (m_type == t_qbytearray) {
    return *m_var.m_qbytearray;
  } else if (m_type == t_bytearray) {
    return QByteArray (&m_var.m_bytearray->front (), int (m_var.m_bytearray->size ()));
  } 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 if (m_type == t_bytearray) {
    return QString::fromUtf8 (&m_var.m_bytearray->front ());
  } else {
    return tl::to_qstring (to_string ());
  }
}

#endif

std::vector<char>
Variant::to_bytearray () const
{
  if (m_type == t_bytearray) {
    return *m_var.m_bytearray;
#if defined(HAVE_QT)
  } else if (m_type == t_qstring) {
    QByteArray ba = m_var.m_qstring->toUtf8 ();
    return std::vector<char> (ba.constBegin (), ba.constEnd ());
  } else if (m_type == t_qbytearray) {
    return std::vector<char> (m_var.m_qbytearray->constBegin (), m_var.m_qbytearray->constEnd ());
#endif
  } else if (m_type == t_stdstring) {
    return std::vector<char> (m_var.m_stdstring->begin (), m_var.m_stdstring->end ());
  } else {
    //  TODO: maybe some other conversion makes sense? I.e. byte representation of int?
    std::string s = to_string ();
    return std::vector<char> (s.begin (), s.end ());
  }
}

std::string
Variant::to_stdstring () const
{
  if (m_type == t_stdstring) {
    return *m_var.m_stdstring;
  } else if (m_type == t_bytearray) {
    return std::string (m_var.m_bytearray->begin (), m_var.m_bytearray->end ());
#if defined(HAVE_QT)
  } 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 ());
#endif
  } 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_bytearray) {

    //  need to add a terminating 0 for safety
    if (! m_string) {
      size_t n = m_var.m_bytearray->size ();
      m_string = new char [n + 1];
      strncpy (m_string, &m_var.m_bytearray->front (), n);
      m_string[n] = 0;
    }

    return m_string;

#if defined(HAVE_QT)
  } else if (m_type == t_qbytearray) {

    //  need to add a terminating 0 for safety
    if (! m_string) {
      size_t n = m_var.m_qbytearray->size ();
      m_string = new char [n + 1];
      strncpy (m_string, m_var.m_qbytearray->constData (), n);
      m_string[n] = 0;
    }

    return m_string;

#endif

  // 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, 15);
    } else if (m_type == t_float) {
      r = tl::to_string (m_var.m_float, 7);
    } else if (m_type == t_char) {
      r += 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);
#if defined(HAVE_QT)
    } else if (m_type == t_qstring) {
      r = tl::to_string (*m_var.m_qstring);
#endif
    } else if (m_type == t_list) {
      r += "(";
      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 ();
      }
      r += ")";
    } else if (m_type == t_array) {
      r += "{";
      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 ();
      }
      r += "}";
    } else if (m_type == t_id)  {
      r = "[id" + tl::to_string (m_var.m_id) + "]";
    } else if (m_type == t_user) {
      void *obj = m_var.mp_user.object;
      if (obj) {
        r = m_var.mp_user.cls->to_string (obj);
      } else {
        r = "[class ";
        r += m_var.mp_user.cls->name ();
        r += "]";
      }
    } else if (m_type == t_user_ref) {
      const void *obj = m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ());
      if (obj) {
        r = m_var.mp_user_ref.cls->to_string (obj);
      } else {
        r = "[null]";
      }
    } 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;
#if defined(HAVE_QT)
  } else if (m_type == t_qbytearray || m_type == t_bytearray) {
    tl::Extractor ex (to_string ());
    __int128 l = 0;
    ex.read (l);
    return l;
  } else if (m_type == t_qstring) {
    std::string s (to_string ());
    tl::Extractor ex (s.c_str ());
    __int128 l = 0;
    ex.read (l);
    return l;
#endif
  } 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_string) {
    std::string s (to_string ());
    tl::Extractor ex (s.c_str ());
    __int128 l = 0;
    ex.read (l);
    return l;
  } else if (m_type == t_user) {
    return m_var.mp_user.cls->to_int (m_var.mp_user.object);
  } else if (m_type == t_user_ref) {
    return m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ()));
  } 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 (unsigned long long) 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;
#if defined(HAVE_QT)
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray || m_type == t_bytearray) {
#else
  } else if (m_type == t_string || m_type == t_bytearray) {
#endif
    unsigned long long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else if (m_type == t_user) {
    return (unsigned long long) m_var.mp_user.cls->to_int (m_var.mp_user.object);
  } else if (m_type == t_user_ref) {
    return (unsigned long long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ()));
  } 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 (long long) 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;
#if defined(HAVE_QT)
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray || m_type == t_bytearray) {
#else
  } else if (m_type == t_string || m_type == t_bytearray) {
#endif
    long long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else if (m_type == t_user) {
    return (long long) m_var.mp_user.cls->to_int (m_var.mp_user.object);
  } else if (m_type == t_user_ref) {
    return (long long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ()));
  } 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 (unsigned long) (m_var.m_ulonglong);
  } else if (m_type == t_longlong) {
    return (unsigned long) (m_var.m_longlong);
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    return (unsigned long) (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;
#if defined(HAVE_QT)
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray || m_type == t_bytearray) {
#else
  } else if (m_type == t_string || m_type == t_bytearray) {
#endif
    unsigned long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else if (m_type == t_user) {
    return (unsigned long) m_var.mp_user.cls->to_int (m_var.mp_user.object);
  } else if (m_type == t_user_ref) {
    return (unsigned long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ()));
  } 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 long (m_var.m_ulonglong);
  } else if (m_type == t_longlong) {
    return long (m_var.m_longlong);
#if defined(HAVE_64BIT_COORD)
  } else if (m_type == t_int128) {
    return long (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;
#if defined(HAVE_QT)
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray || m_type == t_bytearray) {
#else
  } else if (m_type == t_string || m_type == t_bytearray) {
#endif
    long l = 0;
    tl::from_string (to_string (), l);
    return l;
  } else if (m_type == t_user) {
    return (long) m_var.mp_user.cls->to_int (m_var.mp_user.object);
  } else if (m_type == t_user_ref) {
    return (long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ()));
  } 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;
#if defined(HAVE_QT)
  } else if (m_type == t_string || m_type == t_qstring || m_type == t_qbytearray || m_type == t_bytearray) {
#else
  } else if (m_type == t_string || m_type == t_bytearray) {
#endif
    double d = 0;
    tl::from_string (to_string (), d);
    return d;
  } else if (m_type == t_user) {
    return m_var.mp_user.cls->to_double (m_var.mp_user.object);
  } else if (m_type == t_user_ref) {
    return m_var.mp_user_ref.cls->to_double (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ()));
  } else {
    return 0;
  }
}

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

const void *
Variant::native_ptr () const
{
  switch (m_type) {
  case t_user:
    return m_var.mp_user.object;
  case t_user_ref:
    return reinterpret_cast<const WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->get ();
  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;
#if defined(HAVE_QT)
  case t_qstring:
    return m_var.m_qstring;
  case t_qbytearray:
    return m_var.m_qbytearray;
#endif
  case t_bytearray:
    return m_var.m_bytearray;
  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 (), 15);
  } 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_a_string ()) {
    return tl::to_quoted_string (to_stdstring ());
  } else if (is_a_bytearray ()) {
    return tl::to_quoted_string (to_stdstring ()) + "b";
  } 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_char ()) {
    std::string s;
    s += to_char ();
    return tl::to_quoted_string (s) + "c";
  } else if (is_id ()) {
    return "[id" + tl::to_string (m_var.m_id) + "]";
  } else if (is_user ()) {
    if (user_cls ()) {
      //  for downward compatibility we use lower case name + do a translation
      return "[" + tl::VariantUserClassBase::translate_class_name (tl::to_lower_case (user_cls ()->name ())) + ":" + user_cls ()->to_string (to_user ()) + "]";
    } else {
      return "[unknown_user_type]";
    }
  } else {
    return "";
  }
}

void 
tl::Variant::swap (tl::Variant &other)
{
  ValueHolder a = m_var;
  if (m_type == t_user_ref) {
    new (a.mp_user_ref.ptr) tl::WeakOrSharedPtr (*reinterpret_cast <tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr));
    reinterpret_cast <tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->~WeakOrSharedPtr ();
  }
  m_var = other.m_var;
  if (other.m_type == t_user_ref) {
    new (m_var.mp_user_ref.ptr) tl::WeakOrSharedPtr (*reinterpret_cast <tl::WeakOrSharedPtr *> (other.m_var.mp_user_ref.ptr));
    reinterpret_cast <tl::WeakOrSharedPtr *> (other.m_var.mp_user_ref.ptr)->~WeakOrSharedPtr ();
  }
  other.m_var = a;
  if (m_type == t_user_ref) {
    new (other.m_var.mp_user_ref.ptr) tl::WeakOrSharedPtr (*reinterpret_cast <tl::WeakOrSharedPtr *> (a.mp_user_ref.ptr));
    reinterpret_cast <tl::WeakOrSharedPtr *> (a.mp_user_ref.ptr)->~WeakOrSharedPtr ();
  }

  std::swap (m_type, other.m_type);
  std::swap (m_string, other.m_string);
}

#if defined(HAVE_QT)

QVariant Variant::to_qvariant () const
{
  switch (m_type) {
  case t_nil:
    return QVariant ();
  case t_double:
    return QVariant (m_var.m_double);
  case t_float:
    return QVariant ((double) m_var.m_float);
  case t_char:
    return QVariant ((unsigned int) m_var.m_char);
  case t_schar:
    return QVariant ((int) m_var.m_schar);
  case t_uchar:
    return QVariant ((unsigned int) m_var.m_uchar);
  case t_short:
    return QVariant ((int) m_var.m_short);
  case t_ushort:
    return QVariant ((unsigned int) m_var.m_ushort);
  case t_int:
    return QVariant (m_var.m_int);
  case t_uint:
    return QVariant (m_var.m_uint);
  case t_long:
    //  TODO: will int == long always?
    return QVariant ((int) m_var.m_long);
  case t_ulong:
    //  TODO: will unsigned int == long always?
    return QVariant ((unsigned int) m_var.m_ulong);
  case t_longlong:
    return QVariant (m_var.m_longlong);
  case t_ulonglong:
    return QVariant (m_var.m_ulonglong);
#if defined(HAVE_64BIT_COORD)
  case t_int128:
    //  TODO: support for int128 in QVariant?
    return QVariant ((double) m_var.m_int128);
#endif
  case t_bool:
    return QVariant (m_var.m_bool);
  case t_stdstring:
    return QVariant (tl::to_qstring (*m_var.m_stdstring));
  case t_string:
    return QVariant (tl::to_qstring (m_string));
#if defined(HAVE_QT)
  case t_qstring:
    return QVariant (*m_var.m_qstring);
  case t_qbytearray:
    return QVariant (*m_var.m_qbytearray);
#endif
  case t_bytearray:
    return QVariant (to_qbytearray ());
  case t_list:
    {
      QList<QVariant> l;
      for (std::vector<tl::Variant>::const_iterator v = m_var.m_list->begin (); v != m_var.m_list->end (); ++v) {
        l.append (v->to_qvariant ());
      }
      return QVariant (l);
    }
  case t_array:
    {
      QMap<QString, QVariant> a;
      for (const_array_iterator v = m_var.m_array->begin (); v != m_var.m_array->end (); ++v) {
        a.insert (v->first.to_qstring (), v->second.to_qvariant ());
      }
      return QVariant (a);
    }
  case t_id:
    return QVariant ((unsigned int) m_var.m_id);
  case t_user:
    {
      const tl::VariantUserClassBase *cls = user_cls ();
      //  try any of the other supported classes of QVariant:
      if (dynamic_cast<const tl::VariantUserClass<QBitArray> *> (cls)) {
        return QVariant (to_user<QBitArray> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QBitArray> *> (cls)) {
        return QVariant (to_user<QBitArray> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QBitmap> *> (cls)) {
        return QVariant (to_user<QBitmap> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QBrush> *> (cls)) {
        return QVariant (to_user<QBrush> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QChar> *> (cls)) {
        return QVariant (to_user<QChar> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QColor> *> (cls)) {
        return QVariant (to_user<QColor> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QCursor> *> (cls)) {
        return QVariant (to_user<QCursor> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QDate> *> (cls)) {
        return QVariant (to_user<QDate> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QDateTime> *> (cls)) {
        return QVariant (to_user<QDateTime> ());
  #if QT_VERSION >= 0x040700
      } else if (dynamic_cast<const tl::VariantUserClass<QEasingCurve> *> (cls)) {
        return QVariant (to_user<QEasingCurve> ());
  #endif
      } else if (dynamic_cast<const tl::VariantUserClass<QFont> *> (cls)) {
        return QVariant (to_user<QFont> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QIcon> *> (cls)) {
        return QVariant (to_user<QIcon> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QImage> *> (cls)) {
        return QVariant (to_user<QImage> ());
  #if QT_VERSION >= 0x050000
      } else if (dynamic_cast<const tl::VariantUserClass<QKeySequence> *> (cls)) {
        return QVariant (to_user<QKeySequence> ());
  #endif
      } else if (dynamic_cast<const tl::VariantUserClass<QLine> *> (cls)) {
        return QVariant (to_user<QLine> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QLineF> *> (cls)) {
        return QVariant (to_user<QLineF> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QLocale> *> (cls)) {
        return QVariant (to_user<QLocale> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QTransform> *> (cls)) {
        return QVariant (to_user<QTransform> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QMatrix4x4> *> (cls)) {
        return QVariant (to_user<QMatrix4x4> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QPalette> *> (cls)) {
        return QVariant (to_user<QPalette> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QPen> *> (cls)) {
        return QVariant (to_user<QPen> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QPixmap> *> (cls)) {
        return QVariant (to_user<QPixmap> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QPoint> *> (cls)) {
        return QVariant (to_user<QPoint> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QPointF> *> (cls)) {
        return QVariant (to_user<QPointF> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QPolygon> *> (cls)) {
        return QVariant (to_user<QPolygon> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QQuaternion> *> (cls)) {
        return QVariant (to_user<QQuaternion> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QRect> *> (cls)) {
        return QVariant (to_user<QRect> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QRectF> *> (cls)) {
        return QVariant (to_user<QRectF> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QRegExp> *> (cls)) {
        return QVariant (to_user<QRegExp> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QRegion> *> (cls)) {
        return QVariant (to_user<QRegion> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QSize> *> (cls)) {
        return QVariant (to_user<QSize> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QSizeF> *> (cls)) {
        return QVariant (to_user<QSizeF> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QSizePolicy> *> (cls)) {
        return QVariant (to_user<QSizePolicy> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QTextFormat> *> (cls)) {
        return QVariant (to_user<QTextFormat> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QTextLength> *> (cls)) {
        return QVariant (to_user<QTextLength> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QTime> *> (cls)) {
        return QVariant (to_user<QTime> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QUrl> *> (cls)) {
        return QVariant (to_user<QUrl> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QVector2D> *> (cls)) {
        return QVariant (to_user<QVector2D> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QVector3D> *> (cls)) {
        return QVariant (to_user<QVector3D> ());
      } else if (dynamic_cast<const tl::VariantUserClass<QVector4D> *> (cls)) {
        return QVariant (to_user<QVector4D> ());
      } else {
        tl::Variant var;
        cls->to_variant (to_user (), var);
        return var.to_qvariant ();
      }
    }
  default:
    return QVariant ();
  }
}

#endif

void Variant::set_user_object (void *obj, bool shared)
{
  m_var.mp_user.object = obj;
  m_var.mp_user.shared = shared;
}

bool Variant::user_is_const () const
{
  tl_assert (is_user ());
  return user_cls ()->is_const ();
}

bool Variant::user_is_ref () const
{
  if (m_type == t_user) {
    return !m_var.mp_user.shared;
  } else if (m_type == t_user_ref) {
    return !reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr)->is_shared ();
  } else {
    return false;
  }
}

void Variant::user_destroy ()
{
  tl_assert (is_user ());
  void *obj = to_user ();
  if (obj) {
    user_cls ()->destroy (obj);
    m_type = t_nil;
  }
}

void *Variant::user_take ()
{
  tl_assert (is_user ());
  void *obj = to_user ();
  if (obj) {
    m_type = t_nil;
  }
  return obj;
}

void *Variant::user_unshare () const
{
  tl_assert (is_user () && ! user_is_ref ());

  if (m_type == t_user) {
    Variant *nc_this = const_cast<Variant *> (this);
    nc_this->m_var.mp_user.shared = false;
  } else if (m_type == t_user_ref) {
    tl::WeakOrSharedPtr *wptr = const_cast<tl::WeakOrSharedPtr *> (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr));
    wptr->unshare ();
  }

  return const_cast<void *> (to_user ());
}

void Variant::user_change_constness (bool constness)
{
  tl_assert (is_user ());

  if (m_type == t_user) {
    m_var.mp_user.cls = m_var.mp_user.cls->change_constness (constness);
  } else if (m_type == t_user_ref) {
    m_var.mp_user_ref.cls = m_var.mp_user_ref.cls->change_constness (constness);
  }
}

void Variant::user_assign (const tl::Variant &other)
{
  tl_assert (is_user ());
  tl_assert (other.is_user ());
  if (user_cls () == other.user_cls ()) {
    user_cls ()->assign (to_user (), other.to_user ());
  }
}

tl::Variant Variant::user_dup () const
{
  tl_assert (is_user ());
  return tl::Variant (user_cls ()->clone (to_user ()), user_cls (), true);
}

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

template <>
TL_PUBLIC 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 = tl::VariantUserClassBase::find_cls_by_name (cls);
    if (ccls) {
      void *obj = ccls->create ();
      v.set_user (obj, ccls, true);
      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)) {

    if (ex.test ("c") || ex.test ("C")) {
      v = s.empty () ? tl::Variant () : tl::Variant ((char) s.front ());
    } else if (ex.test ("b") || ex.test ("B")) {
      std::vector<char> cv (s.begin (), s.end ());
      v = tl::Variant (std::move (cv));
    } else {
      v = tl::Variant (s);
    }

    return true;

  } else {

    return false;

  }
}

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

} // namespace tl

