/* 
 * Copyright (c) 2007, 2011, 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 "stdafx.h"

#include "base/string_utilities.h"

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

#include <glib.h>


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


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

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

/**
 * Moves the entry at the old index (oi) to the new index (ni). The new index is counted
 * while the object is still at its old location. If ni < oi then all entries [ni, oi) are moved
 * one index up. If ni > oi then all entries (oi, ni] are moved one index down.
 */
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->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);
  }
}

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

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

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

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

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

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


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


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


