/* 
 * Copyright (c) 2007, 2012, 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 "grtpp_helper.h"
#include "grtpp_util.h"

#include "base/file_functions.h"
#include "base/util_functions.h"
#include "base/string_utilities.h"

#include <stdio.h>
#include <errno.h>

#include <glib/gstdio.h>

using namespace grt;


static std::string cppize_class_name(std::string name)
{
  std::string::size_type p;
  while ((p= name.find('.')) != std::string::npos)
    name[p]= '_';
  return name;
}


static std::string format_type_cpp(const TypeSpec &type, bool unknown_as_void= false)
{
  std::string s;
  
  switch (type.base.type)
  {
  case IntegerType: return "grt::IntegerRef";
  case DoubleType: return "grt::DoubleRef";
  case StringType: return "grt::StringRef";
  case ListType:
    switch (type.content.type)
    {
    case IntegerType: return "grt::IntegerListRef";
    case DoubleType: return "grt::DoubleListRef";
    case StringType: return "grt::StringListRef";
    case ListType: return "???? invalid ???";
    case DictType: return "???? invalid ???";
    case ObjectType: return "grt::ListRef<"+cppize_class_name(type.content.object_class)+">";
    default: return "??? invalid ???";
    }
  case DictType: return "grt::DictRef";
  case ObjectType: return "grt::Ref<"+cppize_class_name(type.base.object_class)+">";
  case UnknownType: if (unknown_as_void) return "void";
  default: return "??? invalid ???";
  }
  return s;
}


static std::string format_arg_list(const std::vector<ArgSpec> &args)
{
  std::string s;
  
  for (std::vector<ArgSpec>::const_iterator iter= args.begin();
       iter != args.end(); ++iter)
  {
    if (!s.empty())
      s.append(", ");
    
    if (is_simple_type(iter->type.base.type))
    {
      switch (iter->type.base.type)
      {
        case IntegerType:
          s.append("long ");
          break;
        case DoubleType:
          s.append("double ");
          break;
        case StringType:
          s.append("const std::string &");
          break;
        default:
          break;
      }
    }
    else
      s.append("const ").append(format_type_cpp(iter->type)).append(" &");
    s.append(iter->name);
  }
  return s;
}


static std::string format_signal_args(const std::vector<MetaClass::SignalArg> &args)
{
  std::string s;
  
  for (std::vector<MetaClass::SignalArg>::const_iterator iter= args.begin(); iter != args.end(); ++iter)
  {
    if (!s.empty()) s.append(", ");
    
    switch (iter->type)
    {
    case MetaClass::BoolSArg: s.append("bool"); break;
    case MetaClass::IntSArg: s.append("int"); break;
    case MetaClass::DoubleSArg: s.append("double"); break;
    case MetaClass::StringSArg: s.append("std::string"); break;
    case MetaClass::ObjectSArg: s.append("grt::Ref<").append(cppize_class_name(iter->object_class)).append(">"); break;
    }
  }
  return s;
}


static std::string format_signal_names(const std::vector<MetaClass::SignalArg> &args)
{
  std::string s;
  
  for (std::vector<MetaClass::SignalArg>::const_iterator iter= args.begin(); iter != args.end(); ++iter)
  {
    if (!s.empty()) s.append(", ");
    
    s.append(iter->name);
  }
  return s;
}


//static std::string format_argname_list(const std::vector<ArgSpec> &args)
//{
//  std::string s;
//  for (std::vector<ArgSpec>::const_iterator iter= args.begin();
//       iter != args.end(); ++iter)
//  {
//    if (!s.empty())
//      s.append(", ");
//    s.append(iter->name);
//  }
//  return s;  
//}

static std::string format_wraparg_list(const std::vector<ArgSpec> &args)
{
  std::string s;
  int i= 0;
  for (std::vector<ArgSpec>::const_iterator iter= args.begin();
       iter != args.end(); ++iter)
  {
    char idx[32]; 
    sprintf(idx, "%i", i++);
    if (!s.empty())
      s.append(", ");
    s.append(format_type_cpp(iter->type)).append("::cast_from(args[").append(idx).append("])");
  }
  return s;
}


//--------------------------------------------------------------------------------
// Object Class Generator


struct ClassImplGenerator 
{
  MetaClass *gstruct;
  FILE *header_file;
  std::string cname;
  std::string pname;
  const std::map<std::string,MetaClass::Member> &members;
  const std::map<std::string,MetaClass::Method> &methods;
  bool needs_body;


  ClassImplGenerator(MetaClass *gstru, FILE *hdr) :
    gstruct(gstru), members(gstruct->get_members_partial()), methods(gstruct->get_methods_partial())
  {
    header_file= hdr;
    cname= cppize_class_name(gstruct->name());
    pname= cppize_class_name(gstruct->parent()->name() != internal::Object::static_class_name() ? gstruct->parent()->name() : "grt::internal::Object");
    
    needs_body= false;
  }


  void output_overriden_list_reset_code(FILE *f)
  {    
    for (std::map<std::string,MetaClass::Member>::const_iterator mem= members.begin();
         mem != members.end(); ++mem)
    {
      if ((mem->second.type.base.type == ListType || mem->second.type.base.type == DictType)
          && !mem->second.calculated && mem->second.overrides)
      {
        // we need to reset the list content type descriptor to the overriden types
        // we can't just recreate the list because constructors higher up in the
        // hierarchy could get references to the already initialized list
        fprintf(f, "    _%s.content().__retype(grt::ObjectType, \"%s\");\n", 
                mem->first.c_str(), mem->second.type.content.object_class.c_str());
      }
    }
  }

  void output_constructor_init_list(FILE *f)
  {
    fprintf(f, "  : %s(grt, meta ? meta : grt->get_metaclass(static_class_name()))",
            pname.c_str());
    for (std::map<std::string,MetaClass::Member>::const_iterator mem= members.begin(); 
         mem != members.end(); ++mem)
    {
      if (mem->second.calculated || mem->second.overrides)
        continue;
      
      std::string defval(mem->second.default_value);
      
      switch (mem->second.type.base.type)
      {
      case ListType:
      case DictType:
        fprintf(f, ",\n    _%s(grt, this, %s)", mem->first.c_str(), mem->second.null_content_allowed?"true":"false");
        break;
      case StringType:
        fprintf(f, ",\n     _%s(\"%s\")", mem->first.c_str(), defval.c_str());
        break;
      case IntegerType:
        fprintf(f, ",\n     _%s(%s)", mem->first.c_str(), defval.empty() ? "0" : defval.c_str());
        break;
      case DoubleType:
        fprintf(f, ",\n     _%s(%s)", mem->first.c_str(), defval.empty() ? "0.0" : defval.c_str());
        break;
      default: break;
      }
    }
    if (!gstruct->get_attribute("simple-impl-data").empty())
      fprintf(f, ",\n    _data(0), _release_data(NULL)");
    else if ((needs_body && gstruct->impl_data()))
      fprintf(f, ",\n    _data(0)");
    fprintf(f, "\n");
  }

  void generate_class_doc(FILE *f)
  {
    std::string doc = gstruct->get_attribute("desc", false);
    if (!doc.empty())
      fprintf(f, "  /** %s */\n", doc.c_str());
  }

  void generate_getter_doc(FILE *f, const MetaClass::Member &member)
  {
    std::string doc= gstruct->get_member_attribute(member.name, "desc", false);
    
    fprintf(f, "  /** Getter for attribute %s%s\n", member.name.c_str(), member.read_only ? " (read-only)" : "");
    fprintf(f, "   \n");
    fprintf(f, "    %s\n", doc.c_str());
    fprintf(f, "   \\par In Python:\nvalue = obj.%s\n", member.name.c_str());
    fprintf(f, "   */\n");
  }

  void generate_setter_doc(FILE *f, const MetaClass::Member &member)
  {
    std::string doc= gstruct->get_member_attribute(member.name, "desc", false);
    
    fprintf(f, "  /** Setter for attribute %s\n", member.name.c_str());
    fprintf(f, "   \n");
    fprintf(f, "    %s\n", doc.c_str());
    fprintf(f, "    \\par In Python:\nobj.%s = value\n", member.name.c_str());
    fprintf(f, "   */\n");
  }

  void generate_method_doc(FILE *f, const MetaClass::Method &method)
  {
    std::string doc= gstruct->get_member_attribute(method.name, "desc", false);
    
    fprintf(f, "  /** Method. %s\n", doc.c_str());
    for (ArgSpecList::const_iterator arg = method.arg_types.begin(); arg != method.arg_types.end(); ++arg)
    {
      fprintf(f, "  \\param %s %s\n", arg->name.c_str(), gstruct->get_member_attribute(method.name+":"+arg->name, "desc").c_str());
    }
    doc = gstruct->get_member_attribute(method.name+":return", "desc", false);
    fprintf(f, "  \\return %s\n", doc.c_str());
    fprintf(f, "\n");
    fprintf(f, "   */\n");
  }

  void generate_class_header(const std::string &dll_export)
  {
    FILE *f= header_file;
 
    // check if we will need a implementation file (.cpp)
    if (gstruct->force_impl() || !methods.empty())
      needs_body= true;
     
    for (std::map<std::string,MetaClass::Member>::const_iterator iter= members.begin(); 
         iter != members.end(); ++iter)
    {
      if (iter->second.delegate_get || iter->second.delegate_set || iter->second.calculated)
      {
        needs_body= true;
        break;
      }
    }

    generate_class_doc(f);
    fprintf(f, "class %s %s : public %s\n", needs_body ? dll_export.c_str() : "", cname.c_str(), pname.c_str());
    fprintf(f, "{\n");
    fprintf(f, "  typedef %s super;\n", pname.c_str());
    fprintf(f, "public:\n");
    std::string klass = gstruct->get_attribute("simple-impl-data");
    if (gstruct->impl_data() || !klass.empty())
    {
      if (klass.empty())
      {
        fprintf(f, "  class ImplData;\n");
        fprintf(f, "  friend class ImplData;\n");
      }
      else
        fprintf(f, "  typedef %s ImplData;\n", klass.c_str());
    }
    // generate constructors
    bool default_ctor_created= false;
      
    for (std::map<std::string,MetaClass::Method>::const_iterator iter= methods.begin(); 
         iter != methods.end(); ++iter)
    {
      if (iter->second.constructor)
      {
        fprintf(f, "  %s(grt::GRT *grt%s%s, grt::MetaClass *meta=0);\n",
                cname.c_str(), 
                iter->second.arg_types.empty()?"":", ", format_arg_list(iter->second.arg_types).c_str());

        if (iter->second.arg_types.empty())
          default_ctor_created= true;
      }
    }

    if (!default_ctor_created)
    {
      fprintf(f, "  %s(grt::GRT *grt, grt::MetaClass *meta=0)\n",
              cname.c_str());

      output_constructor_init_list(f);

      fprintf(f, "\n  {\n");
      // reinit overriden lists
      output_overriden_list_reset_code(f);
      fprintf(f, "  }\n");
      fprintf(f, "\n");
    }

    if (needs_body || !gstruct->get_attribute("simple-impl-data").empty())
    {
      if (gstruct->get_attribute("simple-impl-data").empty())
        fprintf(f, "  virtual ~%s();\n\n", cname.c_str());
      else
      {
        fprintf(f, "  virtual ~%s() { if (_release_data && _data) _release_data(_data);  }\n\n", cname.c_str());
      }
    }
   
    fprintf(f, "  static std::string static_class_name() { return \"%s\"; }\n", 
            gstruct->name().c_str());
    fprintf(f, "\n");
    
    // generate signal access methods
    for (MetaClass::SignalList::const_iterator iter= gstruct->get_signals_partial().begin();
         iter != gstruct->get_signals_partial().end(); ++iter)
    {
      fprintf(f, "  // args: %s\n", format_signal_names(iter->arg_types).c_str());
      fprintf(f, "  boost::signals2::signal<void (%s) >* signal_%s() { return &_signal_%s; }\n",
              format_signal_args(iter->arg_types).c_str(),
              iter->name.c_str(),
              iter->name.c_str());
    }
    
    // generate member variable accessors
    for (MetaClass::MemberList::const_iterator iter= members.begin(); 
         iter != members.end(); ++iter)
    {
      if (iter->second.private_)
        continue;

      bool overrides_with_same_type= false;
      // if the member is overriden, check if the type has been changed
      if (iter->second.overrides && gstruct->parent())
      {
        const MetaClass::Member *member= gstruct->parent()->get_member_info(iter->second.name);

        if (member && member->type == iter->second.type)
          overrides_with_same_type= true;
      }

      // getter
      if (iter->second.owned_object)
        fprintf(f, "  // %s is owned by %s\n", iter->second.name.c_str(), cname.c_str());

      generate_getter_doc(f, iter->second);
      if (iter->second.delegate_get)
      {
        fprintf(f, "  %s %s() const;\n",
                format_type_cpp(iter->second.type).c_str(), iter->second.name.c_str());
      }
      else
      {
        if (iter->second.overrides)
        {
          if (!overrides_with_same_type)
          {
            fprintf(f, "  %s %s() const { return %s::cast_from(_%s); }\n",
                    format_type_cpp(iter->second.type).c_str(), iter->second.name.c_str(), 
                    format_type_cpp(iter->second.type).c_str(), iter->second.name.c_str());
          }
          else
          {
            // if we're overriding and the setter is delegated, we need to declare the getter too
            if (iter->second.delegate_set)
              fprintf(f, "  %s %s() const { return super::%s(); }\n",
                      format_type_cpp(iter->second.type).c_str(), iter->second.name.c_str(), 
                      iter->second.name.c_str());
          }
        }
        else
          fprintf(f, "  %s %s() const { return _%s; }\n",
                  format_type_cpp(iter->second.type).c_str(), iter->second.name.c_str(), iter->second.name.c_str());
      }
      
      // setter
      
      if (iter->second.read_only)
        fprintf(f, "private: // the next attribute is read-only\n");
      else
        generate_setter_doc(f, iter->second);
      if (iter->second.overrides)
      {
        if (overrides_with_same_type)
        {
          // check if the override is for overriding the setter
          if (iter->second.delegate_set)
          {
            fprintf(f, "  virtual void %s(const %s &value);\n",
                    iter->second.name.c_str(), format_type_cpp(iter->second.type).c_str());
          }
        }
        else
        {
          // variable is overriding another

          // list and dict members cannot be changed
          if (iter->second.type.base.type != ListType && iter->second.type.base.type != DictType)
          {
            if (iter->second.delegate_set)
              fprintf(f, "  virtual void %s(const %s &value);\n",
                      iter->second.name.c_str(), format_type_cpp(iter->second.type).c_str());
            else
              fprintf(f, "  virtual void %s(const %s &value) { super::%s(value); }\n",
                      iter->second.name.c_str(), format_type_cpp(iter->second.type).c_str(),
                      iter->second.name.c_str());
          }
        }
      }
      else
      {
        if (iter->second.delegate_set)
        {
          fprintf(f, "  virtual void %s(const %s &value);\n",
                  iter->second.name.c_str(), format_type_cpp(iter->second.type).c_str());
        }
        else // read-only vars need setter for unserialization  if (!iter->second.read_only)
        {
          if (!iter->second.calculated)
          {
            fprintf(f, "  virtual void %s%s(const %s &value)\n  {\n",
                    /*iter->second.read_only?"__":*/"", iter->second.name.c_str(),
                    format_type_cpp(iter->second.type).c_str());
            
            fprintf(f, "    grt::ValueRef ovalue(_%s);\n", iter->second.name.c_str());
            if (iter->second.owned_object)
            {
              // if member is owned by this object, we have to mark/unmark it as global
              // in case we're in the global tree as well (done in owned_member_changed)
              fprintf(f, "\n");
              fprintf(f, "    _%s= value;\n", iter->second.name.c_str());
              fprintf(f, "    owned_member_changed(\"%s\", ovalue, value);\n", iter->second.name.c_str());
            }
            else
            {
              fprintf(f, "   _%s= value;\n", iter->second.name.c_str());
              fprintf(f, "    member_changed(\"%s\", ovalue, value);\n", iter->second.name.c_str());
            }
            fprintf(f, "  }\n");
          }
        }
      }
      if (iter->second.read_only)
        fprintf(f, "public:\n");
      fprintf(f, "\n");
    }

    // generate methods
    for (std::map<std::string,MetaClass::Method>::const_iterator iter= methods.begin(); 
         iter != methods.end(); ++iter)
    {
      generate_method_doc(f, iter->second);
      if (iter->second.abstract)
        fprintf(f, "  virtual %s %s(%s)= 0;\n",
                format_type_cpp(iter->second.ret_type, true).c_str(), iter->second.name.c_str(),
                format_arg_list(iter->second.arg_types).c_str());
      else
        fprintf(f, "  virtual %s %s(%s);\n",
                format_type_cpp(iter->second.ret_type, true).c_str(), iter->second.name.c_str(),
                format_arg_list(iter->second.arg_types).c_str());
    }
    
    if (needs_body || !gstruct->get_attribute("simple-impl-data").empty())
    {
      bool needs_init = true;
      if (gstruct->impl_data())
      {
        if (gstruct->get_attribute("simple-impl-data").empty())
        {
          fprintf(f, "\n  ImplData *get_data() const { return _data; }\n\n");
          fprintf(f, "  void set_data(ImplData *data);\n");
        }
        else
        {
          fprintf(f, "\n  ImplData *get_data() const { return _data; }\n\n");
          fprintf(f, "  void set_data(ImplData *data, void (*release)(ImplData*))\n");
          fprintf(f, "  {\n");
          fprintf(f, "    if (_data == data) return;\n");
          fprintf(f, "    if (_data && _release_data) _release_data(_data);\n");
          fprintf(f, "    _data= data;\n");
          fprintf(f, "    _release_data= release;\n");
          fprintf(f, "  }\n");
          needs_init = false;
        }
      }
      if (needs_init)
      {
        fprintf(f, "  // default initialization function. auto-called by Ref<Object> constructor\n");
        fprintf(f, "  virtual void init();\n\n");
      }
    }

    fprintf(f, "protected:\n");

    // special methods
    if (gstruct->watch_lists())
    {
      fprintf(f, "  virtual void owned_list_item_added(grt::internal::OwnedList *list, const grt::ValueRef &value);\n");
      fprintf(f, "  virtual void owned_list_item_removed(grt::internal::OwnedList *list, const grt::ValueRef &value);\n");
    }

    if (gstruct->watch_dicts())
    {
      fprintf(f, "  virtual void owned_dict_item_set(grt::internal::OwnedDict *dict, const std::string &key);\n");
      fprintf(f, "  virtual void owned_dict_item_removed(grt::internal::OwnedDict *dict, const std::string &key);\n");
    }

    // signals
    for (MetaClass::SignalList::const_iterator iter= gstruct->get_signals_partial().begin();
         iter != gstruct->get_signals_partial().end(); ++iter)
    {
      fprintf(f, "  boost::signals2::signal<void (%s) > _signal_%s;\n",
              format_signal_args(iter->arg_types).c_str(),
              iter->name.c_str());
    }
    
    fprintf(f, "\n");

    // generate member variables
    for (std::map<std::string,MetaClass::Member>::const_iterator iter= members.begin(); 
         iter != members.end(); ++iter)
    {
      if (!iter->second.calculated && !iter->second.overrides)
        fprintf(f, "  %s _%s;%s\n", format_type_cpp(iter->second.type).c_str(), iter->second.name.c_str(),
                iter->second.owned_object ? "// owned" : "");
    }

   
    fprintf(f, "private: // wrapper methods for use by grt\n");
    if ((needs_body && gstruct->impl_data()) || !gstruct->get_attribute("simple-impl-data").empty())
    {
      fprintf(f, "  ImplData *_data;\n");
      if (!gstruct->get_attribute("simple-impl-data").empty())
        fprintf(f, "  void (*_release_data)(ImplData *);\n");
      fprintf(f, "\n");
    }
    // function to create the object
    if (!gstruct->is_abstract())
    {
      fprintf(f, "  static grt::ObjectRef create(grt::GRT *grt)\n");
      fprintf(f, "  {\n");
      fprintf(f, "    return grt::ObjectRef(new %s(grt));\n", cname.c_str());
      fprintf(f, "  }\n");
      fprintf(f, "\n");
    }

    // generate method wrappers for grt
    for (std::map<std::string,MetaClass::Method>::const_iterator iter= methods.begin(); 
         iter != methods.end(); ++iter)
    {
      if (!iter->second.constructor)
      {
        if (iter->second.ret_type.base.type == UnknownType)
          fprintf(f, "  static grt::ValueRef call_%s(grt::internal::Object *self, const grt::BaseListRef &args)"
                "{ dynamic_cast<%s*>(self)->%s(%s); return grt::ValueRef(); }\n",
                iter->second.name.c_str(), cname.c_str(),
                iter->second.name.c_str(), format_wraparg_list(iter->second.arg_types).c_str());
        else
          fprintf(f, "  static grt::ValueRef call_%s(grt::internal::Object *self, const grt::BaseListRef &args)"
                "{ return dynamic_cast<%s*>(self)->%s(%s); }\n",
                iter->second.name.c_str(), cname.c_str(),
                iter->second.name.c_str(), format_wraparg_list(iter->second.arg_types).c_str());
        fprintf(f, "\n");
      }
    }
    fprintf(f, "\n");
    // class registration
    fprintf(f, "public:\n");
    fprintf(f, "  static void grt_register(grt::GRT *grt)\n");
    fprintf(f, "  {\n");
    fprintf(f, "    grt::MetaClass *meta= grt->get_metaclass(static_class_name());\n");
    fprintf(f, "    if (!meta) throw std::runtime_error(\"error initializing grt object class, metaclass not found\");\n");

    if (gstruct->is_abstract())
      fprintf(f, "    meta->bind_allocator(0);\n");
    else
      fprintf(f, "    meta->bind_allocator(&%s::create);\n", cname.c_str());
    
    for (std::map<std::string,MetaClass::Member>::const_iterator iter= members.begin(); 
         iter != members.end(); ++iter)
    {
      if (iter->second.calculated)
      {
        if (!iter->second.delegate_set)
          fprintf(f, "    meta->bind_member(\"%s\", new grt::MetaClass::Property<%s,%s >(&%s::%s));\n",
                iter->second.name.c_str(), 
                cname.c_str(), format_type_cpp(iter->second.type).c_str(), 
                cname.c_str(), iter->second.name.c_str());
        else
        {
          fprintf(f, "    {\n");
          fprintf(f, "      void (%s::*setter)(const %s &)= &%s::%s%s;\n", 
                cname.c_str(), format_type_cpp(iter->second.type).c_str(), 
                cname.c_str(), /*iter->second.read_only?"__":*/"", iter->second.name.c_str());
          fprintf(f, "      %s (%s::*getter)() const= &%s::%s;\n",
                format_type_cpp(iter->second.type).c_str(), cname.c_str(), cname.c_str(), iter->second.name.c_str());

          fprintf(f, "      meta->bind_member(\"%s\", new grt::MetaClass::Property<%s,%s >(getter, setter));\n",
                iter->second.name.c_str(), 
                cname.c_str(), format_type_cpp(iter->second.type).c_str());
          fprintf(f, "    }\n");
        }
      }
      else
      {
        fprintf(f, "    {\n");
        if (iter->second.overrides)
        {
          if (iter->second.delegate_set)
            fprintf(f, "      void (%s::*setter)(const %s &)= &%s::%s%s;\n", 
                    cname.c_str(), format_type_cpp(iter->second.type).c_str(), 
                    cname.c_str(), /*iter->second.read_only?"__":*/"", iter->second.name.c_str());
          else
            fprintf(f, "      void (%s::*setter)(const %s &)= 0;\n", cname.c_str(),
                    format_type_cpp(iter->second.type).c_str());
        }
        else // read-only members should still have a setter for unserialization
        {
          fprintf(f, "      void (%s::*setter)(const %s &)= &%s::%s%s;\n", 
                  cname.c_str(), format_type_cpp(iter->second.type).c_str(), 
                  cname.c_str(), /*iter->second.read_only?"__":*/"", iter->second.name.c_str());
        }

        if (iter->second.overrides)
        {
          if (iter->second.delegate_get)
            fprintf(f, "      %s (%s::*getter)() const= &%s::%s;\n",
                  format_type_cpp(iter->second.type).c_str(), cname.c_str(), cname.c_str(), iter->second.name.c_str());
          else
            fprintf(f, "      %s (%s::*getter)() const= 0;\n",
                  format_type_cpp(iter->second.type).c_str(), cname.c_str());
        }
        else
        {
          fprintf(f, "      %s (%s::*getter)() const= &%s::%s;\n",
                  format_type_cpp(iter->second.type).c_str(), cname.c_str(), cname.c_str(), iter->second.name.c_str());
        }

        fprintf(f, "      meta->bind_member(\"%s\", new grt::MetaClass::Property<%s,%s >(getter,setter));\n",
                iter->second.name.c_str(),
                cname.c_str(), format_type_cpp(iter->second.type).c_str());
        fprintf(f, "    }\n");
      }
    }  
    for (std::map<std::string,MetaClass::Method>::const_iterator iter= methods.begin(); 
         iter != methods.end(); ++iter)
    {    
      fprintf(f, "    meta->bind_method(\"%s\", &%s::call_%s);\n",
              iter->second.name.c_str(), cname.c_str(), iter->second.name.c_str());
    }
    fprintf(f, "  }\n");
    
    fprintf(f, "};\n");
    fprintf(f, "\n\n");
  }
  
  
  
  void generate_class_body(FILE *f)
  {    
    fprintf(f, "//================================================================================\n");
    fprintf(f, "// %s\n", cname.c_str());
    fprintf(f, "\n\n");
    
    if (gstruct->impl_data())
    {
      fprintf(f, "class %s::ImplData\n{\n", cname.c_str());
      fprintf(f, "};\n");
               
      fprintf(f, "\n\n");
      fprintf(f, "void %s::init()\n{\n  if (!_data) _data= new %s::ImplData();\n}\n\n",
            cname.c_str(), cname.c_str());
      fprintf(f, "%s::~%s()\n{\n  delete _data;\n}\n\n\n", cname.c_str(), cname.c_str());

      fprintf(f, "void %s::set_data(ImplData *data)\n{\n}\n\n", cname.c_str());
    }
    else
    {
      fprintf(f, "void %s::init()\n{\n\n}\n\n", cname.c_str());
      fprintf(f, "%s::~%s()\n{\n  \n}\n\n\n", cname.c_str(), cname.c_str());
    }      

    // generate constructors
    for (std::map<std::string,MetaClass::Method>::const_iterator iter= methods.begin(); 
         iter != methods.end(); ++iter)
    {
      if (iter->second.constructor)
      {
        fprintf(f, "%s::%s(grt::GRT *grt%s%s, grt::MetaClass *meta)\n",
                cname.c_str(), cname.c_str(),
                iter->second.arg_types.empty()?"":", ", format_arg_list(iter->second.arg_types).c_str());
        output_constructor_init_list(f);
      }
    }

    // generate member variable accessors
    for (std::map<std::string,MetaClass::Member>::const_iterator iter= members.begin(); 
         iter != members.end(); ++iter)
    {
      if (iter->second.private_)
        continue;

      if (iter->second.delegate_get)
      {
        fprintf(f, "%s %s::%s() const\n{\n // add code here\n}\n", 
                format_type_cpp(iter->second.type).c_str(), 
                cname.c_str(), iter->second.name.c_str());
      }

      if (!iter->second.read_only && iter->second.delegate_set)
      {
        fprintf(f, "void %s::%s(const %s &value)\n{\n",
                cname.c_str(), iter->second.name.c_str(), format_type_cpp(iter->second.type).c_str());
        fprintf(f, "  grt::ValueRef ovalue(_%s);\n", iter->second.name.c_str());
        if (iter->second.owned_object)
        {
          fprintf(f, "  // this member is owned by this object\n");
          //fprintf(f, "  if (_is_global && _%s.is_valid()) _%s.unmark_global();\n",
          //        iter->second.name.c_str(), iter->second.name.c_str());

          fprintf(f, "// add code here\n");
          fprintf(f, "  _%s= value;\n", iter->second.name.c_str());

          //fprintf(f, "  if (_is_global && _%s.is_valid()) _%s.mark_global();\n",
          //        iter->second.name.c_str(), iter->second.name.c_str());
          fprintf(f, "  owned_member_changed(\"%s\", ovalue, value);\n", iter->second.name.c_str());
        }
        else
        {
          fprintf(f, "// add code here\n");
          fprintf(f, "  _%s= value;\n", iter->second.name.c_str());
          fprintf(f, "  member_changed(\"%s\", ovalue, value);\n", iter->second.name.c_str());
        }

        //fprintf(f, "  _changed_signal.emit(\"%s\", ovalue);\n", iter->second.name.c_str());

        fprintf(f, "}\n\n");
      }
    }

    if (gstruct->watch_lists())
    {
      fprintf(f, "void %s::owned_list_item_added(grt::internal::OwnedList *list, const grt::ValueRef &value)\n",
              cname.c_str());
      fprintf(f, "{\n}\n\n");
      fprintf(f, "void %s::owned_list_item_removed(grt::internal::OwnedList *list, const grt::ValueRef &value)\n",
              cname.c_str());
      fprintf(f, "{\n}\n\n");
    }

    if (gstruct->watch_dicts())
    {
      fprintf(f, "void %s::owned_dict_item_set(grt::internal::OwnedDict *dict, const std::string &key)\n",
              cname.c_str());
      fprintf(f, "{\n}\n\n");
      fprintf(f, "void %s::owned_dict_item_removed(grt::internal::OwnedDict *dict, const std::string &key)\n",
              cname.c_str());
      fprintf(f, "{\n}\n\n");
    }

    // generate methods
    for (std::map<std::string,MetaClass::Method>::const_iterator iter= methods.begin(); 
         iter != methods.end(); ++iter)
    {
      if (!iter->second.abstract && ! iter->second.constructor)
        fprintf(f, "%s %s::%s(%s)\n{\n  // add code here\n}\n\n\n",
                format_type_cpp(iter->second.ret_type, true).c_str(), 
                cname.c_str(), iter->second.name.c_str(),
                format_arg_list(iter->second.arg_types).c_str());
    }
    fprintf(f, "\n\n\n");
  }
};


