# -*- coding: utf-8 -*-
#
# gPodder - A media aggregator and podcast client
# Copyright (c) 2005-2018 The gPodder Team
#
# gPodder 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.
#
# gPodder 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/>.
#

import os
import shutil

from gi.repository import Gdk, Gtk

import gpodder
from gpodder import util
from gpodder.gtkui.base import GtkBuilderWidget

_ = gpodder.gettext


class BuilderWidget(GtkBuilderWidget):
    def __init__(self, parent, **kwargs):
        self._window_iconified = False

        GtkBuilderWidget.__init__(self, gpodder.ui_folders, gpodder.textdomain, parent, **kwargs)

        # Enable support for tracking iconified state
        if hasattr(self, 'on_iconify') and hasattr(self, 'on_uniconify'):
            self.main_window.connect('window-state-event',
                    self._on_window_state_event_iconified)

    def _on_window_state_event_iconified(self, widget, event):
        if event.new_window_state & Gdk.WindowState.ICONIFIED:
            if not self._window_iconified:
                self._window_iconified = True
                self.on_iconify()
        else:
            if self._window_iconified:
                self._window_iconified = False
                self.on_uniconify()

        return False

    def is_iconified(self):
        return self._window_iconified

    def notification(self, message, title=None, important=False, widget=None):
        util.idle_add(self.show_message, message, title, important, widget)

    def get_dialog_parent(self):
        """Return a Gtk.Window that should be the parent of dialogs"""
        return self.main_window

    def show_message(self, message, title=None, important=False, widget=None):
        if important:
            dlg = Gtk.MessageDialog(self.main_window, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK)
            if title:
                dlg.set_title(str(title))
                dlg.set_markup('<span weight="bold" size="larger">%s</span>\n\n%s' % (title, message))
            else:
                dlg.set_markup('<span weight="bold" size="larger">%s</span>' % (message))
            # make message copy/pastable
            for lbl in dlg.get_message_area():
                if isinstance(lbl, Gtk.Label):
                    lbl.set_selectable(True)
            dlg.run()
            dlg.destroy()
        else:
            gpodder.user_extensions.on_notification_show(title, message)

    def show_confirmation(self, message, title=None):
        dlg = Gtk.MessageDialog(self.main_window, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO)
        if title:
            dlg.set_title(str(title))
            dlg.set_markup('<span weight="bold" size="larger">%s</span>\n\n%s' % (title, message))
        else:
            dlg.set_markup('<span weight="bold" size="larger">%s</span>' % (message))
        response = dlg.run()
        dlg.destroy()
        return response == Gtk.ResponseType.YES

    def show_text_edit_dialog(self, title, prompt, text=None, empty=False,
            is_url=False, affirmative_text=Gtk.STOCK_OK):
        dialog = Gtk.Dialog(title, self.get_dialog_parent(),
            Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT)

        dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
        dialog.add_button(affirmative_text, Gtk.ResponseType.OK)

        dialog.set_default_size(300, -1)
        dialog.set_default_response(Gtk.ResponseType.OK)

        text_entry = Gtk.Entry()
        text_entry.set_activates_default(True)
        if text is not None:
            text_entry.set_text(text)
            text_entry.select_region(0, -1)

        if not empty:
            def on_text_changed(editable):
                can_confirm = (editable.get_text() != '')
                dialog.set_response_sensitive(Gtk.ResponseType.OK, can_confirm)
            text_entry.connect('changed', on_text_changed)
            if text is None:
                dialog.set_response_sensitive(Gtk.ResponseType.OK, False)

        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        hbox.set_border_width(10)
        hbox.set_spacing(10)
        hbox.pack_start(Gtk.Label(prompt, True, True, 0), False, False, 0)
        hbox.pack_start(text_entry, True, True, 0)
        dialog.vbox.pack_start(hbox, True, True, 0)

        dialog.show_all()
        response = dialog.run()
        result = text_entry.get_text()
        dialog.destroy()

        if response == Gtk.ResponseType.OK:
            return result
        else:
            return None

    def show_login_dialog(self, title, message, root_url=None, username=None, password=None,
            username_prompt=None, register_callback=None, register_text=None, ask_server=False):
        def toggle_password_visibility(_, entry):
            entry.set_visibility(not entry.get_visibility())

        if username_prompt is None:
            username_prompt = _('Username')

        if register_text is None:
            register_text = _('New user')

        dialog = Gtk.MessageDialog(
            self.main_window,
            Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.QUESTION,
            Gtk.ButtonsType.CANCEL)
        dialog.add_button(_('Login'), Gtk.ResponseType.OK)
        dialog.set_image(Gtk.Image.new_from_icon_name('dialog-password', Gtk.IconSize.DIALOG))
        dialog.set_title(_('Authentication required'))
        dialog.set_markup('<span weight="bold" size="larger">' + title + '</span>')
        dialog.format_secondary_markup(message)
        dialog.set_default_response(Gtk.ResponseType.OK)

        if register_callback is not None:
            dialog.add_button(register_text, Gtk.ResponseType.HELP)

        server_entry = Gtk.Entry()
        server_entry.set_tooltip_text(_('hostname or root URL (e.g. https://gpodder.net)'))
        username_entry = Gtk.Entry()
        password_entry = Gtk.Entry()

        server_entry.connect('activate', lambda w: username_entry.grab_focus())
        username_entry.connect('activate', lambda w: password_entry.grab_focus())
        password_entry.set_visibility(False)
        password_entry.set_activates_default(True)

        if root_url is not None:
            server_entry.set_text(root_url)
        if username is not None:
            username_entry.set_text(username)
        if password is not None:
            password_entry.set_text(password)

        table = Gtk.Table(3, 2)
        table.set_row_spacings(6)
        table.set_col_spacings(6)

        server_label = Gtk.Label()
        server_label.set_markup('<b>' + _('Server') + ':</b>')

        username_label = Gtk.Label()
        username_label.set_markup('<b>' + username_prompt + ':</b>')

        password_label = Gtk.Label()
        password_label.set_markup('<b>' + _('Password') + ':</b>')

        show_password_label = Gtk.Label()
        show_password = Gtk.CheckButton.new_with_label(_('Show Password'))
        show_password.connect('toggled', toggle_password_visibility, password_entry)

        label_entries = [(username_label, username_entry),
                         (password_label, password_entry),
                         (show_password_label, show_password)]

        if ask_server:
            label_entries.insert(0, (server_label, server_entry))

        for i, (label, entry) in enumerate(label_entries):
            label.set_alignment(0.0, 0.5)
            table.attach(label, 0, 1, i, i + 1, Gtk.AttachOptions.FILL, 0)
            table.attach(entry, 1, 2, i, i + 1)

        dialog.vbox.pack_end(table, True, True, 0)
        dialog.show_all()
        username_entry.grab_focus()
        response = dialog.run()

        while response == Gtk.ResponseType.HELP:
            register_callback()
            response = dialog.run()

        password_entry.set_visibility(True)
        root_url = server_entry.get_text()
        username = username_entry.get_text()
        password = password_entry.get_text()
        success = (response == Gtk.ResponseType.OK)

        dialog.destroy()

        if ask_server:
            return (success, (root_url, username, password))
        else:
            return (success, (username, password))

    def show_folder_select_dialog(self, initial_directory=None, title=_('Select destination')):
        if initial_directory is None:
            initial_directory = os.path.expanduser('~')

        dlg = Gtk.FileChooserDialog(title=title, parent=self.main_window, action=Gtk.FileChooserAction.SELECT_FOLDER)
        dlg.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
        dlg.add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.OK)

        dlg.set_do_overwrite_confirmation(True)
        dlg.set_current_folder(initial_directory)

        result = False
        folder = initial_directory
        if dlg.run() == Gtk.ResponseType.OK:
            result = True
            folder = dlg.get_current_folder()

        dlg.destroy()
        return (result, folder)


