/* 
 * 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 "grtpp_module_python.h"

#include "grtpp_util.h"
#include "base/string_utilities.h"
#include "base/log.h"

#include <glib.h>
#include <functional>

DEFAULT_LOG_DOMAIN("grt")

using namespace grt;

#ifdef HAVE_CONFIG_H
# include "../../../config.h"
#endif


#if defined(_WIN32)
#include <objbase.h>
#elif defined(__APPLE__)
#include <CoreFoundation/CoreFoundation.h>
#else
// Linux specific
#include <uuid/uuid.h>
#endif

std::string grt::get_guid()
{
  /* GUIDs must be no more than 50 chars */
  
#if defined(__WIN__) || defined(_WIN32) || defined(_WIN64)
  GUID guid;
  WCHAR guid_wstr[50];
  char guid_str[200];

  int len;

  CoCreateGuid(&guid);
  len= StringFromGUID2(guid, (LPOLESTR) guid_wstr, 50);

  //Covert GUID from WideChar to utf8
  WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) guid_wstr, len,
    (LPSTR) guid_str, 200, NULL, NULL);

  return guid_str;
#elif defined(__APPLE__)
  CFUUIDRef uid;
  CFStringRef str;
  char data[40];
  
  uid= CFUUIDCreate(NULL);
  str= CFUUIDCreateString(NULL, uid);
  
#ifdef ENABLE_DEBUG
  assert((int)sizeof(data) > CFStringGetLength(str));
#endif
  //data= (char*)g_malloc(sizeof(gchar)*(CFStringGetLength(str)+1));
  
  CFStringGetCString(str, data, sizeof(data), kCFStringEncodingUTF8);
  
  CFRelease(uid);
  CFRelease(str);
  
  std::string ret= data;
  //g_free(data);
  
  return ret;
#else
  {
    uuid_t gid;
    char buffer[40];
    uuid_generate_time(gid);
    uuid_unparse(gid, buffer);
    return buffer;
  }
#endif
}


std::string grt::fmt_simple_type_spec(const SimpleTypeSpec &type)
{
  switch (type.type)
  {
    case IntegerType: return "ssize_t";
    case DoubleType: return "double";
    case StringType: return "string";
    case ListType: return "list";
    case DictType: return "dict";
    case ObjectType: return type.object_class;
    default: return "??? invalid ???";
  }
}


std::string grt::fmt_type_spec(const TypeSpec &type)
{
  switch (type.base.type)
  {
    case IntegerType: return "ssize_t";
    case DoubleType: return "double";
    case StringType: return "string";
    case ListType:
    switch (type.content.type)
    {
    case IntegerType: return "list<int>";
    case DoubleType: return "list<double>";
    case StringType: return "list<string>";
    case ListType: return "???? invalid ???";
    case DictType: return "???? invalid ???";
    case ObjectType: return "list<"+type.content.object_class+">";
    default: return "??? invalid ???";
    }
    case DictType: return "dict";
    case ObjectType: if (type.base.object_class.empty()) return "object"; else return type.base.object_class;
    default: return "??? invalid ???";
  }
}


std::string grt::fmt_arg_spec_list(const ArgSpecList &arglist)
{
  std::string args;
  
  for (ArgSpecList::const_iterator arg= arglist.begin();
       arg != arglist.end(); ++arg)
  {
    if (!args.empty())
      args.append(", ");

    args.append(fmt_type_spec(arg->type));
    if (!arg->name.empty())
      args.append(" ").append(arg->name);
  }
  return args;
}


/**
 ****************************************************************************
 * @brief Traverses a dict or object and return the requested value
 *
 *   This will treat the given value as a tree, where each node is named by 
 * the key in its parent.
 *   For dictionaries or objects, the path component is used as the key name. 
 * For lists, the given component is used to match the name attribute of each 
 * list item or if its a number, it's used as the index in the list.
 *
 * @param value a dict or object type value to be traversed which nested dicts
 * @param path a / separated list of key names to the value to be returned.
 *
 * @return A pointer to the value if it's found or NULL otherwise.
 *****************************************************************************/