static std::string basename(std::string s)
{
  if (s.find('/')!=std::string::npos)
    s= s.substr(s.rfind('/')+1);

  if (s.find('\\')!=std::string::npos)
    s= s.substr(s.rfind('\\')+1);

  return s;
}

static std::string pkgname(std::string s)
{
  std::string source(basename(s));

  if (source.find('.') != std::string::npos)
    return source.substr(0, source.rfind('.'));
  return source;
}

static std::string generate_dll_export_name(const std::string &fname)
{
   std::string name= basename(fname);

   name= cppize_class_name(name.substr(0, name.rfind('.')));
   for (size_t i= 0; i < name.size(); i++)
   {
  name[i]= g_ascii_toupper(name[i]);
   }

   return "GRT_"+name;
}

static std::string used_class_name(const TypeSpec &type)
{
  if (type.base.type == ObjectType)
    return type.base.object_class;
  else if (type.content.type == ObjectType)
    return type.content.object_class;
  return std::string();
}

static bool is_header_included_somehow(const std::string &xml_for_header, const std::string &in_xml_for_header,
                                       const std::multimap<std::string,std::string> &requires)
{

    if (xml_for_header == in_xml_for_header) return true;

    for (std::multimap<std::string,std::string>::const_iterator r= requires.find(in_xml_for_header);
          r != requires.end() && r->first == in_xml_for_header; ++r)
    {
        if (basename(r->second) == xml_for_header || is_header_included_somehow(xml_for_header, basename(r->second), requires))
            return true;
    }
    return false;
}

void grt::helper::generate_struct_code(GRT *grt, const std::string &target_file,
                                       const std::string &outpath, const std::string &imploutpath,
                                       const std::multimap<std::string,std::string> &requires_orig)
{
  std::map<std::string, FILE*> files;
  std::map<std::string, std::set<std::string> > foreign_classes; // packagename -> class list
  std::multimap<std::string,std::string> requires;
  const std::list<MetaClass*>& meta(grt->get_metaclasses());

  // requires list is keyed by the full path of the xml, so we add the same entries to a copy of the list with basenames
  for (std::multimap<std::string,std::string>::const_iterator r= requires_orig.begin(); r!=requires_orig.end(); ++r)
      requires.insert(std::make_pair(basename(r->first), r->second));

  {
      std::map<std::string, std::string> package_for_struct; // structname -> packagename
      // fill mapping from struct name to package, to know what structs are declared in the header itself
      // and what comes from other headers. Later, we will forward declare externally defined class names
      // during output
      for (std::list<MetaClass*>::const_iterator iter= meta.begin();
           iter != meta.end(); ++iter)
           package_for_struct[(*iter)->name()] = basename((*iter)->source());

      // fill a list of foreign classes per file
      for (std::list<MetaClass*>::const_iterator iter= meta.begin();
           iter != meta.end(); ++iter)
      {
          // forward declare classes that are mentioned and not in #included files
          for (MetaClass::MemberList::const_iterator mem= (*iter)->get_members_partial().begin();
        mem != (*iter)->get_members_partial().end(); ++mem)
          {
              std::string class_name= used_class_name(mem->second.type);
              if (!class_name.empty() && pkgname(package_for_struct[class_name]) != pkgname((*iter)->source())
                  && !is_header_included_somehow(package_for_struct[class_name], basename((*iter)->source()), requires))
                  foreign_classes[(*iter)->source()].insert(class_name);
          }
          for (MetaClass::MethodList::const_iterator met= (*iter)->get_methods_partial().begin();
               met != (*iter)->get_methods_partial().end(); ++met)
          {
              std::string class_name= used_class_name(met->second.ret_type);
              if (!class_name.empty() && pkgname(package_for_struct[class_name]) != pkgname((*iter)->source())
                  && !is_header_included_somehow(package_for_struct[class_name], basename((*iter)->source()), requires))
                  foreign_classes[(*iter)->source()].insert(class_name);
              for (ArgSpecList::const_iterator arg= met->second.arg_types.begin(); arg != met->second.arg_types.end(); ++arg)
              {
                  class_name= used_class_name(arg->type);
                  if (!class_name.empty() && pkgname(package_for_struct[class_name]) != pkgname((*iter)->source())
                      && !is_header_included_somehow(package_for_struct[class_name], basename((*iter)->source()), requires))
                      foreign_classes[(*iter)->source()].insert(class_name);
              }
          }
      }
  }

  // perform dump
  for (std::list<MetaClass*>::const_iterator iter= meta.begin();
       iter != meta.end(); ++iter)
  {
    FILE *fhdr;
    std::string header_file;
    std::string body_file;
    
    if (!target_file.empty() && basename(target_file) != basename((*iter)->source()))
      continue;

    std::string package_name= (*iter)->source().substr(0, (*iter)->source().rfind('.'));
    package_name= basename(package_name);

    if (package_name.size() > strlen("structs."))
      package_name= package_name.substr(strlen("structs."));
    else
      package_name= "";

    if (files.find((*iter)->source()) == files.end())
    {
      std::string file= (*iter)->source();
      char *outfile= g_path_get_basename(file.c_str());
      char *path;
      char *tmp;

      tmp= strstr(outfile, ".xml");
      if (!tmp)
      {
        g_free(outfile);
        continue;
      }
      strcpy(tmp, ".h");
      header_file= outfile;

      path= g_build_path("/", outpath.c_str(), outfile, NULL);

      fhdr= base_fopen(path, "w+");
      if (!fhdr)
        throw grt::os_error(path, errno);
      g_print("create file %s\n", path);

      fprintf(fhdr, "#ifndef __grts_%s__\n", cppize_class_name(outfile).c_str());
      fprintf(fhdr, "#define __grts_%s__\n", cppize_class_name(outfile).c_str());
      fprintf(fhdr, "\n#include <grtpp.h>\n\n");

      fprintf(fhdr, "#ifdef _WIN32\n");
      fprintf(fhdr, "  #pragma warning(disable: 4355) // 'this' : used in base member initizalizer list\n");
      fprintf(fhdr, "  #ifdef %s_EXPORT\n", generate_dll_export_name(outfile).c_str());
      fprintf(fhdr, "  #define %s_PUBLIC __declspec(dllexport)\n", generate_dll_export_name(outfile).c_str());
      fprintf(fhdr, "#else\n");
      fprintf(fhdr, "  #define %s_PUBLIC __declspec(dllimport)\n", generate_dll_export_name(outfile).c_str());
      fprintf(fhdr, "#endif\n");
      fprintf(fhdr, "#else\n");
      fprintf(fhdr, "  #define %s_PUBLIC\n", generate_dll_export_name(outfile).c_str());
      fprintf(fhdr, "#endif\n\n");

      // include external files
      for (std::multimap<std::string,std::string>::const_iterator r= requires_orig.find(file);
           r != requires_orig.end() && r->first == file; ++r)
      {
        std::string tmp(r->second, 0, r->second.rfind('.'));
        fprintf(fhdr, "#include <grts/%s.h>\n", tmp.c_str());
      }
      fprintf(fhdr, "\n\n");

      // forward declare all classes
      for (std::list<MetaClass*>::const_iterator jter= meta.begin();
           jter != meta.end(); ++jter)
      {
        if ((*jter)->source() == (*iter)->source())
        {
          std::string cname= cppize_class_name((*jter)->name());
          
          fprintf(fhdr, "class %s;\n", 
                  cname.c_str());

          fprintf(fhdr, "typedef grt::Ref<%s> %sRef;\n", 
                  cname.c_str(), cname.c_str());
        }
      }
      fprintf(fhdr, "\n\n");
      
      if (foreign_classes.find((*iter)->source()) != foreign_classes.end())
      {
         std::set<std::string> classes(foreign_classes[(*iter)->source()]);
         for (std::set<std::string>::const_iterator i= classes.begin(); i != classes.end(); ++i)
         {
             g_message("Class %s is not included in %s, forward declaring explicitly", i->c_str(), (*iter)->source().c_str());
             fprintf(fhdr, "class %s;\n", cppize_class_name(*i).c_str());
         }
         fprintf(fhdr, "\n\n");
      }
      
      // forward declare simple-impl-data classes
      for (std::list<MetaClass*>::const_iterator jter= meta.begin();
           jter != meta.end(); ++jter)
      {
        std::string klass = (*jter)->get_attribute("simple-impl-data");
        if (!klass.empty())
        {
          std::vector<std::string> parts(base::split(klass, "::"));

          for (size_t i = 0; i < parts.size()-1; i++)
          {
            fprintf(fhdr, "namespace %s { ", parts[i].c_str());
          }
          fprintf(fhdr, "\n\tclass %s;\n", parts.back().c_str());
          for (size_t i = 0; i < parts.size()-1; i++)
          {
            fprintf(fhdr, "}; ");
          }
          fprintf(fhdr, "\n\n");
        }
      }        
      
      g_free(path);

      files[(*iter)->source()]= fhdr;
    }
    else
    {
      fhdr= files[(*iter)->source()];
      
      header_file= (*iter)->source().substr(0, (*iter)->source().find(".xml"));
      header_file= header_file.substr((*iter)->source().rfind('/')+1);
    }

    body_file= imploutpath;
    body_file.append("/");
    
    ClassImplGenerator gen(*iter, fhdr);
    
    gen.generate_class_header(generate_dll_export_name((*iter)->source())+"_PUBLIC");
    if (gen.needs_body)
    {
      // create the output dir if it doesn't exist
      if (!g_file_test(imploutpath.c_str(), G_FILE_TEST_IS_DIR))
        g_mkdir_with_parents(imploutpath.c_str(), 0755);

      body_file.append(gen.cname).append(".cpp.new");
      g_message("CREATE %s", body_file.c_str());
      FILE *f= base_fopen(body_file.c_str(), "w+");

      fprintf(f, "\n#include <grts/%s.h>\n", header_file.c_str());
      fprintf(f, "\n#include <grtpp_util.h>\n");

      fprintf(f, "\n\n");

      gen.generate_class_body(f);

      fclose(f);
    }
  }

  for (std::map<std::string,FILE*>::const_iterator iter= files.begin();
       iter != files.end(); ++iter)
  {
    if (iter->second)
    {
      std::string name= cppize_class_name(basename(iter->first));
      const char *sname= name.c_str();
      
      // output code to register all classes in this file
      fprintf(iter->second, "\n\ninline void register_%s()\n", sname);
      fprintf(iter->second, "{\n");
      for (std::list<MetaClass*>::const_iterator jter= meta.begin();
           jter != meta.end(); ++jter)
      {
        if ((*jter)->source() == iter->first)
        {
          std::string cname= cppize_class_name((*jter)->name());

          fprintf(iter->second, "  grt::internal::ClassRegistry::register_class<%s>();\n",
                  cname.c_str());
        }
      }
      fprintf(iter->second, "}\n");
      fprintf(iter->second, "\n");
      fprintf(iter->second, "#ifdef AUTO_REGISTER_GRT_CLASSES\n");
      fprintf(iter->second, "static struct _autoreg__%s { _autoreg__%s() { register_%s(); } } __autoreg__%s;\n",
              sname, sname, sname, sname);
      fprintf(iter->second, "#endif\n\n");

      fprintf(iter->second, "#endif\n");

      fclose(iter->second);
    }
  }
}






//--------------------------------------------------------------------------------
// Module Wrapper Generator



static const char *module_wrapper_head=
  "// Automatically generated GRT module wrapper. Do not edit.\n\n"
  "using namespace grt;\n\n";


static const char *module_base_template_h=
  "class %module_class_name% : public %parent_module_class_name% {\n"
  "protected:\n"
  "  friend class grt::GRT;\n"
  "  %module_class_name%(grt::Module *module)\n"
  "  : %parent_module_class_name%(module) {}\n"
  "\n"
  "public:\n"
  "  static const char *static_get_name() { return \"%module_name%\"; }\n";


static const char *module_base_template_f=
  "};\n";


static const char *module_function_template_void=
  "  void %function_name%(%args%)\n"
  "  {\n"
  "    grt::BaseListRef args(get_grt());\n"
  "%make_args%\n"
  "    _module->call_function(\"%function_name%\", args);\n"
  "  }\n";


static const char *module_function_template_int=
  "  int %function_name%(%args%)\n"
  "  {\n"
  "    grt::BaseListRef args(get_grt(), AnyType);\n"
  "%make_args%\n"
  "    grt::ValueRef ret= _module->call_function(\"%function_name%\", args);\n"
/*
  "    if (ret.is_valid())\n"
  "       throw module_error(static_get_name(), \"%function_name%\", \"Attempt to call invalid module function\");\n"
  "    if (!IntegerRef::can_take(ret))\n"
  "       throw module_error exc(static_get_name(), \"%function_name%\", \"Module function returned unexpected result: \"+grt::ValueRef(ret).repr());\n"
*/
  "    return *grt::IntegerRef::cast_from(ret);\n"
  "  }\n";

