/*
 * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
 *
 * 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; version 2 of the
 * License.
 *
 * 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 "base/string_utilities.h"
#include "base/threading.h"

#include "grtpp.h"
#include "grtpp_util.h"
#include "grtpp_undo_manager.h"

#include <glib.h>

#ifdef GRT_LEAK_DETECTOR_ENABLED
#include <iostream>
#endif

#ifdef GRT_LEAK_DETECTOR_RECORD_CALL_STACK
#ifdef _WIN32
#include "stacktrace/win_stack_trace.h"
#else
std::string get_call_stack()
{
    return "No call stack recording impemented for current platform";
}
#endif
#endif


using namespace grt;
using namespace grt::internal;
using namespace base;

static void register_base_class(GRT *grt)
{
  MetaClass *mc= grt->get_metaclass(Object::static_class_name());

  mc->bind_allocator(0);

  // nothing in the base class
}


ClassRegistry::ClassRegistry()
{
  // register the root class
  classes[Object::static_class_name()]= &register_base_class;
}


void ClassRegistry::register_all(GRT *grt)
{
 for (std::map<std::string,ClassRegistrationFunction>::const_iterator iter= classes.begin();
      iter != classes.end(); ++iter)
 {
   // Register classes only for loaded meta classes.
   if (!grt->get_metaclass(iter->first))
   {
     if (grt->verbose())
       grt->send_warning("MetaClass " + iter->first + " is registered but was not loaded from a XML");
     continue;
   }
   (*iter->second)(grt);
 }
}


ClassRegistry *ClassRegistry::get_instance()
{
 static ClassRegistry *instance= new ClassRegistry();
 return instance;
}


//--------------------------------------------------------------------------------------------------

std::string Integer::debugDescription(const std::string &indentation) const
{
  // Simple values don't use indentation as they are always on a RHS.
  return toString();
}

std::string Integer::toString() const
{
  return base::to_string(_value);
}

Integer::Integer(storage_type value)
: _value(value)
{
}


Integer* Integer::get(storage_type value)
{
  static Integer* one= (Integer*)((new Integer(1))->retain());
  static Integer* zero= (Integer*)((new Integer(0))->retain());

  if (value == 1)
    return one;
  if (value == 0)
    return zero;

  return new Integer(value);
}


bool Integer::equals(const Value *o) const
{
  return _value == dynamic_cast<const Integer*>(o)->_value;
}


bool Integer::less_than(const Value *o) const
{
  return _value < dynamic_cast<const Integer*>(o)->_value;
}


//--------------------------------------------------------------------------------------------------

std::string Double::debugDescription(const std::string &indentation) const
{
  return toString();
}

std::string Double::toString() const
{
  return base::to_string(_value);
}

Double::Double(storage_type value)
: _value(value)
{
}


Double* Double::get(storage_type value)
{
  static Double* one= (Double*)((new Double(1.0))->retain());
  static Double* zero= (Double*)((new Double(0.0))->retain());

  if (value == 1.0) return one;
  if (value == 0.0) return zero;

  return new Double(value);
}


bool Double::equals(const Value *o) const
{
  return _value == dynamic_cast<const Double*>(o)->_value;
}


bool Double::less_than(const Value *o) const
{
  return _value < dynamic_cast<const Double*>(o)->_value;
}

//--------------------------------------------------------------------------------------------------

std::string String::debugDescription(const std::string &indentation) const
{
  return "'" + _value + "'";
}

std::string String::toString() const
{
  return _value;
}

String::String(const storage_type &value)
: _value(value)
{
}


String* String::get(const storage_type &value)
{
  static String* empty= (String*)((new String(""))->retain());

  if (value.empty()) return empty;

  return new String(value);
}


bool String::equals(const Value *o) const
{
  return _value == dynamic_cast<const String*>(o)->_value;
}


bool String::less_than(const Value *o) const
{
  return _value < dynamic_cast<const String*>(o)->_value;
}

//--------------------------------------------------------------------------------------------------

std::string List::debugDescription(const std::string &indentation) const
{
  std::string s;

  s.append("[\n"); // Not indented (RHS value).
  for (raw_const_iterator iter= raw_begin(); iter != raw_end(); ++iter)
    s.append(indentation + "  " + iter->debugDescription(indentation + "  ") + "\n");

  s.append(indentation + "]");
  return s;
}

std::string List::toString() const
{
  std::string s;
  bool first = true;

  s.append("[");
  for (raw_const_iterator iter = raw_begin(); iter != raw_end(); ++iter)
  {
    if (!first)
      s.append(", ");
    first = false;
    s.append(iter->toString());
  }

  s.append("]");
  return s;
}

List::List(GRT *grt, bool allow_null)
: _grt(grt), _allow_null(allow_null)
{
  _is_global= 0;
}


List::List(GRT *grt, Type content_type, const std::string &content_class, bool allow_null)
  : _grt(grt), _allow_null(allow_null)
{
  _content_type.type= content_type;
  _content_type.object_class= content_class;

  _is_global= 0;
}


List::~List()
{
}

void List::set_unchecked(size_t index, const ValueRef &value)
{
  if (index >= count()) throw bad_item(index, count());

  if (index == count())
  {
    insert_unchecked(value, index);
    return;
  }

//  if (_content[index].valueptr() != value.valueptr())
  {
    if (_is_global > 0 && _grt->tracking_changes())
      _grt->get_undo_manager()->add_undo(new UndoListSetAction(this, index));

    if (_is_global > 0 && _content[index].is_valid())
    {
      _content[index].unmark_global();
    }

    if (_is_global > 0 && value.is_valid())
    {
      value.mark_global();
    }

    _content[index]= value;
  }
}


void List::insert_unchecked(const ValueRef &value, size_t index)
{
  if (_is_global > 0 && value.is_valid())
    value.mark_global();

  if (index == npos)
  {
    if (_is_global > 0 && _grt->tracking_changes())
      _grt->get_undo_manager()->add_undo(new UndoListInsertAction(this, index));

    _content.push_back(value);
  }
  else if (index > _content.size())
    throw grt::bad_item(index, _content.size());
  else
  {
    if (_is_global > 0 && _grt->tracking_changes())
      _grt->get_undo_manager()->add_undo(new UndoListInsertAction(this, index));

    _content.insert(_content.begin()+index, value);
  }
}


void List::remove(const ValueRef &value)
{
  size_t i= _content.size();
  while (i-- > 0)
  {
    if (_content[i] == value)
    {
      if (_is_global > 0 && _content[i].is_valid())
        _content[i].unmark_global();

      if (_is_global > 0 && _grt->tracking_changes())
        _grt->get_undo_manager()->add_undo(new UndoListRemoveAction(this, i));

      _content.erase(_content.begin()+i);
    }
  }
}


void List::remove(size_t index)
{
  if (index >= count()) throw grt::bad_item(index, count());

  if (_is_global > 0 && _content[index].is_valid())
    _content[index].unmark_global();

  if (_is_global > 0 && _grt->tracking_changes())
    _grt->get_undo_manager()->add_undo(new UndoListRemoveAction(this, index));

  _content.erase(_content.begin()+index);
}

void List::reorder(size_t oi, size_t ni)
{
  if (oi == ni)
    return;

  if (_is_global > 0 && _grt->tracking_changes())
    _grt->get_undo_manager()->add_undo(new UndoListReorderAction(this, oi, ni));

  ValueRef tmp(_content[oi]);
  _content.erase(_content.begin() + oi);
  if (ni >= _content.size())
    _content.insert(_content.end(), tmp);
  else
    _content.insert(_content.begin() + ni, tmp);
}

size_t List::get_index(const ValueRef &value)
{
  size_t i= 0;
  for (std::vector<ValueRef>::const_iterator iter= _content.begin();
       iter != _content.end(); ++iter, ++i)
  {
    if ( *iter == value )
      return i;
  }
  return npos;
}


bool List::check_assignable(const ValueRef &value) const
{
  if (value.is_valid())
  {
    Type vtype= value.type();

    if (content_type() != vtype)
    {
      if (content_type() == AnyType)
        return true;

      return false;
    }

    if (vtype == ObjectType)
    {
      ObjectRef obj(ObjectRef::cast_from(value));

      return obj.is_instance(content_class_name());
    }

    return true;
  }
  return _allow_null;
}


void List::set_checked(size_t index, const ValueRef &value)
{
  if (check_assignable(value))
    set_unchecked(index, value);
  else
  {
    if (value.is_valid())
      throw std::invalid_argument("attempt to insert invalid value to list");
    else
      throw grt::null_value("inserting null value to not null list");
  }
}


void List::insert_checked(const ValueRef &value, size_t index)
{
  if (check_assignable(value))
    insert_unchecked(value, index);
  else
  {
    if (value.is_valid())
    {
      if (_content_type.type != value.type())
        throw grt::type_error(_content_type.type, value.type());
      else
      {
        ObjectRef object(ObjectRef::cast_from(value));
        throw grt::type_error(_content_type.object_class, object.class_name());
      }
    }
    else
      throw grt::null_value("inserting null value to not null list");
  }
}


void List::mark_global() const
{
  if (_is_global == 0)
  {
    if (_content_type.type == AnyType || is_container_type(_content_type.type))
    {
      for (storage_type::const_iterator iter= _content.begin(); iter != _content.end(); ++iter)
      {
        if (iter->is_valid())
          iter->mark_global();
      }
    }
  }
  _is_global++;
}


void List::unmark_global() const
{
  _is_global--;
  if (_is_global == 0)
  {
    if (_content_type.type == AnyType || is_container_type(_content_type.type))
    {
      for (storage_type::const_iterator iter= _content.begin(); iter != _content.end(); ++iter)
      {
        if (iter->is_valid())
          iter->unmark_global();
      }
    }
  }
}

void List::reset_references()
{
  const int max_index = (int)count();
  grt::ValueRef value;
  for ( int i = 0; i < max_index; ++i )
  {
    //g_log("grt", G_LOG_LEVEL_DEBUG, "List::reset_references: '%i'", i);

    value = _content[i];
    if ( value.is_valid() )
      value.valueptr()->reset_references();
  }
}

bool List::equals(const Value *o) const
{
  return this == o;
}


bool List::less_than(const Value *o) const
{
  return this < o;
}


void List::__retype(Type type, const std::string &content_class)
{
  _content_type.type= type;
  _content_type.object_class= content_class;
}


//--------------------------------------------------------------------------------------------------

OwnedList::OwnedList(GRT *grt, Type type, const std::string &content_class, Object *owner, bool allow_null)
  : List(grt, type, content_class, allow_null), _owner(owner)
{
  if (!owner) throw std::invalid_argument("owner cannot be NULL");
}


void OwnedList::set_unchecked(size_t index, const ValueRef &value)
{
  ValueRef item;

  if (index < _content.size())
    item= _content[index];
  else
    throw grt::bad_item(index, _content.size());

  List::set_unchecked(index, value);

  if (item.is_valid())
    _owner->owned_list_item_removed(this, item);
  if (value.is_valid())
    _owner->owned_list_item_added(this, value);
}


void OwnedList::insert_unchecked(const ValueRef &value, size_t index)
{
  List::insert_unchecked(value, index);

  _owner->owned_list_item_added(this, value);
}


void OwnedList::remove(const ValueRef & value)
{
  List::remove(value);

  _owner->owned_list_item_removed(this, value);
}


void OwnedList::remove(size_t index)
{
  ValueRef item(_content[index]);

  List::remove(index);

  _owner->owned_list_item_removed(this, item);
}

//--------------------------------------------------------------------------------------------------

std::string Dict::debugDescription(const std::string &indentation) const
{
  std::string s;

  s.append("{\n");
  for (const_iterator iter = begin(); iter != end(); ++iter)
  {
    s.append(indentation + "  " + iter->first);
    s.append(" = ");
    s.append(iter->second.debugDescription(indentation + "  ") + "\n");
  }
  s.append(indentation + "}");
  return s;
}

std::string Dict::toString() const
{
  std::string s;
  bool first = true;

  s.append("{");
  for (const_iterator iter = begin(); iter != end(); ++iter)
  {
    if (!first)
      s.append(", ");
    first = false;
    s.append(iter->first);
    s.append(" = ");
    s.append(iter->second.toString());
  }
  s.append("}");
  return s;
}

Dict::Dict(GRT *grt, bool allow_null)
: _grt(grt), _allow_null(allow_null)
{
  _content_type.type= AnyType;
  _is_global= 0;
}


Dict::Dict(GRT *grt, Type content_type, const std::string &content_class, bool allow_null)
  : _grt(grt), _allow_null(allow_null)
{
  _content_type.type= content_type;
  _content_type.object_class= content_class;
  _is_global= 0;
}


bool Dict::has_key(const std::string &key) const
{
  return _content.find(key) != _content.end();
}


ValueRef Dict::operator [](const std::string &key) const
{
  const_iterator iter;
  if ((iter= _content.find(key)) == _content.end())
    return ValueRef();
  return iter->second;
}

Dict::const_iterator Dict::begin() const
{
  return _content.begin();
}

Dict::const_iterator Dict::end() const
{
  return _content.end();
}

std::vector<std::string> Dict::keys() const
{
  std::vector<std::string> r;
  for (storage_type::const_iterator i = _content.begin(); i != _content.end(); ++i)
    r.push_back(i->first);
  return r;
}

ValueRef Dict::get(const std::string &key) const
{
  const_iterator iter;
  if ((iter= _content.find(key)) == _content.end())
    return ValueRef();
  return iter->second;
}


void Dict::set(const std::string &key, const ValueRef &value)
{
  if (!value.is_valid() && !_allow_null)
    throw std::invalid_argument("inserting null value to not null dict");

  storage_type::iterator iter= _content.find(key);

  if (_is_global > 0)
  {
    if (_grt->tracking_changes())
      _grt->get_undo_manager()->add_undo(new UndoDictSetAction(this, key));

    if (iter != _content.end() && iter->second.is_valid())
      iter->second.unmark_global();

    if (value.is_valid())
      value.mark_global();
  }

  _content[key]= value;
}


void Dict::remove(const std::string &key)
{
  storage_type::iterator iter= _content.find(key);
  if (iter != _content.end())
  {
    if (_is_global > 0)
    {
      if (_grt->tracking_changes())
        _grt->get_undo_manager()->add_undo(new UndoDictRemoveAction(this, key));

      if (iter->second.is_valid())
        iter->second.unmark_global();
    }

    _content.erase(iter);
  }
}

//--------------------------------------------------------------------------------------------------

/**
 * Removes all entries from this dict. The global marker is updated properly, but
 * no undo record is created (in fact, what has a high level feature like undo/redo to do
 * in such a low level storage container?).
 */
