# Copyright (c) 2017-2018 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
# 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 3 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/>.

from gi.repository import GLib, Gtk, Secret

from gettext import gettext as _

from passbook.define import App, Type


class WindowEvents:
    """
        Main window events
    """

    def __init__(self):
        """
            Get a new window
        """
        self.__timeout_configure = None
        self.__filter = ""
        self.__count_filtered = 0
        self.connect("adaptive-changed", self.__on_adaptive_changed)
        self.connect("can-go-back-changed", self.__on_can_go_back_changed)
        self.secrets_listbox.set_filter_func(self.__secrets_listbox_filter)
        self.filter_bar.connect("notify::search-mode-enabled",
                                self.__on_search_mode_enabled_notify)

    def setup(self):
        """
            Setup window position and size
        """
        self.__setup_size()
        self.__setup_pos()
        if App().settings.get_value("window-maximized"):
            # Lets resize happen
            GLib.idle_add(self.maximize)

#############
# CALLBACKS #
#############
    def on_configure_event(self, window, event):
        """
            Configure a timeout to save window size
            @param window as Window
            @param event as Gdk.Event
        """
        if self.__timeout_configure:
            GLib.source_remove(self.__timeout_configure)
            self.__timeout_configure = None
        self.__timeout_configure = GLib.timeout_add(
            250,
            self.__on_configure_timeout,
            window)

    def on_window_state_event(self, window, event):
        """
            Save window state
            @param window as Window
            @param event as Gdk.Event
        """
        App().settings.set_boolean("window-maximized",
                                   "GDK_WINDOW_STATE_MAXIMIZED" in
                                   event.new_window_state.value_names)

    def on_back_button_clicked(self, button):
        """
            Go back in adaptive window
            @param button as Gtk.Button
        """
        self.go_back()

    def on_search_button_toggled(self, button):
        """
            Show/hide filter bar
            @param button as Gtk.Button
        """
        self.color_button.hide()
        if not button.get_active():
            if self._adaptive_stack:
                self.go_back()
            else:
                self.set_initial_placeholder()
                self.stack.set_visible_child_name("placeholder")
        else:
            self.load_all()
            self.emit("can-go-back-changed", False)
        self.filter_bar.set_search_mode(button.get_active())

    def on_filter_entry_changed(self, entry):
        """
            Update secret listbox filter
            @param entry as Gtk.entry
        """
        self.__filter = entry.get_text()
        if self.__filter == "":
            self.groups_button.set_label(_("All"))
        else:
            self.groups_button.set_label(self.__filter)
        self.__count_filtered = 0
        self.secrets_listbox.invalidate_filter()
        if not self.search_button.get_active():
            pass
        elif self.__count_filtered == 0:
            self.stack.set_visible_child_name("placeholder")
            self.__set_search_placeholder(entry.get_text())
        else:
            self.stack.set_visible_child_name("secrets")

    def on_key_press_event(self, window, event):
        """
            Show filter entry
            @param window as Gtk.Window
            @param event as Gdk.Event
        """
        return self.filter_bar.handle_event(event)

    def on_add_password_clicked(self, *ignore):
        """
            Add a new password
        """
        from passbook.dialog_add_password import AddPasswordDialog
        dialog = AddPasswordDialog()
        dialog.run()

    def on_groups_button_clicked(self, button):
        """
            Show groups popover for filter entry
            @param button as Gtk.Button
        """
        from passbook.pop_groups import GroupsPopover
        collection = App().window.selected_row.collection
        popover = GroupsPopover(collection, self.filter_entry)
        popover.set_relative_to(button)
        popover.set_position(Gtk.PositionType.BOTTOM)
        popover.popup()

    def on_menu_clicked(self, button):
        """
            Show application menu
            @param button as Gtk.Button
        """
        from passbook.pop_menu import MenuPopover
        popover = MenuPopover()
        popover.set_relative_to(button)
        popover.popup()

    def on_collection_delete_confirm_button_clicked(self, button):
        """
            Show info bar to confirm deletion
            @param button as Gtk.Button
        """
        self.collection_infobar.response(Gtk.ResponseType.OK)

    def on_collection_delete_button_clicked(self, button):
        """
            Show info bar to confirm deletion
            @param button as Gtk.Button
        """
        row = App().window.selected_row
        if row is None:
            return
        if row.collection_type == Type.KEYRING:
            self.infobar_button.set_label(_("Delete collection"))
        elif row.collection_type == Type.KEEPASS:
            self.infobar_button.set_label(_("Detach collection"))
        self.collection_header_bar.hide()
        self.collection_infobar.set_revealed(True)

    def on_infobar_response(self, infobar, response_id):
        """
            Delete collection if user clicked remove button
        """
        def on_collection_removed(row):
            """
                Remove row
            """
            if row == self.selected_row:
                self.set_initial_placeholder()
                self.stack.set_visible_child_name("placeholder")
                self.color_button.hide()
            self.set_initial_view()
            collection_type = row.collection_type
            row.destroy()
            if collection_type == Type.KEYRING:
                if len(self.collections_listbox.get_children()) == 0:
                    self.collections_separator.hide()
            else:
                if len(self.keepass_listbox.get_children()) == 0:
                    self.keepass_separator.hide()
            self.collection_header_bar.hide()
        self.collection_header_bar.show()
        self.collection_infobar.set_revealed(False)
        if response_id == Gtk.ResponseType.OK:
            row = self.selected_row
            if row is not None:
                if row.collection_type == Type.KEYRING:
                    self._secrets_helper.remove_collection(
                                                    row.collection,
                                                    on_collection_removed,
                                                    row)
                else:
                    new_uris = []
                    uris = App().settings.get_value("keepass-uris")
                    for uri in uris:
                        if uri != row.collection.uri:
                            new_uris.append(uri)
                    App().settings.set_value("keepass-uris",
                                             GLib.Variant("as", new_uris))
                    on_collection_removed(row)