ValueRef grt::get_value_by_path(const ValueRef &root, const std::string &path)
{
  std::string prefix, suffix;
  ValueRef value= root;
  bool ok= true;

  if (path == "/")
    return value;

  suffix= path.substr(1); // skip the /
  // fix possible typos
  base::replace(suffix, "//", "/");

  while (!suffix.empty() && value.is_valid())
  {
    prefix= base::pop_path_front(suffix);
    
    if (value.type() == DictType)
    {
      value= DictRef::cast_from(value).get(prefix);
      if (!value.is_valid())
      {
        ok= false;
        break;
      }
    }
    else if (value.type() == ListType)
    {
      BaseListRef list(BaseListRef::cast_from(value));
      long lindex;
      
      if (sscanf(prefix.c_str(), "%li", &lindex) == 1 && lindex >= 0 && lindex < (long)list.count())
        value= list.get(lindex);
      else
      {
        ok= false;
        break;
      }
    }
    else if (value.type() == ObjectType)
    {
      value= ObjectRef::cast_from(value)->get_member(prefix);
      if (!value.is_valid())
      {
        ok= false;
        break;
      }
    }
    else
    {
      ok= false;
      value.clear();
      break;
    }
  }
  
  if (!ok)
    log_error("Invalid path element '%s' in path: %s\n", prefix.c_str(), path.c_str());
  return value;
}


/**
 ****************************************************************************
 * @brief Traverses a dict or object and changes the value in the path
 *
 *   This will treat the given value as a tree, where each node is named by 
 * the key in its parent.
 *   For dics and objects, the path component is used as the key name. For lists,
 * the given component is used to match the name attribute of each list item.
 *
 * @param value a value to be traversed which nested values
 * @param path a / separated list of key names to the value to be changed.
 * @param new_value the value to assign in the dict
 *
 * @return true on success
 * @return false if some component of the path doesn't exist or the
 * referenced object is not a dictionary or list.
 *
 *****************************************************************************/
bool grt::set_value_by_path(const ValueRef &value, const std::string &path, const ValueRef &new_value)
{
  std::string front, last;

  if (path == "/")
    return false;

  if (path.find('/') == std::string::npos)
    return false;

  last= path;
  if (*last.rbegin() == '/')  // strip ending / if there is one
    last= last.substr(0, last.length() - 1);

  size_t last_slash= last.rfind('/');
  if (last_slash == std::string::npos)
    front= last;
  else
    if (last_slash == 0)
      front= "/";
    else
      front= last.substr(0, last_slash);
  last= last.substr(last.rfind('/') + 1);
  
  ValueRef container(get_value_by_path(value, front));
  if (container.is_valid())
  {
    if (container.type() == DictType)
      DictRef::cast_from(container).set(last, new_value);
    else if (container.type() == ObjectType)
      ObjectRef::cast_from(container).set_member(last, new_value);
    else if (container.type() == ListType)
    {
      BaseListRef list(BaseListRef::cast_from(container));

      int i; // Unlikely we will ever need an index beyond 32bit.
      if (sscanf(last.c_str(), "%i", &i) != 1 || i >= (int)list.count())
        return false;
      
      list.gset(i, new_value);
    }
    else
      return false;

    return true;
  }
  
  return false;
}






static ObjectRef find_child_object(const BaseListRef &list, const std::string &id,
                                   bool recursive, std::set<internal::Value*> &visited);
static ObjectRef find_child_object(const DictRef &dict, const std::string &id,
                                   bool recursive, std::set<internal::Value*> &visited);
static ObjectRef find_child_object(const ObjectRef &object, const std::string &id,
                                   bool recursive, std::set<internal::Value*> &visited);