void Dict::reset_entries()
{
  if (_is_global > 0)
  {
    if (_content_type.type == AnyType || is_container_type(_content_type.type))
    {
      for (storage_type::const_iterator iter= _content.begin(); iter != _content.end(); ++iter)
      {
        if (iter->second.is_valid())
          iter->second.unmark_global();
      }
    }
  }
  _content.clear();
}

//--------------------------------------------------------------------------------------------------

void Dict::mark_global() const
{
  if (_is_global == 0)
  {
    if (_content_type.type == AnyType || is_container_type(_content_type.type))
    {
      for (storage_type::const_iterator iter= _content.begin(); iter != _content.end(); ++iter)
      {
        if (iter->second.is_valid())
          iter->second.mark_global();
      }
    }
  }
  _is_global++;
}


void Dict::unmark_global() const
{
  _is_global--;

  if (_is_global == 0)
  {
    if (_content_type.type == AnyType || is_container_type(_content_type.type))
    {
      for (storage_type::const_iterator iter= _content.begin(); iter != _content.end(); ++iter)
      {
        if (iter->second.is_valid())
          iter->second.unmark_global();
      }
    }
  }
}

void Dict::reset_references()
{
  storage_type::iterator               it = _content.begin();
  const storage_type::const_iterator last = _content.end();

  for (; last != it; ++it )
  {
    //g_log("grt", G_LOG_LEVEL_DEBUG, "Dict::reset_references: '%s'", it->first.c_str());
    if ( it->second.is_valid() )
      it->second.valueptr()->reset_references();
  }
}

bool Dict::equals(const Value *o) const
{
  return this == o;
}


bool Dict::less_than(const Value *o) const
{
  return this < o;
}

//--------------------------------------------------------------------------------------------------

OwnedDict::OwnedDict(GRT *grt, Type type, const std::string &content_class, Object *owner, bool allow_null)
  : Dict(grt, type, content_class, allow_null), _owner(owner)
{
}


void OwnedDict::set(const std::string &key, const ValueRef &value)
{
  Dict::set(key, value);

  _owner->owned_dict_item_set(this, key);
}


void OwnedDict::remove(const std::string &key)
{
  Dict::remove(key);

  _owner->owned_dict_item_removed(this, key);
}

//--------------------------------------------------------------------------------------------------

/**
 * Removes all entries from this dict and sends a notification for each removal.
 */
void OwnedDict::reset_entries()
{
  for (storage_type::const_iterator iter= _content.begin(); iter != _content.end(); ++iter)
  {
    _owner->owned_dict_item_removed(this, iter->first);
  }
  Dict::reset_entries();
}
//--------------------------------------------------------------------------------------------------
#ifdef GRT_LEAK_DETECTOR_ENABLED
class MYSQLGRT_PUBLIC ObjectLeakDetector
{
protected:
    struct ObjectData
    {
        std::string ClassName;
        std::string Name;
        int refcount;
        std::string callstack;
    };

