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

from gettext import gettext as _
import gettext

import gtk
import gtk.gdk
import gobject
import pango

import GUI
from Configuration import Configuration
from GUI import ProgramContextMenu
from Reminders import Reminders, Reminder
from Notification import Notification

TIMEOUT = 60000

class ProgramTable(gtk.VBox):
    def __init__(self, listings, title):
        title= "<span size=\"larger\" weight=\"bold\">%s</span>" % title
        gtk.VBox.__init__(self, False, 6)
        self.listings = listings
        self.tooltips = gtk.Tooltips()

        self.label = gtk.Label(title)
        self.label.set_use_markup(True)
        self.label.set_padding(3, 3)

        label_box = gtk.HBox(False, 6)
        label_box.pack_start(self.label, False, False, 0)

        event_box = gtk.EventBox()
        event_box.add(label_box)
        style = event_box.get_style()
        event_box_bg = style.bg[gtk.STATE_INSENSITIVE]
        event_box.modify_bg(gtk.STATE_NORMAL, event_box_bg)

        self.table = gtk.Table()
        self.table.set_border_width(6)
        self.table.props.row_spacing = 2
        self.table.props.column_spacing = 6

        self.pack_start(event_box, False)
        self.pack_start(self.table)

        self.filled_programs = []
        self.update_ids = []

        self.reminders_model = None

        gobject.timeout_add(TIMEOUT, self.__refresh)

    def __refresh(self):
        self.update()
        return True

    def update(self, force_update=False):
        if len(self.listings.selected_channels) == 0:
            self.__resize(1)
            self.__clear()

            label = gtk.Label(_("No channels selected"))
            label.set_alignment(0.0, 0.5)
            label.show()
            self._attach(label, 0, 0, xoptions=gtk.SHRINK|gtk.FILL)

            self.filled_programs = None
        else:
            self.set_programs()
            if self.programs != self.filled_programs or force_update:
                self.__resize(len(self.programs))
                self.__clear()
                self.__remove_update_ids()

                for i in xrange(len(self.programs)):
                    program = self.programs[i]
                    self.add_channel(program.channel, 0, i)
                    self.__add_program(program, 1, i)
                    self.add_other_bar(program, 2, i)

                    self.filled_programs = self.programs

    def __resize(self, rows, columns=3):
        if rows == 0:
            rows = 1
        self.table.resize(rows, columns)

    def __clear(self):
        cells = []
        self.table.foreach(cells.append)
        for cell in cells:
            self.table.remove(cell)

        self.filled_programs = []

    def __remove_update_ids(self):
        for id in self.update_ids:
            gobject.source_remove(id)
        self.update_ids = []

    def add_channel(self, channel, column, row):
        if channel.logo_small:
            widget = gtk.Image()
            widget.set_from_pixbuf(channel.logo_small)
        else:
            widget = gtk.Label()
            widget.set_markup("<b>" + channel.markup_escaped_name + "</b>")
            widget.set_alignment(0.0, 0.5)

        widget.show()
        self._attach(widget, column, row, xoptions=gtk.SHRINK|gtk.FILL)

    def __add_program(self, program, column, row):
        g = lambda : program
        menu = ProgramContextMenu(g, Reminders(), self.reminders_model)

        label = gtk.Label()
        label.set_ellipsize(pango.ELLIPSIZE_END)
        label.set_max_width_chars(80)
        label.set_label(program.title)
        label.set_alignment(0.0, 0.5)
        label.show()

        eb = gtk.EventBox()
        eb.add(label)
        eb.connect("button-press-event", self.__eb_button_press_event, menu)
        eb.show()
        if program.description and program.description != "":
            self.tooltips.set_tip(eb, program.description)

        self._attach(eb, column, row, xoptions=gtk.SHRINK|gtk.FILL)

    def _attach(self, widget, column, row, xoptions=gtk.FILL|gtk.EXPAND):
        self.table.attach(widget, column, column + 1, row, row + 1,
                          xoptions=xoptions)

    def __eb_button_press_event(self, event_box, event, menu):
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
            if menu is not None:
                menu.popup(None, None, None, event.button, event.time)

class CurrentProgramTable(ProgramTable):
    def __init__(self, listings):
        self.title = _("Now Playing...")
        ProgramTable.__init__(self, listings, self.title)

    def set_programs(self):
        self.programs = []
        for channel_name in self.listings.selected_channels:
            if self.listings.channels.has_key(channel_name):
                channel = self.listings.channels[channel_name]
                current_program = channel.get_current_program()
                if current_program:
                    self.programs.append(current_program)

    def add_other_bar(self, program, column, row):
        ppb = ProgramProgressBar(program)
        self.update_ids.append(ppb.update_id)
        ppb.update()

        self._attach(ppb, column, row)

class ProgramBar:
    def get_hours(self, duration):
        return duration.seconds / 3600

    def get_minutes(self, duration):
        return (duration.seconds / 60) % 60

    def get_readable_time(self, duration):
        hours = self.get_hours(duration)
        minutes = self.get_minutes(duration)
        if hours > 0 and minutes > 0:
            h = gettext.ngettext("%d hour", "%d hours", hours) % hours
            m = gettext.ngettext("%d minute", "%d minutes", minutes) % minutes
            return _("%s and %s left") % (h, m)
        elif hours > 0 and minutes == 0:
            return gettext.ngettext("%d hour left", "%d hours left", hours) % \
                                    hours
        elif hours == 0 and minutes > 0:
            return gettext.ngettext("%d minute left", "%d minutes left",
                                    minutes) % minutes
        else:
            return gettext.ngettext("%d second left", "%d seconds left",
                                    duration.seconds) % duration.seconds

