# -*- coding: utf-8 -*-

# Copyright (C) 2004-2007 Johan Svedberg <johan@svedberg.com>

# This file is part of ontv.

# ontv 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.

# ontv 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 ontv; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

import struct
import os
from gettext import gettext as _

import gtk
import gconf
import gobject
import pango

import Configuration
import GUI
import Utils
from ChannelDialog import ChannelDialog
from Reminders import Reminder
from XMLTVAssistant import GRABBERS
from XMLTVAssistant import XMLTVAssistant
from ontv import NAME

class PreferencesDialog:
    def __init__(self, ontvapplet):
        self.ontvapplet = ontvapplet
        self.config = ontvapplet.config
        self.xmltvfile = ontvapplet.xmltvfile
        self.reminders = ontvapplet.reminders
        self.kb = ontvapplet.kb
        self.pw = ontvapplet.pw
        self.sd = ontvapplet.sd

        self.xmltvfile.connect("loading", self.__xmltvfile_loading)
        self.xmltvfile.connect("loading-done", self.__xmltvfile_loading_done)
        self.xmltvfile.connect("loaded-channel",
                               self.__xmltvfile_loaded_channel)
        self.xmltvfile.connect("downloading-logo", self.__downloading_logo)
        self.xmltvfile.connect("downloading-logo-done",
                               self.__downloading_logo_done)

        self.__get_widgets()

        self.__make_channels_treeview()
        self.__make_reminders_treeview()
        self.sd.create_search_treeview_menu(self.reminders_treeview.get_model())
        self.pw.set_reminders_model(self.reminders_treeview.get_model())

        self.channels_combobox_model = gtk.ListStore(str)
        self.channels_combobox_model.set_sort_func(0, self.__channels_model_sort_func, False)
        self.channels_combobox_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
        self.channels_comboboxentry.set_model(self.channels_combobox_model)
        self.channels_comboboxentry.set_text_column(0)
        self.channels_comboboxentry.child.set_text(_("All"))
        self.sd.channels_combobox.set_model(self.channels_combobox_model)

        self.__connect_widgets()

        self.__add_config_notifications()

        self.grabber_command_entry.set_text(self.config.grabber_command)
        self.output_file_entry.set_text(self.config.xmltv_file)
        self.current_programs_checkbutton.set_active(self.config.display_current_programs)
        self.upcoming_programs_checkbutton.set_active(self.config.display_upcoming_programs)
        if self.config.upcoming_programs_below:
            self.upcoming_programs_below_radiobutton.set_active(True)
        else:
            self.upcoming_programs_right_radiobutton.set_active(True)
        self.channels_image.set_from_stock(gtk.STOCK_DIALOG_INFO,
                                           gtk.ICON_SIZE_DIALOG)

        self.__auto_reload_id = None

    def __get_widgets(self):
        xml = gtk.glade.XML(GUI.glade_file, domain=NAME.lower())

        self.dialog = xml.get_widget("preferences_dialog")
        self.notebook = xml.get_widget("notebook")

        # General tab
        self.grabber_command_entry = xml.get_widget("grabber_command_entry")
        self.output_file_entry = xml.get_widget("output_file_entry")
        self.browse_button = xml.get_widget("browse_button")
        self.current_programs_checkbutton = xml.get_widget("current_programs_checkbutton")
        self.upcoming_programs_checkbutton = xml.get_widget("upcoming_programs_checkbutton")
        self.position_hbox = xml.get_widget("position_hbox")
        self.upcoming_programs_below_radiobutton = xml.get_widget("upcoming_programs_below_radiobutton")
        self.upcoming_programs_right_radiobutton = xml.get_widget("upcoming_programs_right_radiobutton")

        # Channels tab
        self.channels_treeview = xml.get_widget("channels_treeview")
        self.channels_image = xml.get_widget("channels_image")

        # Reminders tab
        self.reminders_treeview = xml.get_widget("reminders_treeview")
        self.program_entry = xml.get_widget("program_entry")
        self.channels_comboboxentry = xml.get_widget("channels_comboboxentry")
        self.notify_spinbutton = xml.get_widget("notify_spinbutton")
        self.add_reminder_button = xml.get_widget("add_reminder_button")
        self.update_reminder_button = xml.get_widget("update_reminder_button")
        self.remove_reminder_button = xml.get_widget("remove_reminder_button")

    def __make_channels_treeview(self):
        self.channels_model = gtk.ListStore(object)
        self.channels_model.set_sort_func(0, self.__channels_model_sort_func)
        self.channels_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
        self.channels_treeview.set_model(self.channels_model)

        crtoggle = gtk.CellRendererToggle()
        crtoggle.props.xpad = 6
        crtoggle.props.ypad = 3
        crtoggle.props.activatable = True
        crtoggle.connect("toggled", self.__channels_treeview_toggled,
                         self.channels_model)
        selected_column = gtk.TreeViewColumn("Selected", crtoggle)
        selected_column.set_cell_data_func(crtoggle,
                                           self.__crtoggle_cell_data_func)
        self.channels_treeview.append_column(selected_column)

        crpixbuf = gtk.CellRendererPixbuf()
        crpixbuf.props.xpad = 3
        crpixbuf.props.ypad = 3
        logo_column = gtk.TreeViewColumn("Logo", crpixbuf)
        logo_column.set_cell_data_func(crpixbuf, self.__crpixbuf_cell_data_func)
        self.channels_treeview.append_column(logo_column)

        crtext = gtk.CellRendererText()
        crtext.props.xpad = 3
        crtext.props.ypad = 3
        channel_column = gtk.TreeViewColumn("Channel", crtext)
        channel_column.set_cell_data_func(crtext, self.__crtext_cell_data_func)
        self.channels_treeview.append_column(channel_column)
        self.channels_treeview.set_search_column(2)
        self.channels_treeview.set_search_equal_func(self.__channels_treeview_search_equal)

        self.channels_treeview_menu = gtk.Menu()
        self.edit_channel_imi = gtk.ImageMenuItem(gtk.STOCK_EDIT)
        self.edit_channel_imi.child.set_markup_with_mnemonic(_("_Properties"))
        self.edit_channel_imi.show()
        self.channels_treeview_menu.append(self.edit_channel_imi)

    def __channels_model_sort_func(self, model, iter1, iter2, object=True):
        channel = model.get_value(iter1, 0)
        other_channel = model.get_value(iter2, 0)
        if object:
            return Utils.natcmp(channel.name.lower(), other_channel.name.lower())
        return Utils.natcmp(channel.lower(), other_channel.lower())

    def __channels_treeview_toggled(self, cell, path, model):
        channel = model[path][0]
        channel.selected = not channel.selected
        if channel.selected:
            self.listings.selected_channels.append(channel.name)
        else:
            self.listings.selected_channels.remove(channel.name)

        model.row_changed(path, model.get_iter(path))

        self.pw.update()
        self.listings.save()

    def __channels_treeview_search_equal(self, model, column, key, iter):
        channel = model.get_value(iter, 0)
        return key.lower() not in channel.name.lower()

    def __crtoggle_cell_data_func(self, column, cell, model, iter):
        channel = model.get_value(iter, 0)
        cell.props.active = channel.selected

    def __crpixbuf_cell_data_func(self, column, cell, model, iter):
        channel = model.get_value(iter, 0)
        cell.props.pixbuf = channel.logo

    def __crtext_cell_data_func(self, column, cell, model, iter):
        channel = model.get_value(iter, 0)
        markup = "<b>%s</b>"
        cell.props.markup = markup % channel.markup_escaped_name

    def __make_reminders_treeview(self):
        reminders_model = gtk.ListStore(object)
        self.reminders_treeview.set_model(reminders_model)

        program_crt = gtk.CellRendererText()
        program_crt.props.xpad = 3
        program_crt.props.ypad = 3
        program_column = gtk.TreeViewColumn(_("Program"), program_crt)
        program_column.set_cell_data_func(program_crt,
                                          self.__program_crt_cell_data_func)
        self.reminders_treeview.append_column(program_column)

        channel_crt = gtk.CellRendererText()
        channel_crt.props.xpad = 3
        channel_crt.props.ypad = 3
        channel_column = gtk.TreeViewColumn(_("Channel"), channel_crt)
        channel_column.set_cell_data_func(channel_crt,
                                          self.__channel_crt_cell_data_func)
        self.reminders_treeview.append_column(channel_column)

        notify_crt = gtk.CellRendererText()
        notify_crt.props.xpad = 3
        notify_crt.props.ypad = 3
        notify_column = gtk.TreeViewColumn(_("Notification time"), notify_crt)
        notify_column.set_cell_data_func(notify_crt,
                                         self.__notify_crt_cell_data_func)
        self.reminders_treeview.append_column(notify_column)

        for reminder in self.reminders.reminders:
            reminders_model.append([reminder])

    def __program_crt_cell_data_func(self, column, cell, model, iter):
        reminder = model.get_value(iter, 0)
        cell.props.text = reminder.program

    def __channel_crt_cell_data_func(self, column, cell, model, iter):
        reminder = model.get_value(iter, 0)
        cell.props.markup = reminder.channel

    def __notify_crt_cell_data_func(self, column, cell, model, iter):
        reminder = model.get_value(iter, 0)
        cell.props.text = reminder.notify_time

    def __connect_widgets(self):
        self.dialog.connect("delete-event", self.__dialog_delete)
        self.dialog.connect("response", self.__dialog_response)

        # General tab
        self.grabber_command_entry.connect("changed", self.__grabber_command_entry_changed)
        self.grabber_command_entry.connect("activate", self.__grabber_command_entry_activated)
        self.output_file_entry.connect("changed", self.__output_file_entry_changed)
        self.output_file_entry.connect("activate", self.__output_file_entry_activated)
        self.browse_button.connect("clicked", self.__browse_button_clicked)
        self.current_programs_checkbutton.connect("toggled", self.__current_programs_checkbutton_toggled)
        self.upcoming_programs_checkbutton.connect("toggled", self.__upcoming_programs_checkbutton_toggled)
        self.upcoming_programs_below_radiobutton.connect("toggled", self.__upcoming_programs_below_radiobutton_toggled)

        # Channels tab
        self.channels_treeview.connect("row-activated",
                                       self.__channels_treeview_row_activated)
        self.channels_treeview.connect("button-press-event",
                                       self.__channels_treeview_button_press,
                                       self.channels_treeview_menu)
        self.edit_channel_imi.connect("activate",
                                      self.__edit_channel_imi_activate)

        # Reminders tab
        self.reminders_treeview.connect("key-press-event", self.__reminders_treeview_key_press_event)
        selection = self.reminders_treeview.get_selection()
        selection.connect("changed",
                          self.__reminders_treeview_selection_changed)
        self.program_entry.connect("changed", self.__program_entry_changed)
        self.program_entry.connect("activate",
                                   self.__update_reminder_button_clicked)
        self.channels_comboboxentry.connect("changed", self.__channels_comboboxentry_changed)
        self.channels_comboboxentry.child.connect("activate", self.__update_reminder_button_clicked)
        self.notify_spinbutton.connect("value_changed",
                                       self.__notify_spinbutton_value_changed)
        self.notify_spinbutton.connect("activate",
                                       self.__update_reminder_button_clicked)
        self.add_reminder_button.connect("clicked",
                                         self.__add_reminder_button_clicked)
        self.update_reminder_button.connect("clicked", self.__update_reminder_button_clicked)
        self.remove_reminder_button.connect("clicked", self.__remove_reminder_button_clicked)

    def __dialog_response(self, dialog, response):
        self.dialog.hide()

    def __dialog_delete(self, dialog, event):
        return True

    def __grabber_command_entry_changed(self, entry):
        self.config.grabber_command = entry.get_text()

    def __grabber_command_entry_activated(self, entry):
        self.xmltvfile.download()

    def __output_file_entry_changed(self, entry):
        self.config.xmltv_file = entry.get_text()

    def __output_file_entry_activated(self, entry):
        self.xmltvfile.load()

    def __browse_button_clicked(self, button):
        xml_filter = gtk.FileFilter()
        xml_filter.set_name(_("XML files"))
        xml_filter.add_pattern("text/xml")
        filters = [xml_filter]
        fd = GUI.FileChooserDialog(_("Select XMLTV file..."), filters)
        response = fd.run()
        if response == gtk.RESPONSE_OK:
            self.config.xmltv_file = fd.get_filename()
            self.xmltvfile.load()
            fd.hide()
        elif response == gtk.RESPONSE_CANCEL:
            fd.hide()

    def __current_programs_checkbutton_toggled(self, checkbutton):
        self.config.display_current_programs = checkbutton.get_active()

    def __upcoming_programs_checkbutton_toggled(self, checkbutton):
        active = checkbutton.get_active()
        self.config.display_upcoming_programs = active
        self.position_hbox.set_sensitive(active)

    def __upcoming_programs_below_radiobutton_toggled(self, radiobutton):
        active = radiobutton.get_active()
        self.config.upcoming_programs_below = active

    def __channels_treeview_row_activated(self, treeview, path, column):
        self.__channels_treeview_toggled(None, path, self.channels_model)

    def __channels_treeview_button_press(self, treeview, event, menu):
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
            menu.popup(None, None, None, event.button, event.time)

    def __edit_channel_imi_activate(self, menuitem):
        channel = GUI.get_selected_value(self.channels_treeview)
        if channel:
            cd = ChannelDialog(channel, self)
            cd.show()

    def __reminders_treeview_selection_changed(self, selection):
        (reminders_model, reminders_iter) = selection.get_selected()
        if reminders_iter:
            self.remove_reminder_button.set_sensitive(True)
            reminder = reminders_model.get_value(reminders_iter, 0)
            self.program_entry.set_text(reminder.program)
            channel = pango.parse_markup(reminder.channel, u"\x00")[1]
            self.channels_comboboxentry.child.set_text(channel)
            self.notify_spinbutton.set_value(reminder.notify_time)
        else:
            self.program_entry.set_text("")
            self.channels_comboboxentry.child.set_text("")
            self.notify_spinbutton.set_value(5)
            self.add_reminder_button.set_sensitive(False)
            self.update_reminder_button.set_sensitive(False)
            self.remove_reminder_button.set_sensitive(False)

    def __reminders_treeview_key_press_event(self, treeview, event):
        if event.keyval == gtk.keysyms.Delete:
            self.remove_reminder_button.emit("clicked")

    def __program_entry_changed(self, entry):
        if len(entry.get_text()) > 0:
            self.__set_reminder_buttons_sensitivity()
        else:
            self.add_reminder_button.set_sensitive(False)
            self.update_reminder_button.set_sensitive(False)

    def __channels_comboboxentry_changed(self, comboboxentry):
        self.__set_reminder_buttons_sensitivity()

    def __notify_spinbutton_value_changed(self, spinbutton):
        self.__set_reminder_buttons_sensitivity()

    def __set_reminder_buttons_sensitivity(self):
        if not self.__get_reminder() in self.reminders.reminders:
            self.add_reminder_button.set_sensitive(True)
            if GUI.has_selection(self.reminders_treeview):
                self.update_reminder_button.set_sensitive(True)
        else:
            self.add_reminder_button.set_sensitive(False)
            self.update_reminder_button.set_sensitive(False)

    def __add_reminder_button_clicked(self, button):
        reminder = self.__get_reminder()
        if self.reminders.add(reminder):
            reminders_model = self.reminders_treeview.get_model()
            reminders_model.append([reminder])
            GUI.queue_resize(self.reminders_treeview)
            self.reminders.save()
            self.add_reminder_button.set_sensitive(False)
            self.sd.search_treeview.get_selection().emit("changed")
            self.pw.update()

    def __get_reminder(self):
        program = self.program_entry.get_text()
        channel = self.channels_comboboxentry.child.get_text()
        channel = gobject.markup_escape_text(channel)
        notify_time = self.notify_spinbutton.get_value_as_int()
        return Reminder(program, channel, notify_time)

    def __update_reminder_button_clicked(self, button):
        selection = self.reminders_treeview.get_selection()
        (reminders_model, reminders_iter) = selection.get_selected()
        if not reminders_iter:
            self.add_reminder_button.clicked()
        else:
            current_reminder = reminders_model.get_value(reminders_iter, 0)
            new_reminder = self.__get_reminder()
            if not current_reminder is new_reminder:
                self.reminders.update(current_reminder, new_reminder)
                reminders_model.set_value(reminders_iter, 0, new_reminder)
                GUI.queue_resize(self.reminders_treeview)
                self.reminders.save()
                self.add_reminder_button.set_sensitive(False)
                self.update_reminder_button.set_sensitive(False)
                self.sd.search_treeview.get_selection().emit("changed")
                self.pw.update()

    def __remove_reminder_button_clicked(self, button):
        selection = self.reminders_treeview.get_selection()
        (reminders_model, reminders_iter) = selection.get_selected()
        if reminders_iter:
            reminder = reminders_model.get_value(reminders_iter, 0)
            if self.reminders.remove(reminder):
                reminders_model.remove(reminders_iter)
                GUI.queue_resize(self.reminders_treeview)
                self.reminders.save()
                self.sd.search_treeview.get_selection().emit("changed")

    def __add_config_notifications(self):
        self.config.add_notify(Configuration.KEY_GRABBER_COMMAND,
                               self.__grabber_command_key_changed)
        self.config.add_notify(Configuration.KEY_XMLTV_FILE,
                               self.__xmltv_file_key_changed)
        self.config.add_notify(Configuration.KEY_SHOW_WINDOW_HOTKEY,
                               self.__show_window_hotkey_key_changed)
        self.config.add_notify(Configuration.KEY_SHOW_SEARCH_PROGRAM_HOTKEY,
                               self.__show_search_program_hotkey_key_changed)
        self.config.add_notify(Configuration.KEY_DISPLAY_CURRENT_PROGRAMS,
                               self.__display_current_programs_key_changed)
        self.config.add_notify(Configuration.KEY_DISPLAY_UPCOMING_PROGRAMS,
                               self.__display_upcoming_programs_key_changed)
        self.config.add_notify(Configuration.KEY_UPCOMING_PROGRAMS_BELOW,
                               self.__upcoming_programs_below_key_changed)

    def __grabber_command_key_changed(self, client, cnxn_id, entry, data):
        if entry.value.type == gconf.VALUE_STRING:
            command = entry.value.to_string()
            self.grabber_command_entry.set_text(command)

    def __xmltv_file_key_changed(self, client, cnxn_id, entry, data):
        if entry.value.type == gconf.VALUE_STRING:
            xmltv_file = entry.value.to_string()
            self.xmltvfile.props.path = xmltv_file
            self.output_file_entry.set_text(xmltv_file)

    def __show_window_hotkey_key_changed(self, client, cnxn_id, entry, data):
        if not entry.value:
            return
        elif entry.value.type == gconf.VALUE_STRING:
            # Unbind the current binding if any
            if self.kb.props.show_window_hotkey != "":
                self.kb.unbind(self.kb.props.show_window_hotkey)

            value = entry.value.to_string()
            if value != "":
                self.kb.props.show_window_hotkey = value
        else:
            pass # TODO: Output error msg

    def __show_search_program_hotkey_key_changed(self, client, cnxn_id, entry,
                                                 data):
        if not entry.value:
            return
        elif entry.value.type == gconf.VALUE_STRING:
            # Unbind the current binding if any
            if self.kb.props.show_search_program_hotkey != "":
                self.kb.unbind(self.kb.props.show_search_program_hotkey)

            value = entry.value.to_string()
            if value != "":
                self.kb.props.show_search_program_hotkey = value
        else:
            pass # TODO: Output error msg

    def __display_current_programs_key_changed(self, client, cnxn_id, entry,
                                               data):
        if not entry.value:
            self.current_programs_checkbutton.set_active(True)
        elif entry.value.type == gconf.VALUE_BOOL:
            value = entry.value.get_bool()
            self.current_programs_checkbutton.set_active(value)
            if value:
                self.pw.cpt.show()
            else:
                self.pw.cpt.hide()
        else:
            self.current_programs_checkbutton.set_active(True)

    def __display_upcoming_programs_key_changed(self, client, cnxn_id, entry,
                                                data):
        if not entry.value:
            self.upcoming_programs_checkbutton.set_active(True)
        elif entry.value.type == gconf.VALUE_BOOL:
            value = entry.value.get_bool()
            self.upcoming_programs_checkbutton.set_active(value)
            if value:
                self.pw.upt.show()
            else:
                self.pw.upt.hide()
        else:
            self.upcoming_programs_checkbutton.set_active(True)

    def __upcoming_programs_below_key_changed(self, client, cnxn_id, entry,
                                              data):
        if not entry.value:
            self.upcoming_programs_below_radiobutton.set_active(True)
        elif entry.value.type == gconf.VALUE_BOOL:
            value = entry.value.get_bool()
            self.upcoming_programs_below_radiobutton.set_active(value)
            self.pw.position_upcoming_programs(value)
        else:
            self.upcoming_programs_below_radiobutton.set_active(True)

    def __xmltvfile_loading(self, xmltvfile):
        self.channels_model.clear()
        self.channels_combobox_model.clear()

    def __xmltvfile_loading_done(self, xmltvfile, listings):
        if listings:
            self.listings = listings
            self.channels_combobox_model.append([_("All")])
            self.sd.set_all_as_combo_active(self.sd.channels_combobox)

    def __xmltvfile_loaded_channel(self, xmltvfile, channel):
        self.channels_model.append([channel])
        self.channels_combobox_model.append([channel])

    def __channel_changed(self, xmltvfile, channel):
        channels_iter = self.channels_model.get_iter_first()
        for row in self.channels_model:
            if row[0] is channel:
                channels_path = self.channels_model.get_path(channels_iter)
                self.channels_model.row_changed(channels_path, channels_iter)
                break
            channels_iter = self.channels_model.iter_next(channels_iter)

    def __downloading_logo(self, xmltvfile, channel):
        self.__channel_changed(xmltvfile, channel)

    def __downloading_logo_done(self, xmltvfile, channel):
        self.__channel_changed(xmltvfile, channel)

    def show(self, uicomponent=None, verb=None):
        self.dialog.show()

# vim: set sw=4 et sts=4 tw=79 fo+=l:
