# -*- coding: utf-8; -*-
"""
Copyright (C) 2007-2013 Guake authors

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, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA
"""
import logging

from collections import defaultdict

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gdk
from gi.repository import Gtk

from guake import notifier
from guake.common import pixmapfile
from guake.split_utils import FocusMover
from guake.split_utils import SplitMover

log = logging.getLogger(__name__)


class Keybindings:
    """Handles changes in keyboard shortcuts."""

    def __init__(self, guake):
        """Constructor of Keyboard, only receives the guake instance
        to be used in internal methods.
        """
        self.guake = guake
        self.accel_group = None  # see reload_accelerators
        self._lookup = None
        self._masks = None
        self.keymap = Gdk.Keymap.get_for_display(Gdk.Display.get_default())

        # Setup global keys
        self.globalhotkeys = {}
        globalkeys = ["show-hide", "show-focus"]
        for key in globalkeys:
            guake.settings.keybindingsGlobal.onChangedValue(key, self.reload_global)
            guake.settings.keybindingsGlobal.triggerOnChangedValue(
                guake.settings.keybindingsGlobal, key, None
            )

        def x(*args):
            prompt_cfg = self.guake.settings.general.get_int("prompt-on-close-tab")
            self.guake.get_notebook().delete_page_current(prompt=prompt_cfg)

        # Setup local keys
        self.keys = [
            ("toggle-fullscreen", self.guake.accel_toggle_fullscreen),
            ("new-tab", self.guake.accel_add),
            ("new-tab-home", self.guake.accel_add_home),
            ("new-tab-cwd", self.guake.accel_add_cwd),
            ("close-tab", x),
            ("rename-current-tab", self.guake.accel_rename_current_tab),
            ("previous-tab", self.guake.accel_prev),
            ("previous-tab-alt", self.guake.accel_prev),
            ("next-tab", self.guake.accel_next),
            ("next-tab-alt", self.guake.accel_next),
            ("clipboard-copy", self.guake.accel_copy_clipboard),
            ("clipboard-paste", self.guake.accel_paste_clipboard),
            ("select-all", self.guake.accel_select_all),
            ("quit", self.guake.accel_quit),
            ("zoom-in", self.guake.accel_zoom_in),
            ("zoom-in-alt", self.guake.accel_zoom_in),
            ("zoom-out", self.guake.accel_zoom_out),
            ("increase-height", self.guake.accel_increase_height),
            ("decrease-height", self.guake.accel_decrease_height),
            ("increase-transparency", self.guake.accel_increase_transparency),
            ("decrease-transparency", self.guake.accel_decrease_transparency),
            ("toggle-transparency", self.guake.accel_toggle_transparency),
            ("search-on-web", self.guake.search_on_web),
            ("open-link-under-terminal-cursor", self.guake.open_link_under_terminal_cursor),
            ("move-tab-left", self.guake.accel_move_tab_left),
            ("move-tab-right", self.guake.accel_move_tab_right),
            ("switch-tab1", self.guake.gen_accel_switch_tabN(0)),
            ("switch-tab2", self.guake.gen_accel_switch_tabN(1)),
            ("switch-tab3", self.guake.gen_accel_switch_tabN(2)),
            ("switch-tab4", self.guake.gen_accel_switch_tabN(3)),
            ("switch-tab5", self.guake.gen_accel_switch_tabN(4)),
            ("switch-tab6", self.guake.gen_accel_switch_tabN(5)),
            ("switch-tab7", self.guake.gen_accel_switch_tabN(6)),
            ("switch-tab8", self.guake.gen_accel_switch_tabN(7)),
            ("switch-tab9", self.guake.gen_accel_switch_tabN(8)),
            ("switch-tab10", self.guake.gen_accel_switch_tabN(9)),
            ("switch-tab-last", self.guake.accel_switch_tab_last),
            ("reset-terminal", self.guake.accel_reset_terminal),
            (
                "split-tab-vertical",
                lambda *args: self.guake.get_notebook()
                .get_current_terminal()
                .get_parent()
                .split_v()
                or True,
            ),
            (
                "split-tab-horizontal",
                lambda *args: self.guake.get_notebook()
                .get_current_terminal()
                .get_parent()
                .split_h()
                or True,
            ),
            (
                "close-terminal",
                lambda *args: self.guake.get_notebook().get_current_terminal().kill() or True,
            ),
            (
                "focus-terminal-up",
                (
                    lambda *args: FocusMover(self.guake.window).move_up(
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            (
                "focus-terminal-down",
                (
                    lambda *args: FocusMover(self.guake.window).move_down(
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            (
                "focus-terminal-right",
                (
                    lambda *args: FocusMover(self.guake.window).move_right(
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            (
                "focus-terminal-left",
                (
                    lambda *args: FocusMover(self.guake.window).move_left(
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            (
                "move-terminal-split-up",
                (
                    lambda *args: SplitMover.move_up(  # keep make style from concat this lines
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            (
                "move-terminal-split-down",
                (
                    lambda *args: SplitMover.move_down(  # keep make style from concat this lines
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            (
                "move-terminal-split-left",
                (
                    lambda *args: SplitMover.move_left(  # keep make style from concat this lines
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            (
                "move-terminal-split-right",
                (
                    lambda *args: SplitMover.move_right(  # keep make style from concat this lines
                        self.guake.get_notebook().get_current_terminal()
                    )
                    or True
                ),
            ),
            ("search-terminal", self.guake.accel_search_terminal),
            ("toggle-hide-on-lose-focus", self.guake.accel_toggle_hide_on_lose_focus),
        ]
        for key, _ in self.keys:
            guake.settings.keybindingsLocal.onChangedValue(key, self.reload_accelerators)
        self.reload_accelerators()

    def reload_global(self, settings, key, user_data):
        value = settings.get_string(key)
        if value == "disabled":
            return

        try:
            self.guake.hotkeys.unbind(self.globalhotkeys[key])
        except BaseException:
            pass

        self.globalhotkeys[key] = value
        if key == "show-hide":
            log.debug("reload_global: %r", value)
            if not self.guake.hotkeys.bind(value, self.guake.show_hide):
                keyval, mask = Gtk.accelerator_parse(value)
                label = Gtk.accelerator_get_label(keyval, mask)
                filename = pixmapfile("guake-notification.png")
                notifier.showMessage(
                    _("Guake Terminal"),
                    _(
                        "A problem happened when binding <b>%s</b> key.\n"
                        "Please use Guake Preferences dialog to choose another "
                        "key"
                    )
                    % label,
                    filename,
                )
        elif key == "show-focus" and not self.guake.hotkeys.bind(value, self.guake.show_focus):
            log.warning("can't bind show-focus key")

    def activate(self, window, event):
        """If keystroke matches a key binding, activate keybinding. Otherwise, allow
        keystroke to pass through."""
        key = event.keyval
        mod = event.state

        # Set keyval to the first available English keyboard value if character is non-latin
        # and a english keyval is found
        if event.keyval > 126:
            for i in self.keymap.get_entries_for_keycode(event.hardware_keycode)[2]:
                if 0 < i <= 126:
                    key = i
                    break

        if mod & Gdk.ModifierType.SHIFT_MASK:
            if key == Gdk.KEY_ISO_Left_Tab:
                key = Gdk.KEY_Tab
            else:
                key = Gdk.keyval_to_lower(key)
        else:
            keys = Gdk.keyval_convert_case(key)
            if key != keys[1]:
                key = keys[0]
                mod &= ~Gdk.ModifierType.SHIFT_MASK

        mask = mod & self._masks

        func = self._lookup[mask].get(key, None)
        if func:
            func()
            return True

        return False

    def reload_accelerators(self, *args):
        """Reassign an accel_group to guake main window and guake
        context menu and calls the load_accelerators method.
        """
        self._lookup = defaultdict(dict)
        self._masks = 0

        self.load_accelerators()
        self.guake.accel_group = self

    def load_accelerators(self):
        """Reads all gconf paths under /org/guake/keybindings/local
        and adds to the _lookup.
        """

        for binding, action in self.keys:
            key, mask = Gtk.accelerator_parse(
                self.guake.settings.keybindingsLocal.get_string(binding)
            )
            if key > 0:
                self._lookup[mask][key] = action
                self._masks |= mask