    bool enabled;
    std::map<const Object*, ObjectData> obj_map;
public:
    ObjectLeakDetector():enabled(false){};
    ~ObjectLeakDetector();
    static ObjectLeakDetector* get_detector();
    void register_obj(const Object* obj)
    {
        if(!enabled)
            return;
        ObjectData data;
        data.ClassName = obj->class_name();
        //Will need to update that later
        //data.Name = obj->get_string_member("name");
        //Doesn't make sense here object is just created
        //data.refcount
#ifdef GRT_LEAK_DETECTOR_RECORD_CALL_STACK
        data.callstack = get_call_stack();
#endif
        obj_map[obj] = data;
    };
    void unregister_obj(const Object* obj)
    {
        obj_map.erase(obj);
    };

    void print_leaks() const;
    void update_data();

    void reset()
    {
        obj_map.clear();
    };

    void enable()
    {
        enabled = true;
    };
    void disable()
    {
        enabled = false;
    };
};

static ObjectLeakDetector leak_detector;

ObjectLeakDetector* ObjectLeakDetector::get_detector()
{
    return &leak_detector;
}

void ObjectLeakDetector::update_data()
{
    size_t objcounter = 0;
    for(std::map<const Object*, ObjectData>::iterator It = obj_map.begin(); It != obj_map.end(); ++It, ++objcounter)
    {
        It->second.Name = It->first->get_string_member("name");
        It->second.refcount = It->first->refcount();
    }
    std::cout<<"Total "<<objcounter <<" objects leaked"<<std::endl;

};

void ObjectLeakDetector::print_leaks() const
{
    size_t objcounter = 0;
    for(std::map<const Object*, ObjectData>::const_iterator It = obj_map.begin(); It != obj_map.end(); ++It, ++objcounter)
    {
        std::cout<<It->second.ClassName<<":"<<It->second.Name<<" Refcount is "<<It->second.refcount<<std::endl;
#ifdef GRT_LEAK_DETECTOR_RECORD_CALL_STACK
        std::cout<<"Call Stack is :"<<std::endl;
        std::cout<<It->second.callstack<<std::endl;
#endif
    }
    std::cout<<"Total "<<objcounter <<" objects leaked"<<std::endl;

};

ObjectLeakDetector::~ObjectLeakDetector()
{
    get_detector()->print_leaks();
};

namespace grt
{
    namespace LeakDetector
    {
        void print_leaks()
        {
            ObjectLeakDetector::get_detector()->print_leaks();
        };

        void enable()
        {
            ObjectLeakDetector::get_detector()->enable();
        };

        void disable()
        {
            ObjectLeakDetector::get_detector()->disable();
        };

        void reset()
        {
            ObjectLeakDetector::get_detector()->reset();
        };

        void update_objects_data()
        {
            ObjectLeakDetector::get_detector()->update_data();
        };
    };
}
#endif

//--------------------------------------------------------------------------------------------------

Object::Object(MetaClass *metaclass)
: _metaclass(metaclass)//, _valid_flag(true)
{
  if (!_metaclass)
    throw std::runtime_error("GRT object allocated without a metaclass (make sure metaclass data was loaded)");

  _id= get_guid();
  _is_global= 0;
#ifdef GRT_LEAK_DETECTOR_ENABLED
  ObjectLeakDetector::get_detector()->register_obj(this);
#endif
}


Object::Object(GRT *grt, MetaClass *metaclass)
: _metaclass(metaclass)//, _valid_flag(true)
{
  if (!_metaclass)
    throw std::runtime_error("GRT object allocated without a metaclass (make sure metaclass data was loaded)");

  _id= get_guid();
  _is_global= 0;
#ifdef GRT_LEAK_DETECTOR_ENABLED
  ObjectLeakDetector::get_detector()->register_obj(this);
#endif
}

#ifdef GRT_LEAK_DETECTOR_ENABLED
Object::~Object()
{
    ObjectLeakDetector::get_detector()->unregister_obj(this);
}
#endif

const std::string &Object::id() const
{
  return _id;
}

MetaClass* Object::get_metaclass() const
{
  return _metaclass;
}

const std::string &Object::class_name() const
{
  return _metaclass->name();
}

std::string Object::debugDescription(const std::string &indentation) const
{
  std::string s;
  bool first = true;

  s = strfmt("{<%s> (%s)\n", _metaclass->name().c_str(), id().c_str());

  MetaClass *mc= _metaclass;

  do
  {
    for (MetaClass::MemberList::const_iterator iter= mc->get_members_partial().begin();
         iter != mc->get_members_partial().end(); ++iter)
    {
      if (iter->second.overrides) continue;

      if (!first)
        s.append(", ");
      first= false;

      s.append(iter->first);
      s.append(" = ");

      if (iter->second.type.base.type == ObjectType)
      {
        ObjectRef obj(ObjectRef::cast_from(get_member(iter->first)));
        if (obj.is_valid())
        {
          if (obj.has_member("name"))
            s.append(indentation + strfmt("  %s: %s  (%s)",
                            obj.get_string_member("name").c_str(),
                            obj.get_metaclass()->name().c_str(),
                            obj.id().c_str()));
          else
            s.append(indentation + strfmt("  %s (%s)",
                            obj.get_metaclass()->name().c_str(),
                            obj.id().c_str()));
        }
        else
          s.append(indentation + strfmt("  %s: null", iter->first.c_str()));
      }
      else
        s.append(get_member(iter->first).debugDescription(indentation + "  "));
    }

    mc= mc->parent();
  }
  while (mc != 0);

  s.append(indentation + "}\n");

  return s;
}

