/* 
 * © 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.
 */

#ifndef _GRTPP_UTIL_H_
#define _GRTPP_UTIL_H_

#include "grtpp.h"

#include <set>

#include <glib.h>

std::string MYSQLGRT_PUBLIC strfmt(const char *fmt, ...) G_GNUC_PRINTF (1, 2);

#define GRTLIST_FOREACH(type, list, iter) \
  for (grt::ListRef<type>::const_iterator iter##end= list.end(), iter= list.begin(); iter != iter##end; ++iter)

#define GRTLIST_REVERSE_FOREACH(type, list, iter) \
  for (grt::ListRef<type>::const_reverse_iterator iter##end= list.rend(), iter= list.rbegin(); iter != iter##end; ++iter)


namespace grt
{
  std::string MYSQLGRT_PUBLIC type_to_str(Type type);
  Type MYSQLGRT_PUBLIC str_to_type(const std::string &str);

  std::string MYSQLGRT_PUBLIC fmt_simple_type_spec(const SimpleTypeSpec &type);
  std::string MYSQLGRT_PUBLIC fmt_type_spec(const TypeSpec &type);
  std::string MYSQLGRT_PUBLIC fmt_arg_spec_list(const ArgSpecList &args);
  
  ValueRef MYSQLGRT_PUBLIC get_value_by_path(const ValueRef &root, const std::string &path);
  bool MYSQLGRT_PUBLIC set_value_by_path(const ValueRef &value, const std::string &path, const ValueRef &new_value);

  inline bool is_container_type(Type type)
  {
    if (type == ListType
        || type == DictType
        || type == ObjectType)
      return true;
    return false;
  }
    
  inline bool is_simple_type(Type type)
  {
    if (type == IntegerType
        || type == DoubleType
        || type == StringType)
      return true;
    return false;
  }

  std::string MYSQLGRT_PUBLIC get_guid();
  
  inline std::string path_base(const std::string &path)
  {
    std::string::size_type p= path.rfind('/');
    if (p != std::string::npos)
      return path.substr(0, p);
    return "";
  }


  inline std::string path_last(const std::string &path)
  {
    std::string::size_type p= path.rfind('/');
    if (p != std::string::npos)
      return path.substr(p);
    return "";
  }

/*
  inline std::string format_message(const MYX_GRT_MSG &msg, bool withtype= false)
  {
    std::string text;
    
    if (withtype)
    {
      switch (msg.msg_type)
      {
      case MYX_MSG_INFO:  text= "Info: "; break;
      case MYX_MSG_WARNING: text= "Warning: "; break;
      case MYX_MSG_ERROR: text= "Error: "; break;
      }
    }
    
    text+= msg.msg;
    
    if (msg.msg_detail && msg.msg_detail->strings_num > 0 && *msg.msg_detail->strings[0])
    {
      text+= " ("+std::string(msg.msg_detail->strings[0])+")";
    }
    
    return text;
  }
*/

  template<class O>
    inline Ref<O> find_named_object_in_list(const ListRef<O> &list, 
                                            const std::string &value,
                                            bool case_sensitive= true,
                                            const std::string &name= "name")
    {
      size_t i, c= list.count();
      
      if (case_sensitive)
      {
        for (i= 0; i < c; i++)
        {
          Ref<O> tmp= list[i];
          
          if (tmp.is_valid() && tmp->get_string_member(name) == value)
            return tmp;
        }
      }
      else
      {
        for (i= 0; i < c; i++)
        {
          Ref<O> tmp= list[i];
          
          if (tmp.is_valid() && g_strcasecmp(tmp->get_string_member(name).c_str(), value.c_str())==0)
            return tmp;
        }
      }
      return Ref<O>();
    }
  

  template<class O>
    inline Ref<O> find_object_in_list(const ListRef<O> &list, 
                                 const std::string &id)
    {
      size_t i, c= list.count();
      for (i= 0; i < c; i++)
      {
        Ref<O> value= list[i];
        
        if (value.is_valid() && value->id() == id)
          return value;
      }
      return Ref<O>();
    }
   
   
   template<class O>
    inline size_t find_object_index_in_list(ListRef<O> list,
                                            const std::string &id)
    {
      size_t i, c= list.count();
      for (i= 0; i < c; i++)
      {
        Ref<O> value= list.get(i);
        
        if (value.is_valid() && value.id() == id)
          return i;
      }
      return -1;
    }
  
  
  MYSQLGRT_PUBLIC std::string get_name_suggestion_for_list_object(const BaseListRef &objlist, const std::string &prefix, bool serial= true);
  
  
  MYSQLGRT_PUBLIC ObjectRef find_child_object(const DictRef &dict, const std::string &id, bool recursive= true);
  MYSQLGRT_PUBLIC ObjectRef find_child_object(const BaseListRef &list, const std::string &id, bool recursive= true);
  MYSQLGRT_PUBLIC ObjectRef find_child_object(const ObjectRef &object, const std::string &id, bool recursive= true);

  // the following merge functions are not recursive
  MYSQLGRT_PUBLIC void append_contents(BaseListRef target, BaseListRef source);
  MYSQLGRT_PUBLIC void replace_contents(BaseListRef target, BaseListRef source);
  MYSQLGRT_PUBLIC void merge_contents_by_name(ObjectListRef target,
                                              ObjectListRef source,
                                              bool replace_matching);
  MYSQLGRT_PUBLIC void merge_contents_by_id(ObjectListRef target,
                                            ObjectListRef source,
                                            bool replace_matching);

  MYSQLGRT_PUBLIC void replace_contents(DictRef target, DictRef source);
  MYSQLGRT_PUBLIC void merge_contents(DictRef target, DictRef source, bool overwrite);
  MYSQLGRT_PUBLIC void merge_contents(ObjectRef target, ObjectRef source);
  
  MYSQLGRT_PUBLIC bool compare_list_contents(const ObjectListRef &list1, const ObjectListRef &list2);
  
  MYSQLGRT_PUBLIC void remove_list_items_matching(ObjectListRef list, const sigc::slot<bool,grt::ObjectRef> &matcher);
  
  MYSQLGRT_PUBLIC ObjectRef copy_object(GRT *grt, ObjectRef object, std::set<std::string> skip_members= std::set<std::string>());

  //XXX don't use this for objects, use CopyContext::copy() instead
  MYSQLGRT_PUBLIC ValueRef copy_value(ValueRef value, bool deep);

  struct MYSQLGRT_PUBLIC CopyContext
  {
    GRT *grt;
    std::map<internal::Value*, ValueRef> object_copies;
    std::list<ObjectRef> copies;
    
    CopyContext(GRT *agrt) : grt(agrt) {}
        
    ObjectRef copy(const ObjectRef &object, std::set<std::string> skip_members= std::set<std::string>());
    void finish() { update_references(); }
    void update_references();
    
  private:
    ObjectRef duplicate_object(ObjectRef object, std::set<std::string> skip_members);
    void copy_list(BaseListRef &list, const BaseListRef &source, bool dontfollow);
    void copy_dict(DictRef &dict, const DictRef &source, bool dontfollow);
  };
  
  
  // temporary code
  MYSQLGRT_PUBLIC bool init_python_support(grt::GRT *grt, const std::string &python_module_path);
  
  // diffing
  
  class DiffChange;

  struct MYSQLGRT_PUBLIC Omf
  {
    virtual ~Omf() {};
    virtual bool less(const ValueRef& , const ValueRef&) const= 0;
    virtual bool equal(const ValueRef& , const ValueRef&) const= 0;
  };

  typedef sigc::slot<bool, ValueRef, ValueRef,std::string> TSlotNormilizerSlot;

  MYSQLGRT_PUBLIC
    DiffChange *diff_make(const ValueRef &source, const ValueRef &target, const Omf* omf,const TSlotNormilizerSlot sqlDefinitionCmp);
  
  MYSQLGRT_PUBLIC
    void diff_apply(ValueRef &source, DiffChange &change);

  MYSQLGRT_PUBLIC 
    void diff_dump(const DiffChange &change);

  MYSQLGRT_PUBLIC
    void diff_delete(DiffChange *change);

};

#endif
