require "forwardable"
require "rabbit/gtk"
require "rexml/text"

begin
  require "vte3"
rescue LoadError
end

require "rabbit/rabbit"
require "rabbit/utils"

module Rabbit
  class Frame
    include ScreenInfo
    extend Forwardable

    def_delegators(:@window, :icon, :icon=, :set_icon)
    def_delegators(:@window, :icon_list, :icon_list=, :set_icon_list)
    def_delegators(:@window, :iconify, :show, :hide, :visible?)
    def_delegators(:@window, :set_size_request, :resize)

    def_delegators(:@canvas, :apply_theme, :theme_name)
    def_delegators(:@canvas, :saved_image_type=, :saved_image_base_name=)
    def_delegators(:@canvas, :save_as_image, :each_slide_pixbuf)
    def_delegators(:@canvas, :print, :filename=)
    def_delegators(:@canvas, :output_html=, :output_index_html=)
    def_delegators(:@canvas, :rss_base_uri=, :use_gl?, :use_gl=)

    attr_reader :window, :logger
    attr_accessor :geometry

    def initialize(logger, canvas)
      @logger = logger
      @canvas = canvas
      @geometry = nil
      @notebook = nil
      @terminal = nil
      @running = true
    end

    def destroyed?
      @window.nil? or @window.destroyed?
    end

    def quit
      @running = false
      @window.destroy unless destroyed?
      @window = nil
      true
    end

    def width
      @window.size[0]
    end

    def height
      @window.size[1]
    end

    def parse(source, callback=nil, &block)
      @canvas.parse(source, callback, &block)
    end

    def fullscreen
      @window.fullscreen
    end

    def unfullscreen
      @window.unfullscreen
    end

    def toggle_fullscreen
      if fullscreen?
        unfullscreen
      else
        fullscreen
      end
    end

    def fullscreen?
      @fullscreen
    end

    def main_window?
      @main_window
    end

    def update_title(new_title)
      @window.title = Utils.unescape_title(new_title)
    end

    def init_gui(width, height, main_window, window_type=nil)
      init_window(width, height, window_type)
      @fullscreen = false
      @main_window = main_window
      @terminal.show if @terminal
      @notebook.show if @notebook
      @window.show
      @canvas.post_init_gui
    end

    def fullscreen_available?
      true
    end

    def iconify_available?
      true
    end

    def toggle_terminal
      return if @terminal.nil?
      terminal_page = @notebook.page_num(@terminal)
      if @notebook.current_page == terminal_page
        @notebook.current_page = 0
      else
        @notebook.current_page = terminal_page
      end
    end

    def in_terminal?
      return false if @terminal.nil?
      @notebook.current_page == @notebook.page_num(@terminal)
    end

    private
    def init_window(width, height, window_type=nil)
      window_type ||= :toplevel
      @window = Gtk::ApplicationWindow.new(::Rabbit.application)
      @window.set_default_size(width, height)
      @window.parse_geometry(@geometry) if @geometry
      @window.set_app_paintable(true)
      if defined?(Vte::Terminal)
        init_notebook
      end
      set_window_signal
      setup_dnd
      @canvas.attach_to(self, @window, @notebook)
      if defined?(Vte::Terminal)
        init_terminal
      end
    end

    def init_notebook
      @notebook = Gtk::Notebook.new
      @notebook.show_tabs = false
      provider = Gtk::CssProvider.new
      provider.load(data: <<-CSS)
        notebook {
          border-width: 0px;
        }
      CSS
      @notebook.style_context.add_provider(provider,
                                           Gtk::StyleProvider::PRIORITY_USER)
      @window.add(@notebook)
    end

    def set_window_signal
      set_window_signal_window_state_event
      set_window_signal_destroy
    end

    def set_window_signal_window_state_event
      @window.signal_connect("window_state_event") do |widget, event|
        if event.changed_mask.fullscreen?
          @fullscreen = event.new_window_state.fullscreen?
          if @fullscreen
            @canvas.fullscreened
          else
            @canvas.unfullscreened
          end
        elsif event.changed_mask.iconified?
          if event.new_window_state.iconified?
            @canvas.iconified
          end
        end

        false
      end
    end

    def set_window_signal_destroy
      @window.signal_connect("destroy") do
        @canvas.detach
      end
    end

    def setup_dnd
      @window.drag_dest_set(:all,
                            [["text/uri-list", 0, 0],
                             ["_NETSCAPE_URL", 0, 0]],
                            :copy)
      @window.signal_connect("drag-data-received") do |*args|
        widget, context, x, y, selection_data, info, time = args
        uri = selection_data.data.chomp
        Gtk.idle_add do
          parse(Source::URI.new(nil, logger, uri))
          false
        end
        Gtk::Drag.finish(context, true, false, time)
      end

      @window.signal_connect("drag-drop") do |widget, context, x, y, time|
        true
      end
    end

    def init_terminal
      @terminal = Vte::Terminal.new
      # TODO: Support theme
      terminal_font_description = ENV["RABBIT_TERMINAL_FONT_DESCRIPTION"]
      if terminal_font_description
        @terminal.font_desc =
          Pango::FontDescription.new(terminal_font_description)
      end
      terminal_color_foreground = ENV["RABBIT_TERMINAL_COLOR_FOREGROUND"]
      if terminal_color_foreground
        @terminal.color_foreground = terminal_color_foreground
      end
      terminal_color_background = ENV["RABBIT_TERMINAL_COLOR_BACKGROUND"]
      if terminal_color_background
        @terminal.color_background = terminal_color_background
      end
      @terminal.enable_sixel = true if @terminal.respond_to?(:enable_sixel=)
      @notebook.add(@terminal)
      pid = nil
      in_terminal = false
      @notebook.signal_connect(:switch_page) do |_, page,|
        if page == @terminal
          if @running
            pid = @terminal.spawn if pid.nil?
            @canvas.pre_terminal unless in_terminal
            in_terminal = true
          end
        else
          @canvas.post_terminal if in_terminal
          in_terminal = false
        end
      end
      @terminal.signal_connect(:child_exited) do
        pid = nil
        terminal_page = @notebook.page_num(@terminal)
        if @notebook.current_page == terminal_page
          @canvas.activate("ToggleTerminal")
        end
      end
    end
  end

  class NullFrame
    class << self
      def def_null_methods(*names)
        names.each do |name|
          define_method(name) {|*args|}
        end
      end
    end

    def_null_methods(:icon, :icon=, :set_icon)
    def_null_methods(:icon_list, :icon_list=, :set_icon_list)
    def_null_methods(:update_title, :geometry, :geometry=)

    def_null_methods(:fullscreen?, :quit)

    def fullscreen_available?
      false
    end

    def iconify_available?
      false
    end

    def toggle_terminal
    end

    def in_terminal?
      false
    end
  end

  class EmbedFrame < Frame
    def update_title(new_title)
    end

    def fullscreen_available?
      false
    end

    def iconify_available?
      false
    end

    def toggle_terminal
    end

    def in_terminal?
      false
    end

    def init_gui(width, height, main_window, window_type=nil)
      @window = Gtk::EventBox.new
      @window.set_size_request(width, height)
      @canvas.attach_to(self, @window)
      @fullscreen = false
      @main_window = main_window
      @window.show
      @canvas.post_init_gui
    end
  end
end
