# Copyright (c) 2023 M.J.N. Corino, The Netherlands
#
# This software is released under the MIT license.
# 
# Some parts are
# Copyright 2004-2007, wxRuby development team
# released under the MIT-like wxRuby2 license

###

require 'wx'

module ImagePrt

  class ImagePage < Wx::ScrolledWindow

    PAGES = [
      'Lines',
      'Polygons',
      'Text',
      'Arcs',
      'Checkmarks',
      'Scaled Text',
      'Bitmaps',
      'Clipping',
      'Text Position'
    ]
    PAGES.each_with_index do |title, index|
      self.const_set("PAGE_#{title.tr(' ', '')}", index)
    end
    PG_DESC = [
      'Green Cross, Cyan Line and spline',
      'Blue rectangle, red edge, clear rounded rectangle, gold ellipse, gold and clear stars',
      'Swiss, Times text; red text, rotated and colored orange',
      'This is an arc test page',
      'Two check marks',
      'Scaling test page',
      'Icon and Bitmap ',
      'Clipping region',
      'Text position test page'
    ]

    def initialize(parent, index)
      super(parent, style: Wx::VSCROLL|Wx::HSCROLL)
      set_background_colour(Wx::WHITE)
      set_scrollbars(20, 20, 50, 50)
      @index = index
      evt_paint :on_paint
    end

    def on_save(filename)
      case File.extname(filename)
      when '.svg'
        Wx::SVGFileDC.draw_on(filename, 600, 650) do |svgDC|
          on_draw(svgDC)
          svgDC.ok?
        end
      when '.ps'
        print_data = Wx::PRT::PrintData.new
        print_data.set_print_mode(Wx::PRINT_MODE_FILE)
        print_data.set_filename(filename)
        print_data.set_orientation(Wx::PrintOrientation::PORTRAIT)
        print_data.set_paper_id(Wx::PaperSize::PAPER_A4)
        screen_ppi = printer_ppi = nil
        Wx::ScreenDC.draw_on do |dc|
          screen_ppi = dc.get_ppi
        end
        Wx::PRT::PostScriptDC.draw_on(print_data) do |dc|
          printer_ppi = dc.get_ppi
          scale = printer_ppi.x.to_f / screen_ppi.x.to_f
          dc.start_doc("Drawing sample")
          # Define default font.
          dc.set_font(Wx::FontInfo.new(10).family(Wx::FontFamily::FONTFAMILY_MODERN))
          dc.start_page
          Wx::ScaledDC.draw_on(dc, scale) do |sdc|
            on_draw(sdc)
          end
          dc.end_page
          dc.end_doc
        end
      end
    end

    def on_paint
      paint do |dc|
        on_draw(dc)
      end
    end

    # Define the repainting behaviour
    def on_draw(dc)
      dc.set_font(Wx::SWISS_FONT)
      dc.set_pen(Wx::GREEN_PEN)

      case @index
      when PAGE_Lines
        # draw lines to make a cross
        dc.draw_line(0, 0, 200, 200)
        dc.draw_line(200, 0, 0, 200)
        # draw point colored line and spline
        wP = Wx::Pen.new(Wx::CYAN_PEN)
        wP.set_width(3)
        dc.set_pen(wP)

        dc.draw_point(25,15)
        dc.draw_line(50, 30, 200, 30)
        dc.draw_spline(50, 200, 50, 100, 200, 10)

      when PAGE_Polygons
        # draw standard shapes
        dc.set_brush(Wx::CYAN_BRUSH)
        dc.set_pen(Wx::RED_PEN)
        dc.draw_rectangle(10, 10, 100, 70)
        wB = Wx::Brush.new('DARK ORCHID', Wx::BRUSHSTYLE_TRANSPARENT)
        dc.set_brush(wB)
        dc.draw_rounded_rectangle(50, 50, 100, 70, 20)
        dc.set_brush(Wx::Brush.new('GOLDENROD'))
        dc.draw_ellipse(100, 100, 100, 50)

        points = [
          [100, 200],
          [70, 260],
          [160, 230],
          [40, 230],
          [130, 260],
        ]
        dc.draw_polygon(points)
        points << [100, 200]
        dc.draw_lines(points, 160)

      when PAGE_Text
        # draw text in Arial or similar font
        dc.draw_line(50,25,50,35)
        dc.draw_line(45,30,55,30)
        dc.draw_text('This is a Swiss-style string', 50, 30)
        dc.with_text_foreground(:FIREBRICK) do
          # no effect in msw ??
          dc.set_text_background(:wheat)
          dc.draw_text('This is a Red string', 50, 200)
          dc.draw_rotated_text('This is a 45 deg string', 50, 200, 45)
          dc.draw_rotated_text('This is a 90 deg string', 50, 200, 90)
          dc.set_font(Wx::FontInfo.new(18)
                                  .face_name('Times New Roman')
                                  .family(Wx::FONTFAMILY_ROMAN)
                                  .italic.bold)
        end
        dc.draw_text('This is a Times-style string', 50, 60)

      when PAGE_Arcs
        # four arcs start and end points, center
        dc.set_brush(Wx::GREEN_BRUSH)
        dc.draw_arc(200,300, 370,230, 300,300)
        dc.set_brush(Wx::BLUE_BRUSH)
        dc.draw_arc(270-50, 270-86, 270-86, 270-50, 270,270)
        dc.set_device_origin(-10,-10)
        dc.draw_arc(270-50, 270-86, 270-86, 270-50, 270,270)
        dc.set_device_origin(0,0)

        wP = Wx::Pen.new('CADET BLUE')
        dc.set_pen(wP)
        dc.draw_arc(75,125, 110, 40, 75, 75)

        wP.set_colour(:SALMON)
        dc.set_pen(wP)
        dc.set_brush(Wx::RED_BRUSH)
        # top left corner, width and height, start and end angle
        # 315 same center and x-radius as last pie-arc, half Y radius
        dc.draw_elliptic_arc(25,50,100,50,180.0,45.0)

        wP = Wx::Pen.new(Wx::CYAN_PEN)
        wP.set_width(3)
        dc.set_pen(wP)
        dc.set_brush(Wx::Brush.new('SALMON'))
        dc.draw_elliptic_arc(300,  0,200,100, 0.0,145.0)
        # same end point
        dc.draw_elliptic_arc(300, 50,200,100,90.0,145.0)
        dc.draw_elliptic_arc(300,100,200,100,90.0,345.0)

      when PAGE_Checkmarks
        dc.draw_check_mark( 30,30,25,25)
        dc.set_brush(Wx::Brush.new('SALMON',Wx::BRUSHSTYLE_TRANSPARENT))
        dc.draw_check_mark( 80,50,75,75)
        dc.draw_rectangle( 80,50,75,75)

      when PAGE_ScaledText
        dc.set_font(Wx::FontInfo.new(18)
                                .face_name('Times New Roman')
                                .family(Wx::FONTFAMILY_ROMAN)
                                .italic.bold)
        dc.draw_line(0, 0, 200, 200)
        dc.draw_line(200, 0, 0, 200)
        dc.draw_text('This is an 18pt string', 50, 60)

        # rescale and draw in blue
        wP = Wx::Pen.new(Wx::CYAN_PEN)
        dc.set_pen(wP)
        dc.set_user_scale(2.0,0.5)
        dc.set_device_origin(200,0)
        dc.draw_line(0, 0, 200, 200)
        dc.draw_line(200, 0, 0, 200)
        dc.draw_text('This is an 18pt string 2 x 0.5 UserScaled', 50, 60)
        dc.set_user_scale(2.0,2.0)
        dc.set_device_origin(200,200)
        dc.draw_text('This is an 18pt string 2 x 2 UserScaled', 50, 60)

        wP = Wx::Pen.new(Wx::RED_PEN)
        dc.set_pen(wP)
        dc.set_user_scale(1.0,1.0)
        dc.set_device_origin(0,10)
        dc.set_map_mode(Wx::MM_METRIC) #svg ignores this
        dc.draw_line(0, 0, 200, 200)
        dc.draw_line(200, 0, 0, 200)
        dc.draw_text("This is an 18pt string in MapMode", 50, 60)

      when PAGE_Bitmaps
        dc.draw_icon(Wx::Icon.new(File.join(__dir__,'..', 'art', "wxruby.png")), 10, 10)
        dc.draw_bitmap(Wx::Bitmap.new(File.join(__dir__, 'SVGlogo24.xpm')), 50,15)

      when PAGE_Clipping
        dc.set_text_foreground('RED')
        dc.draw_text('Red = Clipping Off', 30, 5)
        dc.set_text_foreground('GREEN')
        dc.draw_text('Green = Clipping On', 30, 25)

        dc.set_text_foreground(:BLACK)

        dc.set_pen(Wx::RED_PEN)
        dc.set_brush(Wx::Brush.new('SALMON', Wx::BRUSHSTYLE_TRANSPARENT))
        dc.draw_check_mark(80,50,75,75)
        dc.draw_rectangle(80,50,75,75)

        dc.set_pen(Wx::GREEN_PEN)

        # Clipped checkmarks
        dc.draw_rectangle(180,50,75,75)
        dc.set_clipping_region(180,50,75,75)                   # x,y,width,height version
        dc.draw_check_mark(180,50,75,75)
        dc.destroy_clipping_region

        dc.draw_rectangle(Wx::Rect.new(80,150,75,75))
        dc.set_clipping_region(Wx::Point.new(80,150), Wx::Size.new(75,75))  # pt,size version
        dc.draw_check_mark(80,150,75,75)
        dc.destroy_clipping_region

        dc.draw_rectangle(Wx::Rect.new(180,150,75,75))
        dc.set_clipping_region(Wx::Rect.new(180,150,75,75))          # rect version
        dc.draw_check_mark(180,150,75,75)
        dc.destroy_clipping_region

        dc.draw_rectangle(Wx::Rect.new(80,250,50,65))
        dc.draw_rectangle(Wx::Rect.new(105,260,50,65))
        dc.set_clipping_region(Wx::Rect.new(80,250,50,65))  # second call to SetClippingRegion
        dc.set_clipping_region(Wx::Rect.new(105,260,50,65))  # forms intersection with previous
        dc.draw_check_mark(80,250,75,75)
        dc.destroy_clipping_region                   # only one call to destroy (there's no stack)

        # ** Clipping by wxRegion not implemented for SVG.   Should be
        # ** possible, but need to access points that define the wxRegion
        # ** from inside DoSetDeviceClippingRegion() and wxRegion does not
        # ** implement anything like getPoints().
        # points[0].x = 180 points[0].y = 250
        # points[1].x = 255 points[1].y = 250
        # points[2].x = 180 points[2].y = 325
        # points[3].x = 255 points[3].y = 325
        # points[4].x = 180 points[4].y = 250
        #
        # dc.DrawLines (5, points)
        # wxRegion reg = wxRegion(5,points)
        #
        # dc.SetClippingRegion(reg)
        # dc.DrawCheckMark ( 180,250,75,75)
        # dc.DestroyClippingRegion()

      when PAGE_TextPosition
        txtPad = 0

        wP = Wx::Pen.new(Wx::RED_PEN)
        dc.set_pen(wP)

        # Horizontal text
        txtStr = 'Horizontal string'
        txtW, txtH, _, _ = dc.get_text_extent(txtStr)
        txtX = 50
        txtY = 300
        dc.draw_rectangle(txtX, txtY, txtW + 2*txtPad, txtH + 2*txtPad)
        dc.draw_text(txtStr, txtX + txtPad, txtY + txtPad)

        # Vertical text
        txtStr = 'Vertical string'
        txtW, txtH, _, _ = dc.get_text_extent(txtStr)
        txtX = 50
        txtY = 250
        dc.draw_rectangle(txtX, txtY - (txtW + 2*txtPad), txtH + 2*txtPad, txtW + 2*txtPad)
        dc.draw_rotated_text(txtStr, txtX + txtPad, txtY - txtPad, 90)

        # 45 degree text
        txtStr = '45 deg string'
        txtW, txtH, _, _ = dc.get_text_extent(txtStr)
        lenW = (txtW + 2*txtPad) / Math.sqrt(2.0)
        lenH = (txtH + 2*txtPad) / Math.sqrt(2.0)
        padding = txtPad / Math.sqrt(2.0)
        txtX = 150
        txtY = 200
        dc.draw_line(txtX - padding.to_i, txtY, txtX + lenW.to_i, txtY - lenW.to_i) # top
        dc.draw_line(txtX + lenW.to_i, txtY - lenW.to_i, txtX - (padding + lenH + lenW).to_i, txtY + (lenH - lenW).to_i)
        dc.draw_line(txtX - padding.to_i, txtY, txtX - (padding + lenH).to_i, txtY + lenH.to_i)
        dc.draw_line(txtX - (padding + lenH).to_i, txtY + lenH.to_i, txtX - (padding + lenH + lenW).to_i, txtY + (lenH - lenW).to_i) # bottom
        dc.draw_rotated_text(txtStr, txtX, txtY, 45)
      end

      Wx.log_status(PG_DESC[@index])
    end

  end

  class ImageFrame < Wx::Frame

    def initialize
      super(nil, title: 'ImagePrint Demo', size: [500, 400])

      icon_file = File.join(__dir__,'..', 'art', "wxruby.png")
      self.icon = Wx::Icon.new(icon_file)

      if Wx.has_feature?(:USE_STATUSBAR)
        create_status_bar()
      end

      # Make a menubar
      file_menu = Wx::Menu.new
      file_menu.append(Wx::ID_SAVE)
      file_menu.append(Wx::ID_EXIT)

      help_menu = Wx::Menu.new
      help_menu.append(Wx::ID_ABOUT)

      mbar = Wx::MenuBar.new

      mbar.append(file_menu, "&File")
      mbar.append(help_menu, "&Help")

      # Associate the menu bar with the frame
      self.menu_bar = mbar

      # Create a notebook
      @notebook = Wx::Notebook.new(self, style: Wx::BK_TOP)

      # Add SVG Windows to a notebook
      ImagePage::PAGES.each_with_index do |title, index|
        @notebook.add_page(ImagePage.new(@notebook, index), title)
      end

      evt_menu Wx::ID_SAVE, :file_save_picture
      evt_menu Wx::ID_EXIT, :on_quit
      evt_menu Wx::ID_ABOUT, :on_about
    end

    def file_save_picture(event)
      page = @notebook.current_page

      wildcard = 'SVG vector picture files (*.svg)|*.svg'
      if Wx.has_feature?(:USE_PRINTING_ARCHITECTURE)
        wildcard << '|PostScript files (*.ps)|*.ps'
      end
      Wx::FileDialog(self, 'Save Picture as', '',
                     @notebook.get_page_text(@notebook.get_selection),
                     wildcard,
                     Wx::FD_SAVE|Wx::FD_OVERWRITE_PROMPT) do |dlg|
        if dlg.show_modal == Wx::ID_OK
          page.on_save(dlg.get_path)
        end
      end
    end

    def on_about(event)
      Wx.message_box(
        "wxRuby ImagePrint sample\n" +
        "(converted from wxWidgets SVGTest)\n" +
        "Authors:\n" +
        "   Chris Elliott (c) 2002-2009\n" +
        "   Prashant Kumar Nirmal (c) 2017\n" +
        "   Martin Corino (c) 2023\n" +
        'Usage: click File|Save to Save the Selected Image page',
        'About ImagePrint Test')
    end

    def on_quit(event)
      close
    end

  end

end

module ImagePrintSample

  include WxRuby::Sample if defined? WxRuby::Sample

  def self.describe
    { file: __FILE__,
      summary: 'wxRuby Image printing example.',
      description: 'wxRuby example showcasing Wx::SVGFileDC/Wx::PostScriptDC to create SVG/PS files.' }
  end

  def self.activate
    frame = ImagePrt::ImageFrame.new
    frame.show
    frame
  end

  if $0 == __FILE__
    Wx::App.run { ImagePrintSample.activate }
  end

end