static ObjectRef find_child_object(const BaseListRef &list, const std::string &id,
                                     bool recursive, std::set<internal::Value*> &visited)
{
  if (!list.is_valid()) throw std::invalid_argument("list is invalid");

  if (visited.find(list.valueptr())!=visited.end())
    return ObjectRef();
  visited.insert(list.valueptr());

  size_t c= list.count(), i;
  ObjectRef found;

  for (i= 0; i < c && !found.is_valid(); i++)
  {
    ValueRef value= list.get(i);

    if (value.type() == ObjectType)
    {
      ObjectRef ovalue(ObjectRef::cast_from(value));
      if (ovalue.id()==id)
        return ovalue;
      
      if (recursive)
        found= find_child_object(ovalue, id, recursive, visited);
    }
    else if (value.type() == ListType && recursive)
      found= find_child_object(BaseListRef::cast_from(value), id, recursive, visited);
    else if (value.type() == DictType && recursive)
      found= find_child_object(DictRef::cast_from(value), id, recursive, visited);
  }

  return found;
}


static ObjectRef find_child_object(const DictRef &dict, const std::string &id, 
                                     bool recursive, std::set<internal::Value*> &visited)
{
  if (!dict.is_valid()) 
    throw std::invalid_argument("dict is invalid");

  if (visited.find(dict.valueptr())!=visited.end())
    return ObjectRef();
  visited.insert(dict.valueptr());

  ObjectRef found;

  DictRef::const_iterator iter;
  for (iter= dict.begin(); iter != dict.end() && !found.is_valid(); ++iter)
  {
    ValueRef value= iter->second;
    std::string key= iter->first;

    if (value.type() == ObjectType)
    {
      ObjectRef ovalue(ObjectRef::cast_from(value));
      if (ovalue.id()==id)
        return ovalue;
      
      if (recursive)
        found= find_child_object(ovalue, id, recursive, visited);
    }
    else if (value.type() == ListType && recursive)
      found= find_child_object(BaseListRef::cast_from(value), id, recursive, visited);
    else if (value.type() == DictType && recursive)
      found= find_child_object(DictRef::cast_from(value), id, recursive, visited);
  }

  return found;
}


static ObjectRef find_child_object(const ObjectRef &object, const std::string &id,
                                     bool recursive, std::set<internal::Value*> &visited)
{
  if (!object.is_valid()) throw std::invalid_argument("object is invalid");

  if (visited.find(object.valueptr())!=visited.end())
    return ObjectRef();
  visited.insert(object.valueptr());

  ObjectRef found;

  if (object.id() == id)
    return object;
  
  MetaClass *mclass= object->get_metaclass();

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

      std::string name= member->second.name;
      ValueRef value= object->get_member(name);
      
      // ignore owner
      if (name == "owner") continue;

      switch (value.type())
      {
      case ListType:
        {
          BaseListRef list(BaseListRef::cast_from(value));
          
          if (recursive && !is_simple_type(list.content_type()))
          {
            found= find_child_object(list, id, recursive, visited);
            if (found.is_valid())
              return found;
          }
        }
        break;
      case DictType:
        {
          DictRef dict(DictRef::cast_from(value));
          
          if (recursive && !is_simple_type(dict.content_type()))
          {
            found= find_child_object(dict, id, recursive, visited);
            if (found.is_valid())
              return found;
          }
        }
        break;
      case ObjectType:
        if (ObjectRef::cast_from(value).id() == id)
          return ObjectRef::cast_from(value);
        
        if (recursive)
        {
          found= find_child_object(ObjectRef::cast_from(value), id, recursive, visited);
          if (found.is_valid())
            return found;
        }
        break;
      default:
        break;
      }
    }
    
    mclass= mclass->parent();
  }
  while (mclass != 0);

  return ObjectRef();
}



ObjectRef grt::find_child_object(const DictRef &dict, const std::string &id, bool recursive)
{
  std::set<internal::Value*> visited;

  return ::find_child_object(dict, id, recursive, visited);
}