std::string Object::toString() const
{
  std::string s;
  bool first= true;

  s= strfmt("{<%s> (%s)\n", _metaclass->name().c_str(), id().c_str());

  MetaClass *mc= _metaclass;

  do
  {
    for (MetaClass::MemberList::const_iterator iter= mc->get_members_partial().begin();
         iter != mc->get_members_partial().end(); ++iter)
    {
      if (iter->second.overrides) continue;

      if (!first)
        s.append(", ");
      first= false;

      s.append(iter->first);
      s.append(" = ");

      if (iter->second.type.base.type == ObjectType)
      {
        ObjectRef obj(ObjectRef::cast_from(get_member(iter->first)));
        if (obj.is_valid())
        {
          if (obj.has_member("name"))
            s.append(strfmt("%s: %s  (%s)",
                            obj.get_string_member("name").c_str(),
                            obj.get_metaclass()->name().c_str(),
                            obj.id().c_str()));
          else
            s.append(strfmt("%s (%s)",
                            obj.get_metaclass()->name().c_str(),
                            obj.id().c_str()));
        }
        else
          s.append(strfmt("%s: null",
            iter->first.c_str()));
      }
      else
        s.append(get_member(iter->first).toString());
    }

    mc= mc->parent();
  }
  while (mc != 0);

  s.append("}");

  return s;
}

bool Object::is_instance(MetaClass *metaclass) const
{
  if (!this)
    return false;
  return _metaclass->is_a(metaclass);
}

bool Object::is_instance(const std::string &name) const
{
  if (!this)
    return false;

  return _metaclass->is_a(get_grt()->get_metaclass(name));
}

void Object::set_member(const std::string &member, const ValueRef &value)
{
  _metaclass->set_member_value(this, member, value);
}

ValueRef Object::get_member(const std::string &member) const
{
  return _metaclass->get_member_value(this, member);
}

bool Object::has_member(const std::string &member) const
{
  return _metaclass->has_member(member);
}


bool Object::has_method(const std::string &method) const
{
  return _metaclass->has_method(method);
}


std::string Object::get_string_member(const std::string &member) const
{
  return StringRef::extract_from(_metaclass->get_member_value(this, member));
}


Double::storage_type Object::get_double_member(const std::string &member) const
{
  return DoubleRef::extract_from(_metaclass->get_member_value(this, member));
}


Integer::storage_type Object::get_integer_member(const std::string &member) const
{
  return IntegerRef::extract_from(_metaclass->get_member_value(this, member));
}


ValueRef Object::call(const std::string &method, const BaseListRef &args)
{
  return _metaclass->call_method(this, method, args);
}


/** Evil function to set ID of an object, use only if you know what you're doing.
 */
void Object::__set_id(const std::string &id)
{
  _id= id;
}

bool process_reset_references_for_member(const MetaClass::Member* m, Object* obj)
{
  if (m && !m->calculated && !grt::is_simple_type(m->type.base.type))
  {
    //g_log("grt", G_LOG_LEVEL_DEBUG, "\tprocess_reset_references_for_member'%s':'%s':'%s'", obj->class_name().c_str(), obj->id().c_str(), m->name.c_str());
    grt::ValueRef member_value = obj->get_member(m->name);
    if ( member_value.is_valid() )
    {
      // if the member is owned, then recursively reset references in it
      if (m->owned_object)
        member_value.valueptr()->reset_references();

      obj->signal_changed()->disconnect_all_slots();
      // set the member value to null
      obj->get_metaclass()->set_member_internal(obj, m->name, grt::ValueRef(), true);
    }
  }
  return true;
}

void Object::reset_references()
{
  //g_log("grt", G_LOG_LEVEL_DEBUG, "Object::reset_references for '%s':'%s'", class_name().c_str(), id().c_str());
  _metaclass->foreach_member(boost::bind(&process_reset_references_for_member, _1, this));
}

void Object::init()
{
}


GRT *Object::get_grt() const
{
  return _metaclass->get_grt();
}

static bool mark_global_(const MetaClass::Member *member, const Object *obj)
{
  if (is_container_type(member->type.base.type))
  {
    ValueRef value(obj->get_member(member->name));
    if (value.is_valid())
      value.mark_global();
  }
  return true;
}


void Object::mark_global() const
{
  _is_global++;
  if (_is_global == 1)
    _metaclass->foreach_member(boost::bind(&mark_global_, _1, this));
}


static bool unmark_global_(const MetaClass::Member *member, const Object *obj)
{
  if (is_container_type(member->type.base.type))
  {
    ValueRef value(obj->get_member(member->name));
    if (value.is_valid())
      value.unmark_global();
  }
  return true;
}


void Object::unmark_global() const
{
  _is_global--;
  if (_is_global == 0)
    _metaclass->foreach_member(boost::bind(&unmark_global_, _1, this));
}



bool Object::equals(const Value *o) const
{
  return this == o;
}


bool Object::less_than(const Value *o) const
{
  return this < o;
}



void Object::owned_member_changed(const std::string &name, const grt::ValueRef &ovalue, const grt::ValueRef &nvalue)
{
  if (_is_global)
  {
    if (ovalue != nvalue)
    {
      if (ovalue.is_valid()) ovalue.unmark_global();
      if (nvalue.is_valid()) nvalue.mark_global();
    }
    if (get_grt()->tracking_changes())
      get_grt()->get_undo_manager()->add_undo(new UndoObjectChangeAction(this, name, ovalue));
  }
  _changed_signal(name, ovalue);
}


void Object::member_changed(const std::string &name, const grt::ValueRef &ovalue, const grt::ValueRef &nvalue)
{
  if (_is_global && get_grt()->tracking_changes())
    get_grt()->get_undo_manager()->add_undo(new UndoObjectChangeAction(this, name, ovalue));
  _changed_signal(name, ovalue);
}



void Object::owned_list_item_added(OwnedList *list, const grt::ValueRef &value)
{
  _list_changed_signal(list, true, value);
}


void Object::owned_list_item_removed(OwnedList *list, const grt::ValueRef &value)
{
  _list_changed_signal(list, false, value);
}