class ProgramProgressBar(ProgramBar, gtk.EventBox):
    def __init__(self, program):
        gtk.EventBox.__init__(self)
        self.program = program
        self.tooltips = gtk.Tooltips()
        self.pb = gtk.ProgressBar()
        self.pb.show()
        self.add(self.pb)
        self.show()

        self.update_id = gobject.timeout_add(TIMEOUT, self.update)

    def __time_delta_to_seconds(self, delta):
        return delta.days * 3600 * 24 + delta.seconds

    def update(self):
        time_from_start = self.program.time_span.time_from_start()
        played = self.__time_delta_to_seconds(time_from_start)
        duration = self.program.time_span.duration()
        total = self.__time_delta_to_seconds(duration)
        time_until_stop = self.program.get_time_until_stop()
        readable_time = self.get_readable_time(time_until_stop)
        self.tooltips.set_tip(self, readable_time)
        ratio = float(played) / float(total)
        if ratio > 1 or ratio < 0:
            return False
        self.pb.set_fraction(ratio)
        return True

class UpcomingProgramTable(ProgramTable):
    def __init__(self, listings):
        self.title = _("Upcoming programs")
        ProgramTable.__init__(self, listings, self.title)

    def add_channel(self, channel, column, row):
        if self.listings.config.upcoming_programs_below:
            ProgramTable.add_channel(self, channel, column, row)

    def set_programs(self):
        self.programs = []
        for channel_name in self.listings.selected_channels:
            if self.listings.channels.has_key(channel_name):
                channel = self.listings.channels[channel_name]
                upcoming_program = channel.get_upcoming_program()
                if upcoming_program:
                    self.programs.append(upcoming_program)

    def add_other_bar(self, program, column, row):
        ptlb = ProgramTimeLeftBar(program)
        self.update_ids.append(ptlb.update_id)
        ptlb.update()

        self._attach(ptlb, column, row)

class ProgramTimeLeftBar(ProgramBar, gtk.Label):
    def __init__(self, program):
        gtk.Label.__init__(self)
        self.program = program
        self.reminders = Reminders()
        self.set_alignment(1.0, 0.5)
        self.show()

        self.update_id = gobject.timeout_add(TIMEOUT, self.update)

    def update(self):
        time_until_start = self.program.get_time_until_start()
        readable_time = self.get_readable_time(time_until_start)
        self.set_label(readable_time)
        if self.reminders.has_reminder(Reminder(self.program)):
            Notification(self.program, self.get_readable_time)
        return True

class ProgramWindow(gtk.Window):
    def __init__(self, xmltvfile, applet, get_docking_data):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        xmltvfile.connect("loading-done", self.__xmltvfile_loading_done)
        self.applet = applet
        self.get_docking_data = get_docking_data

        self.cpt = CurrentProgramTable(xmltvfile.listings)
        self.upt = UpcomingProgramTable(xmltvfile.listings)

        self.connect("key-press-event", self.__key_press_event)

        self.set_decorated(False)
        self.set_resizable(False)
        self.set_keep_above(True)
        self.set_skip_pager_hint(True)
        self.set_skip_taskbar_hint(True)

        self.vbox = gtk.VBox(False, 6)
        self.vbox.set_border_width(6)

        self.hbox = gtk.HBox(False, 6)
        self.hbox.set_border_width(6)

        self.vbox.pack_start(self.hbox)

        self.hbox.pack_start(self.cpt)

        self.position_upcoming_programs(xmltvfile.config.upcoming_programs_below)

        frame = gtk.Frame()
        frame.add(self.vbox)
        frame.set_shadow_type(gtk.SHADOW_IN)

        self.add(frame)

        self.config = Configuration()

    def __xmltvfile_loading_done(self, xmltvfile, listings):
        if listings:
            self.cpt.listings = listings
            self.upt.listings = listings
            self.update()

    def __key_press_event(self, window, event):
        if event.keyval == gtk.keysyms.Escape:
            self.applet.set_state(gtk.STATE_NORMAL)
            window.hide()

    def position_window(self):
        self.realize()
        gtk.gdk.flush()

        (w, h) = self.get_size()
        (w, h) = self.size_request()

        (x, y, gravity) = self.get_docking_data(False, w, h)

        self.move(x, y)
        self.set_gravity(gravity)

    def position_upcoming_programs(self, below):
        if below:
            if self.upt in self.hbox.get_children():
                self.hbox.remove(self.upt)
            if self.upt not in self.vbox.get_children():
                self.vbox.pack_start(self.upt)
        else:
            if self.upt in self.vbox.get_children():
                self.vbox.remove(self.upt)
            if self.upt not in self.hbox.get_children():
                self.hbox.pack_start(self.upt)
        self.update()

    def set_reminders_model(self, reminders_model):
        self.cpt.reminders_model = reminders_model
        self.upt.reminders_model = reminders_model

    def update(self):
        self.cpt.update(True)
        self.upt.update(True)

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