# -*- coding: utf-8 -*-
#
# gPodder - A media aggregator and podcast client
# Copyright (c) 2005-2011 Thomas Perl and 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 gtk
import os
import shutil

import gpodder

_ = gpodder.gettext

from gpodder import util

from gpodder.gtkui.base import GtkBuilderWidget

from gpodder.gtkui.widgets import NotificationWindow

# For gPodder on Fremantle
class Orientation(object):
    LANDSCAPE, PORTRAIT = range(2)

try:
    import pynotify
    if not pynotify.init('gPodder'):
        pynotify = None
except ImportError:
    pynotify = None

class BuilderWidget(GtkBuilderWidget):
    finger_friendly_widgets = []

    def __init__(self, parent, **kwargs):
        self._window_iconified = False
        self._window_visible = False

        # Enable support for portrait mode changes on Maemo 5
        if gpodder.ui.fremantle:
            self._window_orientation = Orientation.LANDSCAPE

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

        # Enable support for portrait mode changes on Maemo 5
        if gpodder.ui.fremantle:
            self.main_window.connect('configure-event', \
                    self._on_configure_event_maemo)

        # Enable support for fullscreen toggle key on Maemo 4
        if gpodder.ui.diablo:
            self._maemo_fullscreen = False
            self._maemo_fullscreen_chain = None
            self.main_window.connect('key-press-event', \
                    self._on_key_press_event_maemo)
            self.main_window.connect('window-state-event', \
                    self._on_window_state_event_maemo)

        # 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)

        # Enable support for tracking visibility state
        if gpodder.ui.desktop:
            self.main_window.connect('visibility-notify-event', \
                        self._on_window_state_event_visibility)

        # Set widgets to finger-friendly mode if on Maemo
        for widget_name in self.finger_friendly_widgets:
            if hasattr(self, widget_name):
                self.set_finger_friendly(getattr(self, widget_name))

        if parent is not None:
            self.main_window.set_transient_for(parent)

            if gpodder.ui.desktop:
                if hasattr(self, 'center_on_widget'):
                    (x, y) = parent.get_position()
                    a = self.center_on_widget.allocation
                    (x, y) = (x + a.x, y + a.y)
                    (w, h) = (a.width, a.height)
                    (pw, ph) = self.main_window.get_size()
                    self.main_window.move(x + w/2 - pw/2, y + h/2 - ph/2)
            elif gpodder.ui.diablo:
                self._maemo_fullscreen_chain = parent
                if parent.window.get_state() & gtk.gdk.WINDOW_STATE_FULLSCREEN:
                    self.main_window.fullscreen()
                    self._maemo_fullscreen = True

    def _handle_menu_bar(self, menu_bar):
        for child in menu_bar.get_children():
            self.set_finger_friendly(child)


    def _handle_button(self, button):
        self.set_finger_friendly(button)

    def on_window_orientation_changed(self, orientation):
        """Override this method to relayout a window for portrait mode."""
        pass

    def _on_configure_event_maemo(self, window, event):
        if float(event.width)/float(event.height) < 1:
            orientation = Orientation.PORTRAIT
        else:
            orientation = Orientation.LANDSCAPE

        if orientation != self._window_orientation:
            self.on_window_orientation_changed(orientation)
            self._window_orientation = orientation

    def _on_key_press_event_maemo(self, widget, event):
        window_type = widget.get_type_hint()
        if window_type != gtk.gdk.WINDOW_TYPE_HINT_NORMAL:
            return False

        if event.keyval == gtk.keysyms.F6:
            if self._maemo_fullscreen:
                if self._maemo_fullscreen_chain is not None:
                    self._maemo_fullscreen_chain.unfullscreen()
                self.main_window.unfullscreen()
                if not self.use_fingerscroll:
                    self.main_window.set_border_width(0)
            else:
                if self._maemo_fullscreen_chain is not None:
                    self._maemo_fullscreen_chain.fullscreen()
                self.main_window.fullscreen()
                if not self.use_fingerscroll:
                    self.main_window.set_border_width(12)
            return True
        else:
            return False

    def _on_window_state_event_maemo(self, widget, event):
        if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
            self._maemo_fullscreen = True
        else:
            self._maemo_fullscreen = False

        return False

    def _on_window_state_event_visibility(self, widget, event):
        if event.state & gtk.gdk.VISIBILITY_FULLY_OBSCURED:
            self._window_visible = False
        else:
            self._window_visible = True

        return False

    def _on_window_state_event_iconified(self, widget, event):
        if event.new_window_state & gtk.gdk.WINDOW_STATE_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

        On non-Fremantle environments, this simply returns the main
        window of this dialog. On Fremantle, it tries to determine
        the topmost window from the window stack if possible.
        """
        if gpodder.ui.fremantle:
            # Determine the topmost visible window and attach the
            # message to that window to avoid Maemo Bug 10030
            import hildon
            stack = hildon.WindowStack.get_default()
            visible_windows = stack.get_windows()
            if visible_windows:
                parent_window = visible_windows[0]

        return self.main_window

    def show_message(self, message, title=None, important=False, widget=None):
        if gpodder.ui.diablo:
            import hildon
            if important:
                try:
                    dlg = hildon.Note('information', (self.main_window, message))
                except TypeError:
                    if title is None:
                        message = message
                    else:
                        message = '%s\n%s' % (title, message)
                    dlg = hildon.hildon_note_new_information(self.main_window, \
                            message)
                dlg.run()
                dlg.destroy()
            else:
                if title is None:
                    title = 'gPodder'
                pango_markup = '<b>%s</b>\n<small>%s</small>' % (title, message)
                try:
                    hildon.hildon_banner_show_information_with_markup(gtk.Label(''), None, pango_markup)
                except TypeError:
                    # We're probably running the Diablo UI on Maemo 5 :)
                    hildon.hildon_banner_show_information(self.main_window, \
                            '', message)
        elif gpodder.ui.fremantle:
            import hildon
            if important:
                if title is None:
                    message = message
                else:
                    message = '%s\n%s' % (title, message)

                dlg = hildon.hildon_note_new_information( \
                        self.get_dialog_parent(), \
                        message)
                dlg.run()
                dlg.destroy()
            else:
                hildon.hildon_banner_show_information(self.get_dialog_parent(), \
                        '', message)
        else:
            # XXX: Dirty hack to get access to the gPodder-specific config object
            config = getattr(self, '_config', getattr(self, 'config', None))

            if important:
                dlg = gtk.MessageDialog(self.main_window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_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))
                dlg.run()
                dlg.destroy()
            elif config is not None and config.enable_notifications:
                if pynotify is not None:
                    if title is None:
                        title = 'gPodder'
                    notification = pynotify.Notification(title, message,\
                            gpodder.icon_file)
                    try:
                        notification.show()
                    except:
                        # See http://gpodder.org/bug/966
                        pass
                elif widget and isinstance(widget, gtk.Widget):
                    if not widget.window:
                        widget = self.main_window
                    elif not gpodder.win32:
                        widget = self.main_window
                    notification = NotificationWindow(message, title, important=False, widget=widget)
                    notification.show_timeout()

    def set_finger_friendly(self, widget):
        """
        If we are on Maemo, we carry out the necessary
        operations to turn a widget into a finger-friendly
        one, depending on which type of widget it is (i.e.
        buttons will have more padding, TreeViews a thick
        scrollbar, etc..)
        """
        if widget is None:
            return None

        if gpodder.ui.maemo or self.use_fingerscroll:
            if isinstance(widget, gtk.Misc):
                widget.set_padding(0, 5)
            elif isinstance(widget, gtk.Button):
                for child in widget.get_children():
                    if isinstance(child, gtk.Alignment):
                        child.set_padding(5, 5, 5, 5)
                    else:
                        child.set_padding(5, 5)
            elif isinstance(widget, gtk.TreeView) or isinstance(widget, gtk.TextView):
                parent = widget.get_parent()
            elif isinstance(widget, gtk.MenuItem):
                for child in widget.get_children():
                    self.set_finger_friendly(child)
                submenu = widget.get_submenu()
                if submenu is not None:
                    for child in submenu.get_children():
                        self.set_finger_friendly(child)
            elif isinstance(widget, gtk.Menu):
                for child in widget.get_children():
                    self.set_finger_friendly(child)

        return widget

    def show_confirmation(self, message, title=None):
        if gpodder.ui.desktop:
            dlg = gtk.MessageDialog(self.main_window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_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.RESPONSE_YES
        elif gpodder.ui.diablo:
            import hildon
            try:
                dlg = hildon.Note('confirmation', (self.main_window, message))
            except TypeError:
                # Kludgy workaround: We're running the Diablo UI on Maemo 5 :)
                dlg = hildon.hildon_note_new_confirmation(self.main_window, \
                        message)
            response = dlg.run()
            dlg.destroy()
            return response == gtk.RESPONSE_OK
        elif gpodder.ui.fremantle:
            import hildon
            dlg = hildon.hildon_note_new_confirmation(self.get_dialog_parent(), \
                    message)
            response = dlg.run()
            dlg.destroy()
            return response == gtk.RESPONSE_OK
        else:
            raise Exception('Unknown interface type')

    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.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)

        if not gpodder.ui.fremantle:
            dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)

        dialog.add_button(affirmative_text, gtk.RESPONSE_OK)

        dialog.set_has_separator(False)
        if gpodder.ui.desktop:
            dialog.set_default_size(300, -1)
        else:
            dialog.set_default_size(650, -1)
        dialog.set_default_response(gtk.RESPONSE_OK)

        if gpodder.ui.fremantle:
            import hildon
            text_entry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
            if is_url:
                # Disable word capitalization and word completion when
                # requesting an URL to be entered (see Maemo bug 5184)
                text_entry.set_property('hildon-input-mode', \
                        gtk.HILDON_GTK_INPUT_MODE_FULL)
        elif gpodder.ui.diablo:
            text_entry = gtk.Entry()
            text_entry.set_property('hildon-input-mode', \
                    'HILDON_GTK_INPUT_MODE_FULL')
        else:
            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.RESPONSE_OK, can_confirm)
            text_entry.connect('changed', on_text_changed)
            if text is None:
                dialog.set_response_sensitive(gtk.RESPONSE_OK, False)

        hbox = gtk.HBox()
        if not gpodder.ui.fremantle:
            hbox.set_border_width(10)
        hbox.set_spacing(10)
        hbox.pack_start(gtk.Label(prompt), False, False)
        hbox.pack_start(text_entry, True, True)
        dialog.vbox.pack_start(hbox, True, True)

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

        if response == gtk.RESPONSE_OK:
            return result
        else:
            return None

    def show_login_dialog(self, title, message, username=None, password=None, username_prompt=_('Username'), register_callback=None):
        """ An authentication dialog based on
                http://ardoris.wordpress.com/2008/07/05/pygtk-text-entry-dialog/ """

        if gpodder.ui.fremantle:
            dialog = gtk.Dialog(title, self.main_window,
                    gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                    (str(_('Login')), gtk.RESPONSE_OK))
            label = gtk.Label(message)
            label.set_alignment(0., .5)
            label.set_padding(0, 5)
            dialog.vbox.add(label)
        else:
            dialog = gtk.MessageDialog(
                self.main_window,
                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                gtk.MESSAGE_QUESTION,
                gtk.BUTTONS_CANCEL)
            dialog.add_button(_('Login'), gtk.RESPONSE_OK)
            dialog.set_image(gtk.image_new_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_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.RESPONSE_OK)

        if register_callback is not None:
            dialog.add_button(_('New user'), gtk.RESPONSE_HELP)

        if gpodder.ui.fremantle:
            import hildon
            username_entry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
            password_entry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
        else:
            username_entry = gtk.Entry()
            password_entry = gtk.Entry()

        # Disable input capitalization for the login fields
        if gpodder.ui.fremantle:
            username_entry.set_property('hildon-input-mode', \
                        gtk.HILDON_GTK_INPUT_MODE_FULL)
            password_entry.set_property('hildon-input-mode', \
                        gtk.HILDON_GTK_INPUT_MODE_FULL)
        elif gpodder.ui.diablo:
            username_entry.set_property('hildon-input-mode', \
                        'HILDON_GTK_INPUT_MODE_FULL')
            password_entry.set_property('hildon-input-mode', \
                        'HILDON_GTK_INPUT_MODE_FULL')

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

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

        table = gtk.Table(2, 2)
        table.set_row_spacings(6)
        table.set_col_spacings(6)

        username_label = gtk.Label()
        username_label.set_markup('<b>' + username_prompt + ':</b>')
        username_label.set_alignment(0.0, 0.5)
        table.attach(username_label, 0, 1, 0, 1, gtk.FILL, 0)
        table.attach(username_entry, 1, 2, 0, 1)

        password_label = gtk.Label()
        password_label.set_markup('<b>' + _('Password') + ':</b>')
        password_label.set_alignment(0.0, 0.5)
        table.attach(password_label, 0, 1, 1, 2, gtk.FILL, 0)
        table.attach(password_entry, 1, 2, 1, 2)

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

        while response == gtk.RESPONSE_HELP:
            register_callback()
            response = dialog.run()

        password_entry.set_visibility(True)
        username = username_entry.get_text()
        password = password_entry.get_text()
        success = (response == gtk.RESPONSE_OK)

        dialog.destroy()

        return (success, (username, password))

    def show_copy_dialog(self, src_filename, dst_filename=None, dst_directory=None, title=_('Select destination')):
        if dst_filename is None:
            dst_filename = src_filename

        if dst_directory is None:
            dst_directory = os.path.expanduser('~')

        base, extension = os.path.splitext(src_filename)

        if not dst_filename.endswith(extension):
            dst_filename += extension

        if gpodder.ui.desktop or gpodder.ui.fremantle:
            # FIXME: Hildonization for Fremantle
            dlg = gtk.FileChooserDialog(title=title, parent=self.main_window, action=gtk.FILE_CHOOSER_ACTION_SAVE)
            dlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
            dlg.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_OK)
        elif gpodder.ui.diablo:
            import hildon
            dlg = hildon.FileChooserDialog(self.main_window, gtk.FILE_CHOOSER_ACTION_SAVE)

        dlg.set_do_overwrite_confirmation(True)
        dlg.set_current_name(os.path.basename(dst_filename))
        dlg.set_current_folder(dst_directory)

        result = False
        folder = dst_directory
        if dlg.run() == gtk.RESPONSE_OK:
            result = True
            dst_filename = dlg.get_filename()
            folder = dlg.get_current_folder()
            if not dst_filename.endswith(extension):
                dst_filename += extension

            shutil.copyfile(src_filename, dst_filename)

        dlg.destroy()
        return (result, folder)

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

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

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

    @classmethod
    def save_button_press_event(cls, treeview, event):
        setattr(treeview, cls.BUTTON_PRESS, (event.x, event.y))
        return True

    @classmethod
    def get_button_press_event(cls, treeview):
        return getattr(treeview, cls.BUTTON_PRESS)

    @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