static const char *module_function_template_double=
  "  double %function_name%(%args%)\n"
  "  {\n"
  "    grt::BaseListRef args(get_grt(), AnyType);\n"
  "%make_args%\n"
  "    grt::ValueRef ret= _module->call_function(\"%function_name%\", args);\n"
/*
  "    if (ret.is_valid())\n"
  "       throw module_error(static_get_name(), \"%function_name%\", \"Attempt to call invalid module function\");\n"
  "    if (!DoubleRef::can_take(ret))\n"
  "       throw module_error exc(static_get_name(), \"%function_name%\", \"Module function returned unexpected result: \"+grt::ValueRef(ret).repr());\n"
*/
  "    return (double)DoubleRef::cast_from(ret);\n"
  "  }\n";

static const char *module_function_template_string=
  "  std::string %function_name%(%args%)\n"
  "  {\n"
  "    grt::BaseListRef args(get_grt(), AnyType);\n"
  "%make_args%\n"
  "    grt::ValueRef ret= _module->call_function(\"%function_name%\", args);\n"
/*
  "    if (ret.is_valid())\n"
  "       throw module_error(static_get_name(), \"%function_name%\", \"Attempt to call invalid module function\");\n"
  "    if (!StringRef::can_take(ret))\n"
  "       throw module_error exc(static_get_name(), \"%function_name%\", \"Module function returned unexpected result: \"+grt::ValueRef(ret).repr());\n"
*/
  "    return (std::string)StringRef::cast_from(ret);\n"
  "  }\n";