ObjectRef grt::find_child_object(const BaseListRef &list, const std::string &id, bool recursive)
{
  std::set<internal::Value*> visited;

  return ::find_child_object(list, id, recursive, visited);
}


ObjectRef grt::find_child_object(const ObjectRef &object, const std::string &id, bool recursive)
{
  std::set<internal::Value*> visited;

  return ::find_child_object(object, id, recursive, visited);
}


class search_in_list_pred : public std::unary_function<std::string,bool>
{
private:
  ObjectListRef _list;
  public:
    search_in_list_pred(const ObjectListRef& list) : _list(list) {};
    result_type operator()(argument_type arg) const
    {
        return find_named_object_in_list(_list, arg).is_valid();
    }
};


std::string grt::get_name_suggestion_for_list_object(const BaseListRef &baselist, const std::string &prefix, bool serial)
{
  return get_name_suggestion(search_in_list_pred(ObjectListRef::cast_from(baselist)),prefix,serial);
}

void CopyContext::copy_list(BaseListRef &list, const BaseListRef &source, bool dontfollow)
{
  for (size_t c= source.count(), i= 0; i < c; i++)
  {
    grt::ValueRef value(source.get(i));

    if (!value.is_valid() || is_simple_type(value.type()))
      list.ginsert(value);
    else if (value.type() == ListType)
    {
      if (dontfollow)
        list.ginsert(value);
      else
      {
        BaseListRef clist(list.get_grt());
        copy_list(clist, BaseListRef::cast_from(value), dontfollow);
        list.ginsert(clist);
      }
    }
    else if (value.type() == DictType)
    {
      if (dontfollow)
        list.ginsert(value);
      else
      {
        DictRef cdict(list.get_grt());
        copy_dict(cdict, DictRef::cast_from(value), dontfollow);
        list.ginsert(cdict);
      }
    }
    else if (value.type() == ObjectType)
    {
      if (dontfollow)
        list.ginsert(value);
      else
        list.ginsert(copy(ObjectRef::cast_from(value)));
    }
  }
}


void CopyContext::copy_dict(DictRef &dict, const DictRef &source, bool dontfollow)
{
  for (DictRef::const_iterator iter= source.begin(); iter != source.end(); ++iter)
  {
    std::string key= iter->first;
    grt::ValueRef value= iter->second;

    if (!value.is_valid() || is_simple_type(value.type()))
      dict.set(key, value);
    else if (value.type() == ListType)
    {
      if (dontfollow)
        dict.set(key, value);
      else
      {
        BaseListRef clist(dict.get_grt());
        copy_list(clist, BaseListRef::cast_from(value), dontfollow);
        dict.set(key, clist);
      }
    }
    else if (value.type() == DictType)
    {
      if (dontfollow)
        dict.set(key, value);
      else
      {
        DictRef cdict(dict.get_grt());
        copy_dict(cdict, DictRef::cast_from(value), dontfollow);
        dict.set(key, cdict);
      }
    }
    else if (value.type() == ObjectType)
    {
      if (dontfollow)
        dict.set(key, value);
      else
        dict.set(key, copy(ObjectRef::cast_from(value)));
    }
  }
}