class TreeViewHelper(object):
    """Container for gPodder-specific TreeView attributes."""
    LAST_TOOLTIP = '_gpodder_last_tooltip'
    CAN_TOOLTIP = '_gpodder_can_tooltip'
    ROLE = '_gpodder_role'
    COLUMNS = '_gpodder_columns'

    # Enum for the role attribute
    ROLE_PODCASTS, ROLE_EPISODES, ROLE_DOWNLOADS = list(range(3))

    @classmethod
    def set(cls, treeview, role):
        setattr(treeview, cls.LAST_TOOLTIP, None)
        setattr(treeview, cls.CAN_TOOLTIP, True)
        setattr(treeview, cls.ROLE, role)

    @staticmethod
    def make_search_equal_func(gpodder_model):
        def func(model, column, key, iter):
            if model is None:
                return True
            key = key.lower()
            for column in gpodder_model.SEARCH_COLUMNS:
                if key in model.get_value(iter, column).lower():
                    return False
            return True
        return func

    @classmethod
    def register_column(cls, treeview, column):
        if not hasattr(treeview, cls.COLUMNS):
            setattr(treeview, cls.COLUMNS, [])

        columns = getattr(treeview, cls.COLUMNS)
        columns.append(column)

    @classmethod
    def get_columns(cls, treeview):
        return getattr(treeview, cls.COLUMNS, [])

    @staticmethod
    def make_popup_position_func(widget):
        """
        :return: suitable function to pass to Gtk.Menu.popup()
        It's used for instance when the popup trigger is the Menu key:
        it will position the menu on top of the selected row even if the mouse is elsewhere
        see http://lazka.github.io/pgi-docs/#Gtk-3.0/classes/Menu.html#Gtk.Menu.popup
        """
        def position_func(menu, *unused_args):
            _, x, y = widget.get_bin_window().get_origin()

            # If there's a selection, place the popup menu on top of
            # the first-selected row (otherwise in the top left corner)
            selection = widget.get_selection()
            model, paths = selection.get_selected_rows()
            if paths:
                path = paths[0]
                area = widget.get_cell_area(path, widget.get_column(0))
                x += area.x
                y += area.y

            return (x, y, True)
        return position_func
