# vim:set et sts=4 sw=4:
# -*- coding: utf-8 -*-
#
# ibus - The Input Bus
#
# Copyright (c) 2014 Peng Huang <shawn.p.huang@gmail.com>
# Copyright (c) 2014 Takao Fujiwara <takao.fujiwara1@gmail.com>
# Copyright (c) 2013-2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.

# This file is ported from
# gnome-control-center/panels/region/cc-input-chooser.c

from os import path

from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import GdkPixbuf
from gi.repository import IBus

import functools
import gettext
import locale

import config

_ = lambda a : gettext.dgettext(config.gettext_package, a)
N_ = lambda a: a

ROW_TRAVEL_DIRECTION_NONE,    \
ROW_TRAVEL_DIRECTION_FORWARD,   \
ROW_TRAVEL_DIRECTION_BACKWARD = list(range(3))

class LayoutDialog(Gtk.Dialog):
  __gtype_name__ = 'LayoutDialog'
  __initial_keys = [ '2', '2noshift',
                '3-90', '3-91', '3-p3',
                '3moa-semoe-2016',
                '3shin-2003', '3shin-p2',
                'qwerty', 'dvorak', 'colemak',]
  __row_keys_initial_count = 0
  __row_keys_total_count = 0
  __row_keys_filter_count = 0
  __row_key_activated = False

  def __init__(self, transient_for=None,
                current_layout_id=None,
                layout_list=None):
    super(LayoutDialog, self).__init__(
        transient_for = transient_for,
        resizable = False)
    buttons = (_("_Layout..."), Gtk.ResponseType.NONE,
            _("_Cancel"), Gtk.ResponseType.CANCEL,
            _("_Ok"), Gtk.ResponseType.APPLY)
    self.add_buttons(*buttons)
    self.set_title(_("Select a keyboard layout"))
    self.set_response_sensitive(Gtk.ResponseType.APPLY, False)
    self.set_response_sensitive(Gtk.ResponseType.NONE, False)
    self.connect("response", self.__on_response, None)

    self.__layout_list = {}
    self.__layout_list_keys = {}

    self.__scrolled = Gtk.ScrolledWindow(
        hscrollbar_policy = Gtk.PolicyType.NEVER,
        vscrollbar_policy = Gtk.PolicyType.NEVER,
        shadow_type = Gtk.ShadowType.IN,
        margin_left = 6,
        margin_right = 6,
        margin_top = 6,
        margin_bottom = 6, )

    self.vbox.add(self.__scrolled)
    viewport = Gtk.Viewport()
    self.__scrolled.add(viewport)
    # Gtk.ListBox has been available since gtk 3.10
    self.__list = Gtk.ListBox(vexpand = True,
                        halign = Gtk.Align.FILL,
                        valign = Gtk.Align.FILL)
    viewport.add(self.__list)

    self.__adjustment = self.__scrolled.get_vadjustment()
    self.__list.set_adjustment(self.__adjustment)
    self.__list.set_filter_func(self.__list_filter, None)
    self.__list.connect("key-press-event", self.__row_on_key_press)
    self.__list.connect("button-press-event", self.__row_on_button_press)
    #self.__list.connect("key-release-event", self.__row_on_key)
    #self.__list.connect("button-release-event", self.__row_on_button)
    self.__list.connect('row-activated', self.__row_activated)
    self.__list.connect('row-selected', self.__row_selected)

    self.__showing_extra = False
    self.__more_row = self.__more_row_new()

    # 찾기
    self.__filter_timeout_id = 0
    self.__filter_word = None
    self.__filter_entry = Gtk.SearchEntry(hexpand = True,
                        margin_left = 6,
                        margin_right = 6,
                        margin_top = 2,       # 높이를 self.__list 의 row 의 높이와 같도록
                        margin_bottom = 2)   # 맞추기위해 2 로 한다.
    self.__filter_entry.set_no_show_all(True)
    self.__filter_entry.connect('search-changed', self.__filter_changed)
    self.vbox.add(self.__filter_entry)

    self.set_layouts(current_layout_id, layout_list)

    self.show_all()

  def __on_response(self, widget, response, data = None):
    if response == Gtk.ResponseType.NONE:
      self.__layout_view_clicked(None)
      widget.stop_emission_by_name("response")

  def __list_filter(self, row, data):
    if row == self.__more_row:
      return not self.__showing_extra
    if not self.__showing_extra and row.is_extra:
      return False
    if self.__filter_word == None:
      return True

    name = row.name.lower()
    if self.__filter_word != None:
      word = self.__filter_word.lower()
      if name.find(word) != -1:
        row.filter_index = self.__row_keys_filter_count
        self.__row_keys_filter_count += 1
        return True
    return False

  def __row_on_button_press (self, widget, event, data = None):
    self.__row_key_activated = False

  def __row_on_key_press (self, widget, event, data = None):  #widget: listbox
    row = self.__list.get_selected_row()
    if event.hardware_keycode in [111, 116]:   # arrow up, arrow down
      self.__row_key_activated = True
    elif event.hardware_keycode in [23]:   # tab
      self.__scrolled.grab_focus()
    elif row == self.__more_row:
      return
    elif event.hardware_keycode in [36, 65]: # enter, space_bar
      self.response(Gtk.ResponseType.APPLY)

  def __row_activated(self, box, row):
    if row == self.__more_row:
      self.__show_more()
      return

  def __row_selected(self, box, row):
    if row and self.__showing_extra and self.__row_key_activated:
      if self.__filter_word == None:
        page_index = int(row.index / self.__row_keys_initial_count)
      else:
        page_index = int(row.filter_index / self.__row_keys_initial_count)
      self.__adjustment.set_value(self.__adjustment.get_lower() + \
                              (self.__adjustment.get_page_size() * page_index))
    self.__row_key_activated = False
    self.set_response_sensitive(Gtk.ResponseType.APPLY,
                            row != None and row != self.__more_row)
    self.set_response_sensitive(Gtk.ResponseType.NONE,
                            row != None and row != self.__more_row)

  def __padded_label_new(self, text, icon, alignment, direction):
    hbox = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)

    if direction == ROW_TRAVEL_DIRECTION_BACKWARD:
      rtl = (Gtk.Widget.get_default_direction() == \
           Gtk.TextDirection.RTL)
      if rtl:
        arrow = Gtk.Image.new_from_icon_name(
          'go-previous-rtl-symbolic', Gtk.IconSize.MENU)
      else:
        arrow = Gtk.Image.new_from_icon_name(
          'go-previous-symbolic', Gtk.IconSize.MENU)
      hbox.pack_start(arrow, False, True, 0)

    label = Gtk.Label()
    label.set_text(text)
    label.set_halign(alignment)
    label.set_valign(Gtk.Align.CENTER)
    label.set_margin_left(20)
    label.set_margin_right(20)
    label.set_margin_top(6)
    label.set_margin_bottom(6)
    hbox.pack_start(label, True, True, 0)
    return hbox

  def __list_box_row_new(self, key):
    row = Gtk.ListBoxRow()
    row.key = key
    row.is_extra = False
    row.name = self.__layout_list[key]
    row.index = self.__row_keys_total_count
    self.__row_keys_total_count += 1
    return row

  def __layout_key_row_new(self, key):
    row = self.__list_box_row_new(key)
    if not self.__showing_extra and key not in self.__initial_keys:
      row.is_extra = True
    else:
      self.__row_keys_initial_count += 1
    widget = self.__padded_label_new(self.__layout_list[key],
                        None,
                        Gtk.Align.CENTER,
                        ROW_TRAVEL_DIRECTION_NONE)
    row.add(widget)
    row.set_tooltip_text(key)
    return row

  def __more_row_new(self):
    row = Gtk.ListBoxRow()
    hbox = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)
    row.add(hbox)
    row.set_tooltip_text(_("More..."))
    row.name = 'more'

    arrow = Gtk.Image.new_from_icon_name('view-more-symbolic',
                                    Gtk.IconSize.MENU)
    arrow.set_margin_left(20)
    arrow.set_margin_right(20)
    arrow.set_margin_top(6)
    arrow.set_margin_bottom(6)
    arrow.set_halign(Gtk.Align.CENTER)
    arrow.set_valign(Gtk.Align.CENTER)
    hbox.pack_start(arrow, True, True, 0)
    return row

  def __set_fixed_size(self):
    if self.__scrolled.get_policy()[0] == Gtk.PolicyType.AUTOMATIC:
      return
    (width, height) = self.get_size()
    self.set_size_request(width, height)
    self.__scrolled.set_policy(Gtk.PolicyType.AUTOMATIC,
                        Gtk.PolicyType.AUTOMATIC)

  def __remove_all_children(self):
    for item in self.__list.get_children():
      self.__list.remove(item)

  def __show_layout_rows(self):
    self.__remove_all_children()
    for key in self.__layout_list_keys:
      row = self.__layout_key_row_new(key)
      self.__list.add(row)
    if self.__row_keys_total_count > self.__row_keys_initial_count:
      self.__list.add(self.__more_row)
    self.__list.show_all()
    self.__list.select_row(self.__list.get_row_at_index(0))
    self.__adjustment.set_value(self.__adjustment.get_lower())
    self.__list.invalidate_filter()
    self.__list.set_selection_mode(Gtk.SelectionMode.SINGLE)

  def __show_more(self):
    self.__set_fixed_size()
    self.__filter_entry.show()
    self.__showing_extra = True
    self.__list.invalidate_filter()
    self.__list.select_row(self.__list.get_row_at_index(0))
    self.__filter_entry.grab_focus()

  def __do_filter(self):
    text = self.__filter_entry.get_text()
    if text == '':
      self.__filter_word = None
    else:
      self.__filter_word = text
    self.__list.invalidate_filter()
    self.__filter_timeout_id = 0
    return False

  def __filter_changed(self, entry):
    if self.__filter_timeout_id == 0:
      self.__row_keys_filter_count = 0
      self.__filter_timeout_id = GLib.timeout_add(150, self.__do_filter)

  def __layout_view_clicked(self, widget):
    row = self.__list.get_selected_row()
    if row == None:
      return None

    extension_image = '.png'
    extension_text = '.txt'
    layout = None
    filename_layout = None
    filename_description = None
    if row.key.startswith('2'):
      layout = '2set_'
    elif row.key.startswith('3') or row.key in ['ahn']:
      layout = '3set_'
    elif row.key in ['ro', 'qwerty', 'dvorak', 'colemak']:
      layout = 'kb_us_'

    help_layout = None
    help_txt = None
    if layout != None :
      filename_layout = layout + row.key + extension_image
      filename_description = layout + row.key + extension_text
      help_layout = path.join(config.libhanguldatadir, "keyboards", filename_layout)
      help_txt = path.join(config.libhanguldatadir, "keyboards", filename_description)

    lines = ''
    index = 0
    if help_txt != None and path.exists(help_txt):
      # in Python 3.0, Removed file. Use open
      try:
        file_open = file(help_txt)
      except NameError:
        file_open = open(help_txt)
      for index, line in enumerate(file_open):
        lines = lines + line
      if index > 12:
        index = 12
    else:
      lines = _("Description file does not exist")

    dialog = Gtk.Dialog(transient_for=self)
    buttons=(_("_Ok"), Gtk.ResponseType.OK)
    dialog.add_buttons(*buttons)
    dialog.set_title(_("Layout infomation"))

    image = None
    if help_layout != None and path.exists(help_layout):
      pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size (help_layout, 500, 400)

      #image = Gtk.Image.new_from_file (help_layout)
      image = Gtk.Image.new_from_pixbuf (pixbuf)
    else:
      image = Gtk.Label ()
      notice = _("Image file does not exist")
      image.set_markup ('<span size="large" weight="bold">%s</span>' % notice)
    image.show()

    buffer = Gtk.TextBuffer()
    buffer.set_text(lines)
    buffer.place_cursor(buffer.get_start_iter())
    text_view = Gtk.TextView.new_with_buffer(buffer)
    text_view.set_editable(False)
    text_view.set_wrap_mode(Gtk.WrapMode.CHAR)
    scrolled_window = Gtk.ScrolledWindow()
    scrolled_window.add(text_view)
    scrolled_window.set_min_content_height((index + 2) * 20)
    scrolled_window.show_all()

    mainbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
    mainbox.pack_start(image, False, True, 0)
    mainbox.pack_start(scrolled_window, True, True, 0)
    mainbox.show()

    parent_box = dialog.vbox
    parent_box.add(mainbox)

    dialog.set_default_size(500, 200)
    dialog.run()
    dialog.destroy()

  def set_layouts(self, current_layout_id, layout_list):
    if current_layout_id == None or layout_list == None:
      return
    self.__layout_list = {}

    for (key, name) in layout_list:
      if key not in self.__layout_list:
        self.__layout_list[key] = _(name)

    keys = list(self.__layout_list.keys())
    keys.sort(key=functools.cmp_to_key(locale.strcoll))

    # move current layout to the first place
    if current_layout_id in keys:
      keys.remove(current_layout_id)
      keys.insert(0, current_layout_id)

    self.__layout_list_keys = keys
    self.__show_layout_rows()

  def get_selected_layout(self):
    row = self.__list.get_selected_row()
    if row == None:
      return (None, None)
    return (row.key, row.name)