static void fixup_object_copied_references(ObjectRef copy, 
                                           std::map<std::string, ValueRef> &object_copies)
{
  MetaClass *metac(copy.get_metaclass());
    
  do 
  {
    for (MetaClass::MemberList::const_iterator mem= metac->get_members_partial().begin();
         mem != metac->get_members_partial().end(); ++mem)
    {
      if (mem->second.overrides) continue;

      std::string k= mem->second.name;
      grt::ValueRef v= copy->get_member(k);
      
      if (!v.is_valid())
        continue;
      
      Type type= mem->second.type.base.type;
      
      bool dontfollow= !mem->second.owned_object;
      
      if (type == ListType)
      {
        BaseListRef list(BaseListRef::cast_from(v));

        for (size_t c= list.count(), i= 0; i < c; i++)
        {
          ValueRef value(list.get(i));
          
          if (value.type() != ObjectType)
            continue;
          
          if (dontfollow)
          {
            ObjectRef obj(ObjectRef::cast_from(value));
            if (object_copies.find(obj.id()) != object_copies.end())
            {
              list.gset(i, object_copies[obj.id()]);
            }
          }
          else
            fixup_object_copied_references(ObjectRef::cast_from(value), object_copies);
        }
      }
      else if (type == DictType)
      {
        DictRef dict(DictRef::cast_from(v));
        for (DictRef::const_iterator iterator= dict.begin(); iterator != dict.end(); iterator++)
        {
          std::string k= iterator->first;
          ValueRef value= iterator->second;
          if (value.type() != ObjectType)
            continue;
          
          if (dontfollow)
          {
            ObjectRef obj(ObjectRef::cast_from(value));
            if (object_copies.find(obj.id()) != object_copies.end())
              dict[k]= object_copies[obj.id()];
          }
          else
            fixup_object_copied_references(ObjectRef::cast_from(value), object_copies);
        }
      }
      else if (type == ObjectType)
      {
        // if a dontfollow member is being copied too, it should be updated later to 
        // point to the new copy
        if (dontfollow)
        {
          ObjectRef obj(ObjectRef::cast_from(v));
          if (object_copies.find(obj.id()) != object_copies.end())
            copy.set_member(k, object_copies[obj.id()]);
        }
        else
          fixup_object_copied_references(ObjectRef::cast_from(v), object_copies);
      }
    }
    
    metac= metac->parent();
  }
  while (metac != 0);
}