void Object::owned_dict_item_set(OwnedDict *dict, const std::string &key)
{
  _dict_changed_signal(dict, true, key);
}


void Object::owned_dict_item_removed(OwnedDict *dict, const std::string &key)
{
  _dict_changed_signal(dict, false, key);
}

#ifdef USE_EXPRERIMENTAL_REFS
namespace
{
static const int max_ref_size = 32;
    /*
    This is compile time check to be sure that sizeof (ValueRef) won't grow too big without notice
    If you get an error here this means that it grown bigger than planned and set in max_ref_size
    If you are sure that ValueRef needs to grow bigger just increase max_ref_size
    */
    void check_ref_size(const char[max_ref_size - sizeof (ValueRef)]);
};

class CountedTypeHandler : public TypeHandler
{
    static internal::Value* get_ptr(const TypeHandle& handle)
    {
        return static_cast<internal::Value*>(handle.refcounted_value.ptr_value);
    };
    virtual void release(TypeHandle& handle)const
    {
        get_ptr(handle)->release();
    };

    virtual void retain(TypeHandle& handle)const
      {
              get_ptr(handle)->retain();
      };
      virtual bool equals(const TypeHandle& handle, const ValueRef& other)const
      {
          if (type(handle) != other.type())
              return false;
          return get_ptr(handle)->equals(get_ptr(other.get_data()));
      };

      virtual bool less_than(const TypeHandle& handle, const ValueRef& other)const
      {
          if (get_ptr(handle)->get_type() != other.type())
              return get_ptr(handle)->get_type() < other.type();
          return get_ptr(handle)->less_than(get_ptr(other.get_data()));
      };
      virtual void reset_references(TypeHandle& handle)const{get_ptr(handle)->reset_references();};
      virtual void clear(TypeHandle& handle)const {release(handle);};
      virtual inline bool is_same(const TypeHandle& handle, const ValueRef &value) const { return get_ptr(handle) == value.valueptr(); }
      virtual inline Type type(const TypeHandle& handle) const { return get_ptr(handle) ? get_ptr(handle)->get_type() : UnknownType; }
      std::string debugDescription(const TypeHandle& handle) const {return get_ptr(handle)->debugDescription();};
      std::string toString(const TypeHandle& handle) const { return get_ptr(handle)->toString(); };
      virtual void mark_global(const TypeHandle& handle) const{get_ptr(handle)->mark_global();};
      virtual void unmark_global(const TypeHandle& handle) const{get_ptr(handle)->unmark_global();};
      virtual internal::Value* valueptr(const TypeHandle& handle) const
      {
          return get_ptr(handle);
      };
      virtual int refcount(const TypeHandle& handle) const{return get_ptr(handle)->refcount();};

};

static CountedTypeHandler counted_handler;

class NonCountedTypeHandler: public TypeHandler
{
public:
    virtual void release(TypeHandle& handle)const {};
    virtual void retain(TypeHandle& handle)const {};
    virtual void reset_references(TypeHandle& handle)const{};
    virtual void clear(TypeHandle& handle)const {};
    virtual void mark_global(const TypeHandle& handle) const{};
    virtual void unmark_global(const TypeHandle& handle) const{};
    //Type isn't counted thus there is no refcount
    virtual int refcount(const TypeHandle& handle) const{return 1;};
    virtual internal::Value* valueptr(const TypeHandle& handle) const
    {//kinda hack shouldn't exist at all
        return static_cast<internal::Value*>(handle.refcounted_value.ptr_value);
    };
};

class DefaultTypeHandler : public NonCountedTypeHandler
{
public:
    virtual Type type(const TypeHandle& handle) const
    {
        return UnknownType;
    }

    //all empty valuerefs are equal so compare only type
    virtual bool equals(const TypeHandle& handle, const ValueRef& other)const
    {
        return other.type() == UnknownType;
    };

    virtual bool less_than(const TypeHandle& handle, const ValueRef& other)const
    {
        return other.type() != UnknownType;
    };

    virtual inline bool is_same(const TypeHandle& handle, const ValueRef &value) const
    {
        return value.type() == UnknownType;
    }

    std::string debugDescription(const TypeHandle& handle) const
    {
        return "NULL";
    };

    std::string toString(const TypeHandle& handle) const
    {
      return "NULL";
    };
};

DefaultTypeHandler default_handler;
TypeHandler* ValueRef::_defalut_handler = &default_handler;

class DoubleTypeHandler : public NonCountedTypeHandler
{
public:
    virtual Type type(const TypeHandle& handle) const
    {
        return DoubleType;
    }

    virtual bool equals(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return false;
        return handle.double_value == other.get_data().double_value;
    };

    virtual bool less_than(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return type(handle) < other.type();

        return handle.double_value < other.get_data().double_value;
    };

    virtual inline bool is_same(const TypeHandle& handle, const ValueRef &value) const
    {
        return handle.double_value == value.get_data().double_value;
    }

    std::string debugDescription(const TypeHandle& handle) const
    {
      return toString(handle);
    };

    std::string toString(const TypeHandle& handle) const
    {
      return base::to_string(handle.double_value);
    };
};

static DoubleTypeHandler double_handler;

class IntegerTypeHandler : public NonCountedTypeHandler
{
public:
    virtual Type type(const TypeHandle& handle) const
    {
        return IntegerType;
    }

    virtual bool equals(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return false;
        return handle.int_value == other.get_data().int_value;
    };

    virtual bool less_than(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return type(handle) < other.type();

        return handle.int_value < other.get_data().int_value;
    };

    virtual inline bool is_same(const TypeHandle& handle, const ValueRef &value) const
    {
        return handle.int_value == value.get_data().int_value;
    }

    std::string debugDescription(const TypeHandle& handle) const
    {
      return toString(handle);
    };

    std::string toString(const TypeHandle& handle) const
    {
      return base::to_string(handle.int_value);
    };
};

