# Copyright (c) 2023 M.J.N. Corino, The Netherlands
#
# This software is released under the MIT license.

###
# wxRuby3 wxWidgets interface director
###

module WXRuby3

  class Director

    class XmlResource < Director

      def setup
        super
        # use XRC handlers as is in Ruby
        spec.ignore %w[wxXmlResource::AddHandler wxXmlResource::ClearHandlers wxXmlResource::InsertHandler]
        # not really added value; will add alternative allowing more flexibility (see below)
        spec.ignore 'wxXmlResource::LoadDocument(wxXmlDocument *, const wxString &)'
        # For these three methods, there are two variants in wxWidgets. One loads an
        # XRC from scratch and returns an instance of a core Wx class
        # (Wx::Dialog, Wx::Frame, or Wx::Panel). The other loads a layout into
        # an already existing instance of one of these classes, which may be a
        # custom ruby subclass containing extended methods etc. These two types
        # are disambiguated below.
        spec.rename_for_ruby 'LoadDialogSubclass' => 'wxXmlResource::LoadDialog(wxDialog *, wxWindow *, const wxString &)',
                             'LoadPanelSubclass' => 'wxXmlResource::LoadPanel(wxPanel *, wxWindow *, const wxString &)',
                             'LoadFrameSubclass'=> 'wxXmlResource::LoadFrame(wxFrame *, wxWindow *, const wxString &)'
        # XmlResource is an exception to the general rule in typemap.i - it has
        # methods which accept a 'wxWindow* parent' argument which may be
        # null. The common typemap checks if the parent is NULL, but Dialogs
        # and Frames may have NULL (Ruby:nil) parents as created by XmlResource
        # LoadDialog and LoadFrame methods. So we disable that part of the
        # checking for all methods (including those which would ideally retain
        # it - eg LoadPanel - b/c no way to apply a typemap based on method
        # name in SWIG, but we will fix that with some pure Ruby overrides)
        spec.map 'wxWindow* parent' => 'Wx::Window' do
          map_check code: <<~__CODE
            if ( !wxRuby_IsAppRunning() )
            { 
              rb_raise(rb_eRuntimeError,
                       "Cannot create a Window before App.main_loop has been called");
            }
          __CODE
        end
        # ignore this but not it's docs; will add special wrapper implementation (see below)
        spec.ignore 'wxXmlResource::AddSubclassFactory', ignore_doc: false
        spec.add_header_code 'static VALUE WxRuby_XmlSubclassFactory_klass;'
        spec.add_extend_code 'wxXmlResource', <<~__HEREDOC
          static void AddSubclassFactory(VALUE factory)
          {
            void *pfactory = 0;
            if (TYPE(factory) != T_DATA || !rb_obj_is_kind_of(factory, WxRuby_XmlSubclassFactory_klass))
            {
              rb_raise(rb_eArgError, "expected Wx::XmlSubclassFactory for argument 1"); return;
            }
            RDATA(factory)->dfree = 0; // wxWidgets will take over management 
            Data_Get_Struct(factory, void, pfactory);
            wxXmlResource::AddSubclassFactory(reinterpret_cast< wxXmlSubclassFactory * >(pfactory));
          }
          __HEREDOC
        spec.add_wrapper_code <<~__HEREDOC
          // Custom director-like class for XmlSubclassFactory
          class WxRuby_XmlSubclassFactory : public wxXmlSubclassFactory
          {
          public:
            WxRuby_XmlSubclassFactory(VALUE self) : wxXmlSubclassFactory(), self_(self) {}
            virtual wxObject *Create(wxString const &className);
            virtual ~WxRuby_XmlSubclassFactory();

          private:
            VALUE self_;
          };

          wxObject *WxRuby_XmlSubclassFactory::Create(wxString const &className)
          {
            static WxRuby_ID create_id("create");

            wxObject *c_result ;
            VALUE rb_classname = Qnil ;
            VALUE SWIGUNUSED result;
            void *result_ptr;
            
            if (rb_respond_to(this->self_, create_id()))
            {
              rb_classname = WXSTR_TO_RSTR(className);
              result = rb_funcall(this->self_, create_id(), 1, rb_classname);
              if (result != Qnil)
              {
                if (TYPE(result) != T_DATA || !rb_obj_is_kind_of(result, ((swig_class *) (SWIGTYPE_p_wxObject->clientdata))->klass))
                {
                  Swig::DirectorTypeMismatchException::raise(rb_eTypeError, "in output value of type '""wxObject *""'");
                }
                Data_Get_Struct(result, void, result_ptr);
                RDATA(result)->dfree = 0; // disown
                // the returned wxObject's should be Window objects which are always managed by wxWidgets after creation
                // so no need to own anything anymore
                c_result = reinterpret_cast< wxObject * >(result_ptr);
                return c_result;
              }
            }
            return 0;
          }  

          WxRuby_XmlSubclassFactory::~WxRuby_XmlSubclassFactory()
          {
            DATA_PTR(this->self_) = 0; // unlink
          }

          SWIGINTERN void
          WxRuby_XmlSubclassFactory_free(void *self) 
          {
            wxXmlSubclassFactory *arg1 = (wxXmlSubclassFactory *)self;
            delete arg1;
          }

          SWIGINTERN VALUE
          #ifdef HAVE_RB_DEFINE_ALLOC_FUNC
          WxRuby_XmlSubclassFactory_allocate(VALUE self)
          #else
          WxRuby_XmlSubclassFactory_allocate(int argc, VALUE *argv, VALUE self)
          #endif
          {
            VALUE obj;
            obj = Data_Wrap_Struct(self, 0, WxRuby_XmlSubclassFactory_free, 0);
            WxRuby_XmlSubclassFactory *pfactory = new WxRuby_XmlSubclassFactory(obj); 
            DATA_PTR(obj) = pfactory;
          #ifndef HAVE_RB_DEFINE_ALLOC_FUNC
            rb_obj_call_init(obj, argc, argv);
          #endif
            return obj;
          }
          __HEREDOC
        spec.add_init_code <<~__HEREDOC
          WxRuby_XmlSubclassFactory_klass = rb_define_class_under(mWxXmlResource, "XmlSubclassFactory", rb_cObject);
          rb_define_alloc_func(WxRuby_XmlSubclassFactory_klass, WxRuby_XmlSubclassFactory_allocate);
          __HEREDOC
      end
    end # class XmlResource

  end # class Director

end # module WXRuby3