ObjectRef CopyContext::duplicate_object(ObjectRef object, std::set<std::string> skip_members, bool _dontfollow)
{
  if (object.is_valid())
  {
    MetaClass *metac(object.get_metaclass());
    ObjectRef copy= metac->allocate();

    // save a mapping from the original value to its copy
    object_copies[object.id()]= copy;
    
    do
    {
      for (MetaClass::MemberList::const_iterator mem= metac->get_members_partial().begin(); 
           mem != metac->get_members_partial().end(); ++mem)
      {
        std::string k= mem->second.name;
        grt::ValueRef v= object.get_member(k);
        
        if (skip_members.find(k) != skip_members.end() || mem->second.overrides)
          continue;

        if (mem->second.calculated) // don't copy calculated members
          continue;

        Type type= mem->second.type.base.type;
        
        bool dontfollow= _dontfollow || !mem->second.owned_object;
        
        if (is_simple_type(type))
        {
          copy.set_member(k, v);
        }
        else if (type == ListType)
        {
          BaseListRef clist(BaseListRef::cast_from(copy.get_member(k)));
          BaseListRef olist(BaseListRef::cast_from(v));
          
          copy_list(clist, olist, dontfollow);
        }
        else if (type == DictType)
        {
          DictRef dict(DictRef::cast_from(copy.get_member(k)));
          copy_dict(dict, DictRef::cast_from(v), dontfollow);
        }
        else if (type == ObjectType)
        {
          // if a dontfollow member is being copied too, it should be updated later to 
          // point to the new copy
          if (dontfollow)
          {
            ObjectRef obj(ObjectRef::cast_from(v));
            if (obj.is_valid() && object_copies.find(obj.id()) != object_copies.end())
              copy.set_member(k, object_copies[obj.id()]);
            else
              copy.set_member(k, v);
          }
          else
          {
            if (k == "owner") throw; // consistency check
            ObjectRef vcopy(duplicate_object(ObjectRef::cast_from(v), std::set<std::string>(), false));
            copy.set_member(k, vcopy);
          }
        }
      }

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

    return copy;
  }
  return ObjectRef();
}

void grt::update_ids(ObjectRef object, const std::set<std::string>& skip_members)
{
  if (!object.is_valid())
    return;
  MetaClass *metac(object.get_metaclass());

  do
  {
    for (MetaClass::MemberList::const_iterator mem= metac->get_members_partial().begin(); 
      mem != metac->get_members_partial().end(); ++mem)
    {
      std::string k= mem->second.name;
      grt::ValueRef v= object.get_member(k);

      if (skip_members.find(k) != skip_members.end() || mem->second.overrides)
        continue;

      if (mem->second.calculated)
        continue;

      Type type= mem->second.type.base.type;

      if (!mem->second.owned_object)
        continue;

      if (type == ListType)
      {
        BaseListRef olist(BaseListRef::cast_from(v));
        for (size_t c = olist.count(), i = 0; i < c; i++)
        {
          if (ObjectRef::can_wrap(olist[i]))
            update_ids(ObjectRef::cast_from(olist[i]),skip_members);
        }
      }
      else if (type == DictType)
      {
        DictRef::cast_from(v);
      }
      else if (type == ObjectType)
      {
        update_ids(ObjectRef::cast_from(v),skip_members);
      }
    }
    metac= metac->parent();
  }
  while (metac != 0);
  object->__set_id(get_guid());
}

void grt::append_contents(BaseListRef target, BaseListRef source)
{
  if (source.is_valid())
  {
    for (size_t c= source.count(), i= 0; i < c; i++)
      target.ginsert(source[i]);
  }
}


void grt::replace_contents(BaseListRef target, BaseListRef source)
{
  for (size_t c= target.count(), i= 0; i < c; i++)
    target.remove(0);

  for (size_t c= source.count(), i= 0; i < c; i++)
    target.ginsert(source[i]);
}



void grt::merge_contents_by_name(ObjectListRef target, ObjectListRef source,
                                 bool replace_matching)
{
  std::map<std::string, int> known_names;

  for (size_t c= target.count(), i= 0; i < c; i++)
    known_names[StringRef::cast_from(target[i].get_member("name"))]= (int)i;

  for (size_t c= source.count(), i= 0; i < c; i++)
  {
    ObjectRef value(source[i]);
    std::string name= StringRef::cast_from(value.get_member("name"));
    if (known_names.find(name) != known_names.end())
    {
      if (replace_matching)
        target.set(known_names[name], value);
    }
    else
      target.insert(value);
  }
}



void grt::merge_contents_by_id(ObjectListRef target, ObjectListRef source,
                                 bool replace_matching)
{
  std::map<std::string, size_t> index_of_known_ids;

  for (size_t c= target.count(), i= 0; i < c; i++)
    index_of_known_ids[target[i].id()]= i;

  for (size_t c= source.count(), i= 0; i < c; i++)
  {
    ObjectRef value(source[i]);
    if (index_of_known_ids.find(value.id()) != index_of_known_ids.end())
    {
      if (replace_matching)
        target.set(index_of_known_ids[value.id()], value);
    }
    else
      target.insert(value);
  }
}


void grt::merge_contents(DictRef target, DictRef source, bool overwrite)
{
  DictRef::const_iterator iter;
  for (iter= source.begin(); iter != source.end(); ++iter)
  {
    std::string k= iter->first;
    ValueRef v= iter->second;

    if (!overwrite && target.has_key(k))
      continue;

    target.set(k, v);
  }
}


void grt::replace_contents(DictRef target, DictRef source)
{
  DictRef::const_iterator iter, current;

  iter= target.begin();
  while (iter != target.end())
  {
    current= iter;
    ++iter;
    target.remove(current->first);
  }

  for (iter= source.begin(); iter != source.end(); ++iter)
  {
    target.set(iter->first, iter->second);
  }
}


void grt::merge_contents(ObjectRef target, ObjectRef source)
{
  MetaClass *metac= source->get_metaclass();
  
  do
  {
    for (MetaClass::MemberList::const_iterator mem= metac->get_members_partial().begin();
         mem != metac->get_members_partial().end(); ++mem)
    {
      if (mem->second.overrides || mem->second.read_only) continue;
      
      std::string k= mem->second.name;
      ValueRef v= source->get_member(k);
      
      target.set_member(k, v);
    }
    metac= metac->parent();
  }
  while (metac != 0);
}


std::string grt::join_string_list(const StringListRef &list, const std::string &separator)
{
  std::string result;
  for (StringListRef::const_iterator i = list.begin(); i != list.end(); ++i)
  {
    if (i != list.begin())
      result.append(separator);
    result.append(*i);
  }
  return result;
}




ObjectRef CopyContext::copy(const ObjectRef &object, std::set<std::string> skip_members)
{
  ObjectRef copy= duplicate_object(object, skip_members, false);
  if (copy.is_valid())
    copies.push_back(copy);

  return copy;
}

ObjectRef CopyContext::shallow_copy(const ObjectRef &object)
{
  ObjectRef copy= duplicate_object(object, std::set<std::string>(), true);
  if (copy.is_valid())
    copies.push_back(copy);
  
  return copy;
}


void CopyContext::update_references()
{
  // go through everything that was copied, replacing copied object references
  // if needed
  for (std::list<ObjectRef>::iterator iter= copies.begin(); iter != copies.end(); ++iter)
    fixup_object_copied_references(*iter, object_copies);
}


ValueRef CopyContext::copy_for_object(ValueRef object)
{
  ObjectRef obj(ObjectRef::cast_from(object));
  if (object_copies.find(obj.id()) != object_copies.end())
    return object_copies[obj.id()];
  return ValueRef();
}

static ValueRef copy_value(ValueRef value, bool deep, internal::Object *owner)
{
  switch (value.type())
  {
  case AnyType:
    break;

  case IntegerType:
  case DoubleType:
  case StringType:
    // no need to duplicate simple values as they're immutable
    return value;

  case ListType:
    {
      BaseListRef orig(BaseListRef::cast_from(value));
      BaseListRef list(orig.content().get_grt(), orig.content_type(), orig.content_class_name());

      //Copy items
      if (deep)
        for (internal::List::raw_const_iterator iter= orig.content().raw_begin();
             iter != orig.content().raw_end(); ++iter)
          list.ginsert(copy_value(*iter, deep, 0));
      else
        for (internal::List::raw_const_iterator iter= orig.content().raw_begin();
             iter != orig.content().raw_end(); ++iter)
          list.ginsert(*iter);

      return list;
    }
  case DictType:
    {
      DictRef orig(DictRef::cast_from(value));
      DictRef dict(orig.content().get_grt(), orig.content_type(), orig.content_class_name());

      //Copy items
      if (deep)
        for (internal::Dict::const_iterator iter= orig.content().begin();
             iter != orig.content().end(); ++iter)
          dict.set(iter->first, copy_value(iter->second, deep, 0));
      else
        for (internal::Dict::const_iterator iter= orig.content().begin();
             iter != orig.content().end(); ++iter)
          dict.set(iter->first, iter->second);

      return dict;
    }
  case ObjectType:
    {
      ObjectRef obj(ObjectRef::cast_from(value));

      return copy_object(obj);
    }
  }
  
  return ValueRef();
}



ValueRef grt::copy_value(ValueRef value, bool deep)
{
  return ::copy_value(value, deep, 0);
}



bool grt::compare_list_contents(const ObjectListRef &l1, const ObjectListRef &l2)
{
  bool l1_valid= l1.is_valid();
  bool l2_valid= l2.is_valid();
  if (!l1_valid || !l2_valid)
    return (l1_valid == l2_valid);
  if (l1.count() != l2.count())
    return false;
  for (size_t n= 0, count= l1.count(); n < count; ++n)
  {
    grt::ObjectRef o1= l1.get(n);
    grt::ObjectRef o2= l2.get(n);
    if (o1.is_valid() != o2.is_valid())
      return false;
    if (o1.is_valid() && (o1.id() != o2.id()))
      return false;
  }
  return true;
}


void grt::remove_list_items_matching(ObjectListRef list, const boost::function<bool (grt::ObjectRef)> &matcher)
{
  for (size_t i= list.count(); i >= 1; --i)
  {
    if (matcher(list[i-1]))
      list.remove(i-1);
  }
}



// temporary code, should be replaced with dynamic loading once langauge support is pluginized

bool grt::init_python_support(grt::GRT *grt, const std::string &module_path)
{
  PythonModuleLoader *loader= new PythonModuleLoader(grt, module_path);
  if (!module_path.empty())
  {
    loader->get_python_context()->add_module_path(module_path, true);
#ifdef _WIN32
    loader->get_python_context()->add_module_path(module_path + "/python/site-packages", true);
#endif
  }
  grt->add_module_loader(loader);
  return true;
}


void grt::add_python_module_dir(grt::GRT *grt, const std::string &python_module_path)
{
  PythonModuleLoader *loader= dynamic_cast<PythonModuleLoader *>(grt->get_module_loader("python"));
  if (loader && !python_module_path.empty())
    loader->get_python_context()->add_module_path(python_module_path, true);
}

static void dump_value(const grt::ValueRef &value, int level, bool skip_spacing=false);

static bool dump_member(grt::ObjectRef object, const grt::MetaClass::Member *member, int level)
{
  if (!object.get_member(member->name).is_valid())
    printf("%*s%s = NULL", level, "  ", member->name.c_str());
  else if (member->type.base.type == grt::ObjectType && !member->owned_object)
    printf("%*s%s = <<%s>>", level, "  ", member->name.c_str(), grt::ObjectRef::cast_from(object.get_member(member->name))->get_string_member("name").c_str());
  else
  {
    printf("%*s%s = ", level, "  ", member->name.c_str());
    ::dump_value(object.get_member(member->name), level+1, true);
  }
  printf(";\n");
  return true;
}

static void dump_value(const grt::ValueRef &value, int level, bool skip_spacing)
{
  int s = skip_spacing ? 0 : 1;
  switch (value.type())
  {
    case grt::ListType:
    {
      grt::BaseListRef list(grt::BaseListRef::cast_from(value));
      printf("%*s%s", level*s, "  ", "[\n");
      for (size_t i = 0; i < list.count(); i++)
      {
        if (i > 0)
          printf(",\n");
        ::dump_value(list[i], level+1);
      }
      printf("%*s%s", level, "  ", "]");
      break;
    }

    case grt::DictType:
    {
      grt::DictRef dict(grt::DictRef::cast_from(value));
      printf("%*s%s", level, "  ", "  {\n");
      for (grt::DictRef::const_iterator iter = dict.begin(); iter != dict.end(); ++iter)
      {
        if (iter != dict.begin())
          printf(",\n");
        printf("%*s%s: ", level, "  ", iter->first.c_str());
        ::dump_value(iter->second, level+1, true);
      }
      printf("%*s%s", level, "  ", "}");
      break;
    }

    case grt::ObjectType:
    {
      grt::ObjectRef object(grt::ObjectRef::cast_from(value));
      grt::MetaClass *mc = object.get_metaclass();
      printf("%*s%s", level, "  ", "  {\n");
      mc->foreach_member(boost::bind(::dump_member, object, _1, level+1));
      printf("%*s%s", level, "  ", "}");
      break;
    }

    default:
      printf("%*s%s", level, "  ", value.debugDescription().c_str());
      break;
  }
}

void grt::dump_value(const grt::ValueRef &value)
{
  ::dump_value(value, 0);
  printf("\n");
}


