/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


#include "stdafx.h"

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

#include <glib.h>


using namespace grt;
using namespace grt::internal;



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


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

#define DEBUG
void Value::release()
{
#ifdef DEBUG
  if (_refcount == 0) g_warning("GRT: releasing invalid object");
#endif

  --_refcount;
  if (_refcount == 0)
    delete this;
}

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

std::string Integer::repr() const
{
  char s[100];
  g_snprintf(s, sizeof(s), "%li", _value);
  return s;
}


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::repr() const
{
  char s[100];
  g_snprintf(s, sizeof(s), "%g", _value);
  return s;
}

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

  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].valueptr()->unmark_global();
    }

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

    _content[index]= value;
  }
}


void List::insert_unchecked(const ValueRef &value, size_t index)
{
  if (_is_global > 0 && value.is_valid())
    value.valueptr()->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].valueptr()->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].valueptr()->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 (_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->is_same(value)) return i; // FIXED by mx
    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->valueptr()->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->valueptr()->unmark_global();
      }
    }
  }
}

void List::reset_references()
{
  const int max_index = 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::repr() 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.repr());
  }
  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;
}


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.valueptr()->unmark_global();

    if (value.is_valid())
      value.valueptr()->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.valueptr()->unmark_global();
    }

    _content.erase(iter);
  }
}



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


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

Object::Object(GRT *grt, MetaClass *metaclass)
: _grt(grt), _metaclass(metaclass)
{
  if (!_metaclass)
    throw std::runtime_error("GRT object allocated without a metaclass (make sure metaclass data was loaded)");
  
  _id= get_guid();
  _is_global= 0;
}


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::repr() 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())
          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: null",
            iter->first.c_str()));
      }
      else
        s.append(get_member(iter->first).repr());
    }
    
    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(_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::cast_from(_metaclass->get_member_value(this, member));
}


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


Integer::storage_type Object::get_integer_member(const std::string &member) const
{
  return *IntegerRef::cast_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->owned_object )
  {
    //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() )
    {
      member_value.valueptr()->reset_references();
      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(sigc::bind(sigc::ptr_fun(&process_reset_references_for_member), this));
}

void Object::init()
{
}


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.valueptr()->mark_global();
  }
  return true;
}


void Object::mark_global() const
{
  _is_global++;
  if (_is_global == 1)
    _metaclass->foreach_member(sigc::bind<const Object*>(sigc::ptr_fun(&mark_global_), 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.valueptr()->unmark_global();
  }
  return true;
}


void Object::unmark_global() const
{
  _is_global--;
  if (_is_global == 0)
    _metaclass->foreach_member(sigc::bind<const Object*>(sigc::ptr_fun(&unmark_global_), 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.valueptr()->unmark_global();
      if (nvalue.is_valid()) nvalue.valueptr()->mark_global();
    }
    if (_grt->tracking_changes())
      _grt->get_undo_manager()->add_undo(new UndoObjectChangeAction(this, name, ovalue));
  }
  _changed_signal.emit(name, ovalue);
}


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



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


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


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


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