static IntegerTypeHandler int_handler;


class StringTypeHandler : public TypeHandler
{
public:
    static const int small_string_size = 36;
    static StringRef::storage_type get_ptr(const TypeHandle& handle)
    {
        return handle.string_ptr;
    };
    virtual bool equals(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return false;
        return !strcmp(get_ptr(handle), get_ptr(other.get_data()));//get_ptr(handle)->equals(get_ptr(other.get_data()));
    };

    virtual bool less_than(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return type(handle) < other.type();
        return strcmp(get_ptr(handle), get_ptr(other.get_data())) < 0;
    };

    virtual void reset_references(TypeHandle& handle)const{};

    virtual void clear(TypeHandle& handle)const {
        release(handle);
    };

    virtual inline bool is_same(const TypeHandle& handle, const ValueRef &value) const { return get_ptr(handle) == value.get_data().string_ptr; }
    virtual inline Type type(const TypeHandle& handle) const { return StringType; }
    std::string debugDescription(const TypeHandle& handle) const {return get_ptr(handle);};
    std::string toString(const TypeHandle& handle) const { return get_ptr(handle); };
    virtual void mark_global(const TypeHandle& handle) const{};
    virtual void unmark_global(const TypeHandle& handle) const{};
    virtual internal::Value* valueptr(const TypeHandle& handle) const {return NULL;};
    virtual int refcount(const TypeHandle& handle) const {return 1;};
};


struct CountedStringDataStruct
{
    int refcounter;
//    size_t size;
};

#ifdef _DEBUG
//#define STRING_ALLOCATION_LOGGER_ENABLED
#endif

#ifdef STRING_ALLOCATION_LOGGER_ENABLED
#include <iostream>
struct stringlogger
{
    static const char delim = '\t';
    struct string_log_data
    {
        int alloc_count;
        int dealloc_count;
        int retain_count;
        int release_count;
    };
    std::map<std::string, string_log_data> log;
    void log_string_alloc(const char* str)
    {
        log[str].alloc_count++;
    };

    void log_string_dealloc(const char* str)
    {
        log[str].dealloc_count++;
    };

    void log_string_retain(const char* str)
    {
        log[str].retain_count++;
    };

    void log_string_release(const char* str)
    {
        log[str].release_count++;
    };

    ~stringlogger()
    {
        for (std::map<std::string, string_log_data>::iterator It = log.begin(); It != log.end(); ++It)
        {
            if(It->first.size() > 36)
                continue;
            std::cerr<<It->first
                <<delim<<It->second.retain_count
                <<delim<<It->second.alloc_count
                <<delim<< It->first.size()
                <<std::endl;
        }
    }
};
stringlogger logger;

#endif

static char empty_string = 0;
class EmptyStringTypeHandler : public StringTypeHandler
{
public:
    StringRef::storage_type allocate(const char* srcstring, const size_t len) const
    {
        return len ? NULL : &empty_string;
    };

    virtual bool equals(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return false;
        return get_ptr(other.get_data())[0] == 0;
    };

    virtual void release(TypeHandle& handle) const{};
    virtual void retain(TypeHandle& handle) const{};
};

EmptyStringTypeHandler empty_string_handler;

const char* fixed_strins[] = {
"Application/Workbench",
"standalone",
"string",
"Workbench",
"normal",
"action",
"internal",
"db.Catalog",
"Menu/SQL/Utilities",
".",
"gui",
"Menu/Utilities",
"activeDiagram",
"file",
"keychain",
"model.Diagram",
"Catalog/Utilities",
"Menu/Text",
"Password:",
"database/Database",
"Username:",
"userName",
"schema",
"root",
"pyodbc",
"Menu/Objects",
"catalog/Editors",
"SQLIDEUtils",
"Home",
"boolean",
"*model",
"db.query.QueryEditor",
"activeQueryEditor",
"activeCatalog",
"WbUtils",
"Menu/SQL/Resultset",
"Menu/Catalog",
"Filter",
"Name of the user to connect with.",
"separator",
"Database",
"MySQLDbModule",
"http://www.mysql.com",
"FALSE",
"127.0.0.1",
"activeModel",
".\\db.mysql.editors.wbp.fe.dll",
"Others/Menu/Ungrouped",
"Database:",
"Oracle Corp.",
"Validation",
"save",
"Home/Connections",
"Home/Instances",
"Home/ModelFiles",
"Export",
"Menu/Model",
"Editors",
"WbModel",
"Menu/Database",
"Menu/SQL/Script",
"Menu/SQL/Catalog",
"Menu/SQL/Editor",
"model/Editors",
"hostName",
"port",
"int",
"enum",
"sa",
"model.Model",
"%userName%::Mysql@%hostName%:%port%",
"SQLEditor",
"Menu/Ungrouped",
"TRUE",
"WbAdmin",
"PyWbUtils",
"workbench.physical.Model",
"open",
".\\wb.model.editors.wbp.fe.dll",
"Menu/Administrator",
"WB Plugin",
"Model",
"localhost",
"Website",
"SSL Key File:",
"sslCA",
"Path to Key file for SSL.",
"Default Schema:",
"SSL CA File:",
"SSL CERT File:",
"SSL Cipher:",
"Use SSL if available.",
"tristate",
"sslKey",
"sslCipher",
"getSchemata",
"useAnsiQuotes",
"sslCert",
"Path to Certificate file for SSL.",
"mysqlcppconn",
"useSSL",
"ReverseEngineeringMysql",
"TCP/IP port.",
"Hostname:",
"Port:",
"Pre-configured ODBC data source.",
"driver",
"Driver:",
"DSN:",
"dsn",
"DbMssqlRE",
"getDataSourceNames",
"INT",
"windows,linux",
"Open Model",
"db.mysql.Table",
"Model/Printing",
"BINARY",
"*query",
"binary"
};

struct str_less{