static const char *module_function_template=
  "  %return_type% %function_name%(%args%)\n"
  "  {\n"
  "    grt::BaseListRef args(get_grt(), AnyType);\n"
  "%make_args%\n"
  "    grt::ValueRef ret= _module->call_function(\"%function_name%\", args);\n"
  /*
  "    if (ret.is_valid())\n"
  "       throw module_error(static_get_name(), \"%function_name%\", \"Attempt to call invalid module function\");\n"
  "    if (!%return_type%::can_take(ret))\n"
  "       throw module_error exc(static_get_name(), \"%function_name%\", \"Module function returned unexpected result: \"+grt::ValueRef(ret).repr());\n"
   */
  "    return %return_type%::cast_from(ret);\n"
  "  }\n";



static void export_module_function(FILE *f, const Module::Function &function)
{
  unsigned int i;
  const char *func_template= module_function_template;
  std::string return_type;
  std::string args;
  std::string make_args;

  return_type= format_type_cpp(function.ret_type);
  
  switch (function.ret_type.base.type)
  {
  case IntegerType: func_template= module_function_template_int; break;
  case DoubleType: func_template= module_function_template_double; break;
  case StringType: func_template= module_function_template_string; break;
  case ListType:
  case DictType:
  case ObjectType: func_template= module_function_template; break;
  default: func_template= module_function_template_void; break;
  }

  i= 0;
  const ArgSpecList::const_iterator last = function.arg_types.end();
  for (ArgSpecList::const_iterator param= function.arg_types.begin();
       param != last; ++param)
  {
    const char *proto_arg_type= NULL;
    const std::string arg_type= format_type_cpp(param->type);
    std::string arg_name;
    
    switch (param->type.base.type)
    {
    case IntegerType: proto_arg_type= "int"; break;
    case DoubleType: proto_arg_type= "double"; break;
    case StringType: proto_arg_type= "const std::string &"; break;
    case ListType:
    case DictType:
    case ObjectType:
      break;
    default: g_warning("invalid parameter type found in module function %s",
                       function.name.c_str());
    }
    
    if (param->name.empty())
    {
      char buf[40];
      sprintf(buf, "param%i", i++);
      arg_name= buf;
    }
    else
      arg_name= param->name;

    if (!args.empty())
      args.append(", ");
    if (!make_args.empty())
      make_args.append("\n");
    
    if (proto_arg_type)
    {
      args.append(proto_arg_type).append(" ").append(arg_name);
      make_args.append("    args.ginsert(").append(arg_type).append("(").append(arg_name).append("));");
    }
    else
    {
      args.append("const ").append(arg_type).append("& ").append(arg_name);
      make_args.append("    args.ginsert(").append(arg_name).append(");");;
    }
  }

  {
    char *code;
    code= str_g_subst(func_template, "%return_type%", return_type.c_str());
    code= str_g_replace(code, "%function_name%", function.name.c_str());
    code= str_g_replace(code, "%args%", args.c_str());
    code= str_g_replace(code, "%make_args%", make_args.c_str());

    fprintf(f, "%s", code);

    g_free(code);
  }
}


