require 'erb'
require 'fileutils'

begin
  require "poppler"
rescue LoadError
end

module Util
  def image_type
    "png"
  end

  def image_suffix
    "png"
  end

  def image_name(i)
    "#{i}.#{image_suffix}"
  end

  def thumbnail_name(i)
    "#{i}-thumb.#{image_suffix}"
  end

  def html_name(i)
    "#{i}.html"
  end

  def index_html
    "index.html"
  end

  def text_html
    "text.html"
  end

  def filename_escape(uri)
    uri.split(/[:\/]/).join("_")
  end
end

extend Util

def ruby_poppler_is_unavailable(action, kz)
  statusbar_timeout = 3 * 1000
  name = action.name
  kz.statusbar.set_text(_("need to install Ruby/Poppler"), name)
  Kz.pop_statusbar(kz, name, statusbar_timeout)
end

def start_downloader(kz, uri)
  tmp_dir = File.join(TMP_DIR, filename_escape(uri))
  FileUtils.mkdir_p(tmp_dir)
  filename = File.join(TMP_DIR, filename_escape(File.basename(uri)))
  dl = Kz::Downloader.new(uri, filename)
  dl.signal_connect("completed") do
    Kz.barrier do
      completed(kz, tmp_dir, filename)
    end
  end
  Kz::DownloaderGroup.add(dl)
  dl.to_file
end

def make_images(kz, pages, tmp_dir, tab, index_uri)
  scale = 0.1
  id = Gtk.idle_add do
    Kz.barrier do
      page, i = pages.shift
      width, height = page.size
      pixbuf_width, pixbuf_height = page.size.collect {|x| x * scale}

      pixbuf = Gdk::Pixbuf.new(Gdk::Pixbuf::COLORSPACE_RGB, true, 8,
                               width, height)
      page.render(0, 0, width, height, 1, 0, pixbuf)
      output = File.join(tmp_dir, image_name(i))
      pixbuf.save(output, image_type)

      pixbuf = Gdk::Pixbuf.new(Gdk::Pixbuf::COLORSPACE_RGB, true, 8,
                               pixbuf_width, pixbuf_height)
      page.render(0, 0, width, height, scale, 0, pixbuf)
      thumbnail = File.join(tmp_dir, thumbnail_name(i))
      pixbuf.save(thumbnail, image_type)

      pixbuf = nil
      GC.start

      if (pages.size % 10).zero? and
          !tab.destroyed? and tab.location == index_uri
        tab.reload(Kz::EmbedReloadFlag::BYPASS_CACHE)
      end

      if pages.empty? or tab.destroyed?
        Gtk.idle_remove(id)
        false
      else
        true
      end
    end
  end
end

