# 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 Pen < Director

      def setup
        super
        spec.items << 'wxPenInfo'
        spec.gc_as_untracked 'wxPenInfo'
        spec.gc_as_untracked 'wxPen'
        spec.disable_proxies
        spec.add_header_code <<~__HEREDOC
          // special free funcs are needed to clean up Dashes array if it has been
          // set; wxWidgets does not do this automatically so will leak if not
          // dealt with.
          void GC_free_wxPen(wxPen *pen) 
          {
            SWIG_RubyRemoveTracking(pen);
            if (pen && pen->IsOk ())
            {
              wxDash *dashes;
              int dash_count = pen->GetDashes(&dashes);
              if ( dash_count )
                delete dashes;
            }
            delete pen;
          }
          void GC_free_wxPenInfo(wxPenInfo *pen_info) 
          {
            SWIG_RubyRemoveTracking(pen_info);
            if (pen_info)
            {
              wxDash *dashes;
              int dash_count = pen_info->GetDashes(&dashes);
              if ( dash_count )
                delete dashes;
            }
            delete pen_info;
          }
          __HEREDOC
        spec.add_swig_code '%feature("freefunc") wxPen "GC_free_wxPen";'
        spec.add_swig_code '%feature("freefunc") wxPenInfo "GC_free_wxPenInfo";'
        # all but the default ctor require a running App
        spec.require_app 'wxPen::wxPen(const wxPenInfo &info)',
                         'wxPen::wxPen(const wxColour &colour, int width, wxPenStyle style)',
                         'wxPen::wxPen(const wxBitmap &stipple, int width)',
                         'wxPen::wxPen(const wxPen &pen)'
        # ignore because of memory management issue
        spec.ignore 'wxPen::wxPen(const wxPenInfo &)'
        # add our own
        spec.add_extend_code 'wxPen', <<~__HEREDOC
          wxPen(const wxPenInfo &info)
          {
            if (!wxRuby_IsAppRunning()) 
              rb_raise(rb_eRuntimeError, "Contract violation: require: (wxRuby_IsAppRunning())");

            wxPen* new_pen = new wxPen(info.GetColour(), info.GetWidth(), info.GetStyle());
            new_pen->SetJoin(info.GetJoin());
            new_pen->SetCap(info.GetCap());
            // copy the dashes
            wxDash* new_dashes = new wxDash[info.GetDashCount()];
            for (int i = 0; i < info.GetDashCount(); i++)
            {
              new_dashes[i] = info.GetDash()[i];
            }
            new_pen->SetDashes(info.GetDashCount(), new_dashes);
            return new_pen;
          }
          __HEREDOC
        # dealt with below - these require special handling because of the use
        # of wxDash array, which cannot be freed until the pen(info) is disposed of
        # or until a new dash pattern is specified.
        spec.ignore(%w[wxPen::GetDashes wxPen::SetDashes wxPenInfo::GetDashes wxPenInfo::Dashes])
        spec.ignore 'wxPenInfo::GetDash'
        spec.add_extend_code 'wxPen', <<~__HEREDOC
          // Returns a ruby array with the dash lengths
          VALUE get_dashes() 
          {
            VALUE rb_dashes = rb_ary_new();
            wxDash* dashes;
            int dash_count = $self->GetDashes(&dashes);
            for ( int i = 0; i < dash_count; i++ )
            {
              rb_ary_push(rb_dashes, INT2NUM(dashes[i]));
            }
            return rb_dashes;
          }
        
          // Sets the dashes to have the lengths defined in the ruby array of ints
          void set_dashes(VALUE rb_dashes) 
          {
            // Check right parameter type
            if ( TYPE(rb_dashes) != T_ARRAY )
              rb_raise(rb_eTypeError, 
                       "Wrong argument type for set_dashes, should be Array");
        
            // Get old value in case it needs to be deallocated to avoid leaking
            wxDash* old_dashes;
            int old_dashes_count = $self->GetDashes(&old_dashes);
        
            // Create a C++ wxDash array to hold the new dashes, and populate
            int new_dash_count = RARRAY_LEN(rb_dashes);
            wxDash* new_dashes = new wxDash[ new_dash_count ];
            for ( int i = 0; i < new_dash_count; i++ )
            {
              new_dashes[i] = NUM2INT(rb_ary_entry(rb_dashes, i));
            }
            $self->SetDashes(new_dash_count, new_dashes);
        
            // Clean up the old if it existed
            if ( old_dashes_count )
              delete old_dashes;
          }
          __HEREDOC
        spec.add_extend_code 'wxPenInfo', <<~__HEREDOC
          // Returns a ruby array with the dash lengths
          VALUE get_dashes() 
          {
            VALUE rb_dashes = rb_ary_new();
            wxDash* dashes;
            int dash_count = $self->GetDashes(&dashes);
            for ( int i = 0; i < dash_count; i++ )
            {
              rb_ary_push(rb_dashes, INT2NUM(dashes[i]));
            }
            return rb_dashes;
          }
        
          // Sets the dashes to have the lengths defined in the ruby array of ints
          void dashes(VALUE rb_dashes) 
          {
            // Check right parameter type
            if ( TYPE(rb_dashes) != T_ARRAY )
              rb_raise(rb_eTypeError, 
                       "Wrong argument type for set_dashes, should be Array");
        
            // Get old value in case it needs to be deallocated to avoid leaking
            wxDash* old_dashes;
            int old_dashes_count = $self->GetDashes(&old_dashes);
        
            // Create a C++ wxDash array to hold the new dashes, and populate
            int new_dash_count = RARRAY_LEN(rb_dashes);
            wxDash* new_dashes = new wxDash[ new_dash_count ];
            for ( int i = 0; i < new_dash_count; i++ )
            {
              new_dashes[i] = NUM2INT(rb_ary_entry(rb_dashes, i));
            }
            $self->Dashes(new_dash_count, new_dashes);
        
            // Clean up the old if it existed
            if ( old_dashes_count )
              delete old_dashes;
          }
        __HEREDOC
        spec.map 'wxPenInfo &' => 'Wx::PenInfo' do
          map_out code: '$result = self; wxUnusedVar($1);'
        end
        # for SetColour
        spec.map_apply 'const wxColour&' => ['wxColour&']
        # these are defined and loaded in RubyStockObjects.i
        spec.ignore %w[
          wxRED_PEN wxBLUE_PEN wxCYAN_PEN wxGREEN_PEN wxYELLOW_PEN wxBLACK_PEN wxWHITE_PEN
          wxTRANSPARENT_PEN wxBLACK_DASHED_PEN wxGREY_PEN wxMEDIUM_GREY_PEN wxLIGHT_GREY_PEN]
        # do not expose this
        spec.ignore 'wxThePenList'
        # provide it's functionality as a class method of Pen instead
        spec.add_extend_code 'wxPen', <<~__HEREDOC
          static wxPen* find_or_create_pen(const wxColour &colour, int width=1, wxPenStyle style=wxPENSTYLE_SOLID)
          {
            return wxThePenList->FindOrCreatePen(colour, width, style);
          }
          __HEREDOC
      end
    end # class Pen

  end # class Director

end # module WXRuby3