void grt::helper::generate_module_wrappers(GRT *grt, const std::string &outpath,
                                           const std::vector<Module*> &modules)
{
  char *path= g_path_get_basename(outpath.c_str());
  FILE *f= base_fopen(outpath.c_str(), "w+");
  char *header_name;

  if (!f)
    throw grt::os_error(errno);

  header_name= str_g_subst(path, ".", "_");

  fprintf(f, "#ifndef __%s_\n", header_name);
  fprintf(f, "#define __%s_\n", header_name);
  g_free(header_name);
  fprintf(f, "%s", module_wrapper_head);
  
  for (std::vector<Module*>::const_iterator module= modules.begin();
       module != modules.end(); ++module)
  {
    char *code;
    char *tmp;

    code= str_g_subst(module_base_template_h, "%module_name%", (*module)->name().c_str());

    tmp= g_strdup_printf("%sWrapper", (*module)->name().c_str());
    code= str_g_replace(code, "%module_class_name%", tmp);
    g_free(tmp);

    if (!(*module)->extends().empty())
    {
      tmp= g_strdup_printf("%sWrapper", (*module)->extends().c_str());
      code= str_g_replace(code, "%parent_module_class_name%", tmp);
      g_free(tmp);
    }
    else
      code= str_g_replace(code, "%parent_module_class_name%", "grt::ModuleWrapper");

    fprintf(f, "%s", code);


    for (std::vector<Module::Function>::const_iterator func= (*module)->get_functions().begin();
         func != (*module)->get_functions().end(); ++func)
    {
      export_module_function(f, *func);
    }

    fprintf(f, "%s", module_base_template_f);
  }

  fprintf(f, "#endif\n");
}