class HTMLGenerator
  include Util
  include ERB::Util
  public :h

  MOVE_TO_NEXT_KEYS = [
    Gdk::Keyval::GDK_n,
    Gdk::Keyval::GDK_f,
    Gdk::Keyval::GDK_j,
    Gdk::Keyval::GDK_l,
    Gdk::Keyval::GDK_Page_Down,
    Gdk::Keyval::GDK_Tab,
    Gdk::Keyval::GDK_Return,
    Gdk::Keyval::GDK_rightarrow,
    Gdk::Keyval::GDK_downarrow,
    Gdk::Keyval::GDK_space,
    Gdk::Keyval::GDK_plus,
    Gdk::Keyval::GDK_KP_Add,
    Gdk::Keyval::GDK_KP_Page_Down,
    Gdk::Keyval::GDK_KP_Enter,
    Gdk::Keyval::GDK_KP_Tab,
  ]

  MOVE_TO_PREVIOUS_KEYS = [
    Gdk::Keyval::GDK_p,
    Gdk::Keyval::GDK_b,
    Gdk::Keyval::GDK_h,
    Gdk::Keyval::GDK_k,
    Gdk::Keyval::GDK_Page_Up,
    Gdk::Keyval::GDK_leftarrow,
    Gdk::Keyval::GDK_uparrow,
    Gdk::Keyval::GDK_BackSpace,
    Gdk::Keyval::GDK_Delete,
    Gdk::Keyval::GDK_minus,
    Gdk::Keyval::GDK_KP_Subtract,
    Gdk::Keyval::GDK_KP_Page_Up,
    Gdk::Keyval::GDK_KP_Delete,
  ]

  MOVE_TO_FIRST_KEYS = [
    Gdk::Keyval::GDK_a,
    Gdk::Keyval::GDK_Home,
    Gdk::Keyval::GDK_KP_Home,
    Gdk::Keyval::GDK_less,
  ]

  MOVE_TO_LAST_KEYS = [
    Gdk::Keyval::GDK_e,
    Gdk::Keyval::GDK_End,
    Gdk::Keyval::GDK_KP_End,
    Gdk::Keyval::GDK_greater,
    Gdk::Keyval::GDK_dollar,
  ]

  TOGGLE_INDEX_MODE_KEYS = [
    Gdk::Keyval::GDK_i,
  ]

  TOGGLE_TEXT_MODE_KEYS = [
    Gdk::Keyval::GDK_t,
  ]

  template_path = Kz::Actions.search_file("view-pdf-as-image.erb")
  erb = File.open(template_path) {|f| ERB.new(f.read, nil, "-")}
  erb.def_method(self, "to_html", template_path)

  def initialize(doc, base_dir)
    @doc = doc
    @base_dir = base_dir
    @max = @doc.n_pages
  end

  def save
    index_html_file = File.join(@base_dir, index_html)
    text_html_file = File.join(@base_dir, text_html)
    @image_number = 0

    File.open(index_html_file, "w") do |index|
      @index_mode = true
      index << to_html
    end
    @index_mode = false

    File.open(text_html_file, "w") do |text|
      @text_mode = true
      text << to_html
    end
    @text_mode = false

    @doc.n_pages.times do |i|
      path = File.join(@base_dir, html_name(i))
      @image_number = i
      File.open(path, "w") do |image|
        image << to_html
      end
    end
  end

  private
  def _(msgid)
    Kz.gettext(msgid)
  end

  def title
    @doc.title
  end

  def content
    if index_mode?
      result = "<div class=\"thumbnails\">\n"
      @doc.n_pages.times do |i|
        result << "<a href=\"#{h html_name(i)}\">\n"
        result << "<img src=\"#{h thumbnail_name(i)}\">\n"
        result << "</a>\n"
      end
      result << "</div>"
      result
    elsif text_mode?
      result = "<div class=\"text\">\n"
      @doc.each do |page|
        width, height = page.size
        rectangle = Poppler::Rectangle.new(0, 0, width, height)
        result << "<pre class=\"page\">#{h page.get_text(rectangle)}</pre>"
      end
      result << "</div>"
      result
    else
      "<div class=\"image\">\n#{image}\n</div>"
    end
  end

  def image_title(image_number=nil)
    image_number ||= @image_number
    "#{h title} (#{image_number}/#{@max - 1})"
  end

  def image_src(image_number=nil)
    image_number ||= @image_number
    u(image_name(image_number))
  end

  def image(image_number=nil)
    image_number ||= @image_number
    src = image_src(image_number)
    img = "<img title=\"#{image_title(image_number)}\" src=\"#{src}\" />"
    if last_image?(image_number)
      img
    else
      href = next_href(image_number)
      "<a href=\"#{href}\">\n#{img}\n</a>"
    end
  end

  def index_mode?
    @index_mode
  end

  def text_mode?
    @text_mode
  end

  def first_image?(image_number=nil)
    image_number ||= @image_number
    image_number.zero?
  end

  def last_image?(image_number=nil)
    image_number ||= @image_number
    @max.zero? or image_number == @max - 1
  end

  def first_index(image_number=nil)
    0
  end

  def previous_index(image_number=nil)
    image_number ||= @image_number
    image_number - 1
  end

  def next_index(image_number=nil)
    image_number ||= @image_number
    image_number + 1
  end

  def last_index(image_number=nil)
    @max - 1
  end

  def first_href(image_number=nil)
    href(first_index(image_number))
  end

  def previous_href(image_number=nil)
    href(previous_index(image_number))
  end

  def next_href(image_number=nil)
    href(next_index(image_number))
  end

  def last_href(image_number=nil)
    href(last_index(image_number))
  end

  def toggle_index_mode_href
    if index_mode?
      first_href
    else
      index_html
    end
  end

  def toggle_text_mode_href
    if text_mode?
      index_html
    else
      text_html
    end
  end

  def href(image_number)
    image_number ||= @image_number
    u(html_name(image_number))
  end

  def a_link(_href, label, label_only)
    if label_only
      result = label
    else
      result = "<a href=\"#{_href}\">#{label}</a>"
    end
    "[#{result}]"
  end

  def first_link(image_number=nil)
    a_link(first_href(image_number), h("<<"), first_image?(image_number))
  end

  def previous_link(image_number=nil)
    a_link(previous_href(image_number), h("<"), first_image?(image_number))
  end

  def next_link(image_number=nil)
    a_link(next_href(image_number), h(">"), last_image?(image_number))
  end

  def last_link(image_number=nil)
    a_link(last_href(image_number), h(">>"), last_image?(image_number))
  end

  def toggle_mode_href
    if index_mode?
      first_href
    else
      index_html
    end
  end

  def toggle_mode_navi
    @index_mode = !@index_mode
    if index_mode? or text_mode?
      result = a_link(index_html, h(_("Thumbnails")), false)
    else
      result = a_link(first_href, h(_("Image")), false)
    end
    unless text_mode?
      result << a_link(text_html, h(_("Text")), false)
    end
    "<div class=\"toggle-mode\">\n#{result}\n</div>"
  ensure
    @index_mode = !@index_mode
  end

  def navi(image_number=nil)
    return '' if index_mode? or text_mode?
    result = ''
    result << "<div class=\"navi\">\n"
    result << first_link(image_number)
    result << previous_link(image_number)
    result << next_link(image_number)
    result << last_link(image_number)
    result << "\n</div>"
    result
  end
end

def completed(kz, tmp_dir, filename)
  doc = Poppler::Document.new("file://#{filename}")
  pages = []
  doc.each_with_index do |page, i|
    pages.push([page, i])
  end

  HTMLGenerator.new(doc, tmp_dir).save
  html_file = File.join(tmp_dir, index_html)
  index_uri = "file://#{html_file}"
  tab = kz.open_new_tab(index_uri)
  make_images(kz, pages, tmp_dir, tab, index_uri)
end

def act_view_pdf_as_image(action, group, kz)
  unless ::Object.const_defined?(:Poppler)
    ruby_poppler_is_unavailable(action, kz)
    return
  end
  return unless kz.current_page

  uri = nil
  uri = kz.mouse_event_info.link if kz.mouse_event_info
  return if uri.nil?

  start_downloader(kz, uri)
end

def act_view_pdf_as_image_config(config, kz)
  config[:label] = N_("View PDF as image")
  config[:tooltip] = N_("Open image version PDF in new tab")
end
