# 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

# Adapted for wxRuby3
###

require 'wx'

module Printing

  WXPRINT_QUIT = Wx::ID_EXIT
  WXPRINT_PRINT = Wx::ID_PRINT
  WXPRINT_PAGE_SETUP = Wx::ID_PRINT_SETUP
  WXPRINT_PREVIEW = Wx::ID_PREVIEW

  WXPRINT_PRINT_PS = 105
  WXPRINT_PAGE_SETUP_PS = 107
  WXPRINT_PREVIEW_PS = 108

  WXPRINT_ABOUT = Wx::ID_ABOUT

  WXPRINT_ANGLEUP = 110
  WXPRINT_ANGLEDOWN = 111

  class MyFrame < Wx::Frame

    attr_accessor :canvas

    def initialize(parent, title, pos, size)
      super(parent, -1, title, pos, size)
      @canvas = nil
      @bitmap = Wx::Bitmap.new
      @angle = 30

      # map events
      evt_menu(WXPRINT_QUIT) { |e| on_exit(e) }
      evt_menu(WXPRINT_PRINT) { |e| on_print(e) }
      evt_menu(WXPRINT_PREVIEW) { |e| on_print_preview(e) }
      evt_menu(WXPRINT_PAGE_SETUP) { |e| on_page_setup(e) }
      evt_menu(WXPRINT_ABOUT) { |e| on_print_about(e) }
      evt_menu(WXPRINT_ANGLEUP) { |e| on_angle_up(e) }
      evt_menu(WXPRINT_ANGLEDOWN) { |e| on_angle_down(e) }
    end

    def draw(dc)
      dc.set_background(Wx::WHITE_BRUSH)
      dc.clear()
      dc.set_font(Wx::get_app.test_font)

      dc.set_background_mode(Wx::BRUSHSTYLE_TRANSPARENT)

      dc.set_brush(Wx::CYAN_BRUSH)
      dc.set_pen(Wx::RED_PEN)

      dc.draw_rounded_rectangle(0, 20, 200, 80, 20)

      dc.draw_text("Rectangle 200 by 80", 40, 40)

      dc.set_pen(Wx::Pen.new(Wx::BLACK, 0, Wx::PENSTYLE_DOT_DASH))
      dc.draw_ellipse(50, 140, 100, 50)
      dc.set_pen(Wx::RED_PEN)

      dc.draw_text("Test message: this is in 10 point text", 10, 180)

      test = "Hebrew    שלום -- Japanese (日本語)"
      dc.draw_text(test, 10, 200)

      points = []
      points << Wx::Point.new(0, 0)
      points << Wx::Point.new(20, 0)
      points << Wx::Point.new(20, 20)
      points << Wx::Point.new(10, 20)
      points << Wx::Point.new(10, -20)

      dc.draw_polygon(points, 20, 250, Wx::ODDEVEN_RULE)
      dc.draw_polygon(points, 50, 250, Wx::WINDING_RULE)

      dc.draw_elliptic_arc(80, 250, 60, 30, 0.0, 270.0)

      points[0].x = 150
      points[0].y = 250
      points[1].x = 180
      points[1].y = 250
      points[2].x = 180
      points[2].y = 220
      points[3].x = 200
      points[3].y = 220
      points.pop
      dc.draw_spline(points)

      dc.draw_arc(20, 10, 10, 10, 25, 40)

      str = ""
      i = 0
      str = "---- Text at angle #{i} ----"
      dc.draw_rotated_text(str, 100, 300, i)

      i = @angle;
      str = "---- Text at angle #{i} ----"
      dc.draw_rotated_text(str, 100, 300, i)

      dc.set_pen(Wx::BLACK_PEN)
      dc.draw_line(0, 0, 200, 200)
      dc.draw_line(200, 0, 0, 200)

      # Load icon
      if Wx::PLATFORM == "WXMSW"
        icon_file = File.join(File.dirname(__FILE__), 'mondrian.ico')
        my_icon = Wx::Icon.new(icon_file, Wx::BITMAP_TYPE_ICO)
      else
        icon_file = File.join(File.dirname(__FILE__), 'mondrian.xpm')
        my_icon = Wx::Icon.new(icon_file, Wx::BITMAP_TYPE_XPM)
      end

      dc.draw_icon(my_icon, 100, 100)

      if @bitmap.is_ok
        dc.draw_bitmap(@bitmap, 10, 10)
      end
    end

    def on_angle_up(event)
      @angle += 5
      canvas.refresh
    end

    def on_angle_down(event)
      @angle -= 5
      canvas.refresh
    end

    def on_size(event)
      super(event)
    end

    def on_print(event)
      printer = Wx::Printer.new(Wx::PrintDialogData.new(Wx::get_app.page_setup_data.print_data))
      printout = MyPrintout.new("My printout")
      if (!printer.print(self, printout, true))
        if (Wx::Printer.get_last_error == Wx::PRINTER_ERROR)
          Wx::message_box("There was a problem printing.\nPerhaps your current printer is not set correctly?", "Printing", Wx::OK)
        else
          Wx::message_box("You canceled printing", "Printing", Wx::OK)
        end
      else
        Wx::get_app.page_setup_data.print_data = printer.print_dialog_data.print_data
      end
    end

    def on_print_preview(event)
      # Pass two printout objects: for preview, and possible printing.
      preview = Wx::PrintPreview.new(MyPrintout.new,
                                     MyPrintout.new,
                                     Wx::PrintDialogData.new(Wx::get_app.page_setup_data.print_data))
      if not preview.is_ok
        # delete preview;
        Wx::message_box("There was a problem previewing.\nPerhaps your current printer is not set correctly?", "Previewing", Wx::OK)
        return
      end

      prev_frame = Wx::PreviewFrame.new(preview, self, "Demo Print Preview", Wx::Point.new(100, 100), Wx::Size.new(600, 650))
      prev_frame.centre(Wx::BOTH)
      prev_frame.init
      prev_frame.show
    end

    def on_page_setup(event)
      Wx::PRT.PageSetupDialog(self, Wx::get_app.page_setup_data) do |dlg|
        dlg.show_modal

        Wx::get_app.page_setup_data = dlg.page_setup_data
      end
    end

    def on_exit(event)
      close(true)
    end

    def on_print_about(event)
      Wx::message_box("wxWidgets printing demo\nAuthor: Julian Smart\nAdapted to wxRuby by Sean Long",
                      "About wxRuby printing demo", Wx::OK | Wx::CENTRE)
    end

  end

  class MyCanvas < Wx::ScrolledWindow
    # attr_accessor :frame

    def initialize(parent, pos, size, style = Wx::RETAINED)
      super(parent, -1, pos, size, style)
      @frame = parent
      set_background_colour(Wx::WHITE)
      evt_mouse_events() { |e| on_event(e) }
    end

    def on_draw(dc)
      @frame.draw(dc)
    end

    def on_event(event) end
  end

  class MyPrintout < Wx::Printout
    def initialize(title = "My printout")
      super(title)
    end

    def on_print_page(page)
      dc = get_dc
      if dc
        if page == 1
          draw_page_one(dc)
        elsif (page == 2)
          draw_page_two(dc)
        end
        dc.set_device_origin(0, 0)
        dc.set_user_scale(1.0, 1.0)

        buf = "PAGE #{page}"
        dc.draw_text(buf, 10, 10)

        return true
      else
        return false
      end
    end

    def on_begin_document(start_page, end_page)
      go_ahead = super(start_page, end_page)
      if (!go_ahead)
        return false
      end
      return true
    end

    def has_page(page)
      return (page == 1 || page == 2)
    end

    def get_page_info
      min_page = 1
      max_page = 2
      sel_page_from = 1
      sel_page_to = 2
      [min_page, max_page, sel_page_from, sel_page_to]
    end

    def draw_page_one(dc)
      # You might use THIS code if you were scaling
      # graphics of known size to fit on the page.

      # We know the graphic is 200x200. If we didn't know this,
      # we'd need to calculate it.
      max_x = 200
      max_y = 200

      # Let's have at least 50 device units margin
      margin_x = 50
      margin_y = 50

      # Add the margin to the graphic size
      max_x += (2 * margin_x)
      max_y += (2 * margin_y)

      # Get the size of the DC in pixels
      size = dc.get_size
      w = size.get_width
      h = size.get_height

      # Calculate a suitable scaling factor
      scale_x = (w / max_x)
      scale_y = (h / max_y)

      # Use x or y scaling factor, whichever fits on the DC
      # actualScale = Wx::min(scaleX,scaleY)
      if scale_x < scale_y
        actual_scale = scale_x
      else
        actual_scale = scale_y
      end

      # Calculate the position on the DC for centring the graphic
      pos_x = ((w - (200 * actual_scale)) / 2.0)
      pos_y = ((h - (200 * actual_scale)) / 2.0)

      # Set the scale and origin
      dc.set_user_scale(actual_scale, actual_scale)
      dc.set_device_origin(pos_x.to_i, pos_y.to_i)

      Wx::get_app.frame.draw(dc)
    end

    def draw_page_two(dc)
      # You might use THIS code to set the printer DC to ROUGHLY reflect
      # the screen text size. This page also draws lines of actual length
      # 5cm on the page.

      # Get the logical pixels per inch of screen and printer
      ppi_screen_x, ppi_screen_y = get_ppi_screen
      ppi_printer_x, ppi_printer_y = get_ppi_printer

      # This scales the DC so that the printout roughly represents the
      # the screen scaling. The text point size _should_ be the right size
      # but in fact is too small for some reason. This is a detail that will
      # need to be addressed at some point but can be fudged for the
      # moment.
      scale = (ppi_printer_x.to_f / ppi_screen_x.to_f)

      # Now we have to check in case our real page size is reduced
      # (e.g. because we're drawing to a print preview memory DC)
      page_width, page_height = get_page_size_pixels
      size = dc.get_size
      w = size.get_width
      h = size.get_height

      # If printer pageWidth == current DC width, then this doesn't
      # change. But w might be the preview bitmap width, so scale down.
      overall_scale = scale * (w.to_f / page_width.to_f)
      dc.set_user_scale(overall_scale, overall_scale)

      # Calculate conversion factor for converting millimetres into
      # logical units.
      # There are approx. 25.4 mm to the inch. There are ppi
      # device units to the inch. Therefore 1 mm corresponds to
      # ppi/25.4 device units. We also divide by the
      # screen-to-printer scaling factor, because we need to
      # unscale to pass logical units to DrawLine.

      # Draw 50 mm by 50 mm L shape
      log_units_factor = (ppi_printer_x.to_f / (scale * 25.4))
      log_units = (50 * log_units_factor)
      dc.set_pen(Wx::BLACK_PEN)
      dc.draw_line(50, 250, (50.0 + log_units).to_i, 250)
      dc.draw_line(50, 250, 50, (250.0 + log_units).to_i)

      dc.set_background_mode(Wx::BRUSHSTYLE_TRANSPARENT)
      dc.set_brush(Wx::TRANSPARENT_BRUSH)

      # GetTextExtent demo:
      words = ["This ", "is ", "GetTextExtent ", "testing ", "string. ", "Enjoy ", "it!"]
      w, h = 0, 0
      x = 200
      y = 250
      fnt = Wx::Font.new(15, Wx::FONTFAMILY_SWISS, Wx::FONTSTYLE_NORMAL, Wx::FONTWEIGHT_NORMAL)

      dc.set_font(fnt)

      words.each do |word|
        w, h = dc.get_text_extent(word)
        dc.draw_rectangle(x, y, w.to_i, h.to_i)
        w, h = dc.get_text_extent(word)
        dc.draw_text(word, x, y)
        x += w
      end

      dc.set_font(Wx::get_app.test_font)

      dc.draw_text("Some test text", 200, 300)

      left_margin = 20
      right_margin = 20
      top_margin = 20
      bottom_margin = 20

      page_width_mm, page_height_mm = get_page_size_mm

      left_margin_logical = (log_units_factor * left_margin).to_i
      top_margin_logical = (log_units_factor * top_margin).to_i
      bottom_margin_logical = (log_units_factor * (page_height_mm - bottom_margin)).to_i
      right_margin_logical = (log_units_factor * (page_width_mm - right_margin)).to_i

      dc.set_pen(Wx::RED_PEN)
      dc.draw_line(left_margin_logical, top_margin_logical,
                   right_margin_logical, top_margin_logical)
      dc.draw_line(left_margin_logical, bottom_margin_logical,
                   right_margin_logical, bottom_margin_logical)

      write_page_header(self, dc, "A header", log_units_factor)
    end

    # Writes a header on a page. Margin units are in millimetres.
    def write_page_header(printout, dc, text, mm_to_logical)
      page_width_mm, page_height_mm = printout.get_page_size_mm

      left_margin = 10
      top_margin = 10
      right_margin = 10

      left_margin_logical = (mm_to_logical * left_margin).to_i
      top_margin_logical = (mm_to_logical * top_margin).to_i
      right_margin_logical = (mm_to_logical * (page_width_mm - right_margin)).to_i

      x_extent, y_extent = dc.get_text_extent(text)
      x_pos = (((((page_width_mm - left_margin - right_margin) / 2.0) + left_margin) * mm_to_logical) - (x_extent / 2.0))
      dc.draw_text(text, x_pos.to_i, top_margin_logical)

      dc.set_pen(Wx::BLACK_PEN)
      dc.draw_line(left_margin_logical, (top_margin_logical + y_extent).to_i,
                   right_margin_logical, (top_margin_logical + y_extent).to_i)

      return true
    end
  end

  class MyApp < Wx::App
    attr_accessor :frame, :test_font, :page_setup_data

    def on_init
      @test_font = Wx::Font.new(10, Wx::FONTFAMILY_SWISS, Wx::FONTSTYLE_NORMAL, Wx::FONTWEIGHT_NORMAL)

      # Create the main frame window
      @frame = MyFrame.new(nil, "wxRuby Printing Demo", Wx::Point.new(0, 0), Wx::Size.new(400, 400))

      # Give it a status line
      @frame.create_status_bar(2)

      # Load icon
      @frame.icon = Wx.Icon(:sample, Wx::BITMAP_TYPE_XPM, art_path: File.join(__dir__, '..'))

      # Make a menubar
      file_menu = Wx::Menu.new

      file_menu.append(WXPRINT_PRINT, "&Print...", "Print")
      file_menu.append(WXPRINT_PAGE_SETUP, "Page Set&up...", "Page setup")
      file_menu.append(WXPRINT_PREVIEW, "Print Pre&view", "Preview")

      file_menu.append_separator()
      file_menu.append(WXPRINT_ANGLEUP, "Angle up\tAlt-U", "Raise rotated text angle")
      file_menu.append(WXPRINT_ANGLEDOWN, "Angle down\tAlt-D", "Lower rotated text angle")
      file_menu.append_separator()
      file_menu.append(WXPRINT_QUIT, "E&xit", "Exit program")

      help_menu = Wx::Menu.new
      help_menu.append(WXPRINT_ABOUT, "&About", "About this demo")

      menu_bar = Wx::MenuBar.new

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

      # Associate the menu bar with the frame
      @frame.set_menu_bar(menu_bar)

      canvas = MyCanvas.new(@frame, Wx::Point.new(0, 0), Wx::Size.new(100, 100), Wx::RETAINED | Wx::HSCROLL | Wx::VSCROLL)

      # Give it scrollbars: the virtual canvas is 20 * 50 = 1000 pixels in each direction
      canvas.set_scrollbars(20, 20, 50, 50)

      @frame.canvas = canvas

      @frame.centre(Wx::BOTH)
      @frame.show()

      @frame.set_status_text("Printing demo")

      set_top_window(@frame)

      #@print_data = Wx::PrintData.new
      @page_setup_data = Wx::PageSetupDialogData.new

      return true
    end

    def on_exit
      return true
    end
  end

end

module PrintingSample

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

  def self.describe
    { file: __FILE__,
      summary: 'wxRuby Printing example.',
      description: 'wxRuby example showcasing printing framework.' }
  end

  def self.run
    execute(__FILE__)
  end

  if $0 == __FILE__
    Printing::MyApp.run
  end

end