    bool operator()(const char* str1, const char* str2) const
    {
        return strcmp(str1, str2) < 0;
    };
};
str_less str_less_pred;

class FixedStringTypeHandler : public StringTypeHandler
{
public:
    FixedStringTypeHandler()
    {
        std::sort(fixed_strins, fixed_strins+sizeof(fixed_strins)/sizeof(fixed_strins[0]), str_less_pred);
    }

    StringRef::storage_type allocate(const char* srcstring, const size_t len) const
    {
        if (len >= small_string_size)
            return NULL;
        const char** found = std::lower_bound(fixed_strins, fixed_strins+sizeof(fixed_strins)/sizeof(fixed_strins[0]), srcstring, str_less_pred);
        if(found != fixed_strins+sizeof(fixed_strins)/sizeof(fixed_strins[0]) && !strcmp(*found, srcstring))
            return *found;
        return NULL;
    };

    virtual bool equals(const TypeHandle& handle, const ValueRef& other)const
    {
        if (type(handle) != other.type())
            return false;
        return get_ptr(handle) == get_ptr(other.get_data());
    };

    virtual void release(TypeHandle& handle) const{};
    virtual void retain(TypeHandle& handle) const{};
};

FixedStringTypeHandler fixed_string_handler;

class ShortStringTypeHandler : public StringTypeHandler
{
protected:
struct SmallStringBlock
{
    CountedStringDataStruct string_data;
    char buffer[small_string_size];
};

//NODO: implement dynamic buffers allocation
static const int smallstrings_buffer_size = 10240;
SmallStringBlock smallstrings[smallstrings_buffer_size];
mutable std::vector<SmallStringBlock*> small_strings_stack;
static GStaticMutex _allocation_mutex;

public:
    ShortStringTypeHandler()
    {
        small_strings_stack.reserve(smallstrings_buffer_size);
        for(int i = 0; i < smallstrings_buffer_size; ++i)
            small_strings_stack.push_back(&smallstrings[i]);
    };

    ~ShortStringTypeHandler()
    {
      g_static_mutex_free(&_allocation_mutex);
    }

    StringRef::storage_type allocate(const char* srcstring, const size_t len)
    {
      base::GStaticMutexLock lock(_allocation_mutex);

      if((len >= small_string_size) || small_strings_stack.empty())
        return NULL;

      SmallStringBlock* block_ptr = small_strings_stack.back();
      small_strings_stack.pop_back();
      block_ptr->string_data.refcounter = 0;
      memcpy(block_ptr->buffer, srcstring, len + 1);
      return block_ptr->buffer;
    };

    virtual void release(TypeHandle& handle) const
    {
#ifdef STRING_ALLOCATION_LOGGER_ENABLED
        logger.log_string_release(get_ptr(handle));
#endif
        CountedStringDataStruct* ptr = (CountedStringDataStruct*)(handle.string_ptr) - 1;
        ptr->refcounter--;
        if(ptr->refcounter <= 0)
        {
          base::GStaticMutexLock lock(_allocation_mutex);
          small_strings_stack.push_back(reinterpret_cast<SmallStringBlock*>(ptr));
        }
    };

    virtual void retain(TypeHandle& handle) const
    {
#ifdef STRING_ALLOCATION_LOGGER_ENABLED
        logger.log_string_retain(get_ptr(handle));
#endif

        CountedStringDataStruct* ptr = (CountedStringDataStruct*)(handle.string_ptr) - 1;
        ptr->refcounter++;
    };

};

GStaticMutex ShortStringTypeHandler::_allocation_mutex = G_STATIC_MUTEX_INIT;

ShortStringTypeHandler short_string_handler;

class LongStringTypeHandler : public StringTypeHandler
{
public:
    StringRef::storage_type allocate(const char* srcstring, const size_t len) const
    {
        size_t size = len + 1 + sizeof(CountedStringDataStruct);
        void* buffer = new char[size];
        memset(buffer, 0, sizeof(CountedStringDataStruct));
        char* string_buffer = static_cast<char*>(buffer) + sizeof(CountedStringDataStruct);
        memcpy(string_buffer, srcstring, len + 1);
        return string_buffer;
    };

    virtual void release(TypeHandle& handle) const
    {

#ifdef STRING_ALLOCATION_LOGGER_ENABLED
        logger.log_string_release(get_ptr(handle));
#endif

        CountedStringDataStruct* ptr = (CountedStringDataStruct*)(handle.string_ptr) - 1;
        ptr->refcounter--;
        if(ptr->refcounter <= 0)
            delete [](ptr);
    };

    virtual void retain(TypeHandle& handle) const
    {
#ifdef STRING_ALLOCATION_LOGGER_ENABLED
        logger.log_string_retain(get_ptr(handle));
#endif
        CountedStringDataStruct* ptr = (CountedStringDataStruct*)(handle.string_ptr) - 1;
        ptr->refcounter++;
    };
};

static LongStringTypeHandler long_string_handler;

TypeHandler* grt::get_string_type_handler(TypeHandler::TypeHandle& handle, const char* srcstring, const size_t len)
{
#ifdef STRING_ALLOCATION_LOGGER_ENABLED
    logger.log_string_alloc(srcstring);
#endif

    if((handle.string_ptr = empty_string_handler.allocate(srcstring, len)))
        return &empty_string_handler;
    if((handle.string_ptr = fixed_string_handler.allocate(srcstring, len)))
        return &fixed_string_handler;
    else if ((handle.string_ptr = short_string_handler.allocate(srcstring, len)))
        return &short_string_handler;
    else if ((handle.string_ptr = long_string_handler.allocate(srcstring, len)))
        return &long_string_handler;
    else
        throw std::runtime_error("String Allocation Failed");
}

TypeHandler* grt::get_int_type_handler()
{
    return &int_handler;
}

TypeHandler* grt::get_double_type_handler()
{
    return &double_handler;
}

TypeHandler* grt::get_object_type_handler()
{
    return &counted_handler;
};
#endif