############
# PRIVATE  #
############
    def __set_search_placeholder(self, text):
        """
            Set placeholder for empty search
            @param text as str
        """
        self.placeholder_label.set_markup(
            _('<big>No result for "%s"</big>') % text)
        self.placeholder_image.set_from_icon_name(
            "edit-find-symbolic", Gtk.IconSize.DIALOG)

    def __secrets_listbox_filter(self, row):
        """
            Filter row based on label
            @param row as SecretsListBoxRow
        """
        if isinstance(row.item, Secret.Item):
            filtered = self.__filter in row.label
        else:
            filtered = self.__filter in row.label or\
                       self.__filter in row.item.kp_item.parentgroup.path
        if filtered:
            self.__count_filtered += 1
        self.collection_count_label.set_text(_("%s passwords") %
                                             self.__count_filtered)
        return filtered

    def __setup_size(self):
        """
            Set window size
            @param name as str
        """
        size_setting = App().settings.get_value("window-size")
        if len(size_setting) == 2 and\
           isinstance(size_setting[0], int) and\
           isinstance(size_setting[1], int):
            self.resize(size_setting[0], size_setting[1])

    def __setup_pos(self):
        """
            Set window position
            @param name as str
        """
        position_setting = App().settings.get_value("window-position")
        if len(position_setting) == 2 and\
           isinstance(position_setting[0], int) and\
           isinstance(position_setting[1], int):
            self.move(position_setting[0], position_setting[1])

    def __on_configure_timeout(self, window):
        """
            Save window position/size
            @param window as Window
        """
        self.__timeout_configure = None
        if not self.is_maximized():
            size = self.get_size()
            App().settings.set_value("window-size",
                                     GLib.Variant("ai", [size[0], size[1]]))

            position = window.get_position()
            App().settings.set_value("window-position",
                                     GLib.Variant("ai",
                                                  [position[0], position[1]]))

    def __on_adaptive_changed(self, window, b):
        """
            Show/hide back button
            @param window as Gtk.Window
            @param b as bool
        """
        if b:
            self.back_button.show()
        else:
            self.back_button.hide()

    def __on_can_go_back_changed(self, window, b):
        """
            Make button sensitive
            @param window as Gtk.Window
            @param b as bool
        """
        if b:
            self.back_button.set_sensitive(True)
        else:
            self.back_button.set_sensitive(False)

    def __on_search_mode_enabled_notify(self, search_bar, param):
        """
            Sync button state with search mode
            @param search_bar as Gtk.SeachBar
            @param param as GObject.property
        """
        search_bar_active = search_bar.get_property(param.name)
        search_button_active = self.search_button.get_active()
        if search_bar_active and not search_button_active:
            self.search_button.set_active(True)
        elif not search_bar_active and search_button_active:
            self.search_button.set_active(False)
