# -*- 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 os.path
import subprocess
import sys
import thread
from gettext import gettext as _

import gtk
import gobject

from GUI import ErrorDialog
import xmltv

from Listings import Listings
from Channel import Channel
from Program import Program

class XMLTVFile(gobject.GObject):
    __gproperties__ = {"path": (str, "XMLTV file path",
                       "The path to the XMLTV file", "",
                       gobject.PARAM_READWRITE)}

    __gsignals__ = {"loading-done": (gobject.SIGNAL_RUN_LAST, None, (object,)),
                    "loading": (gobject.SIGNAL_RUN_LAST, None, ()),
                    "loaded-channel": (gobject.SIGNAL_RUN_LAST, None,
                                      (object,)),
                    "downloading-logo": (gobject.SIGNAL_RUN_LAST, None,
                                        (object,)),
                    "downloading-logo-done": (gobject.SIGNAL_RUN_LAST, None,
                                             (object,)),
                    "downloading": (gobject.SIGNAL_RUN_LAST, None, ()),
                    "downloading-done": (gobject.SIGNAL_RUN_LAST, None,
                                         (int, int)),
                    "sorting": (gobject.SIGNAL_RUN_LAST, None, ()),
                    "sorting-done": (gobject.SIGNAL_RUN_LAST, None,
                                         (int, int))}

    def __init__(self, config):
        gobject.GObject.__init__(self)
        self.config = config
        self.listings = Listings(config)

        self.path = self.config.xmltv_file

        self.__channel_names = {}

    def do_get_property(self, property):
        if property.name == "path":
            return self.path

    def do_set_property(self, property, value):
        if property.name == "path":
            self.path = value

    def load(self, force_reload=False):
        thread.start_new_thread(self.__load_in_thread, (force_reload,))

    def __load_in_thread(self, force_reload):
        if os.path.exists(self.listings.file) and not force_reload:
            try:
                self.listings = self.listings.load()
            except:
                if self.config.debug:
                    os.rename(self.listings.file, "%s.debug" %
                              self.listings.file)
                else:
                    os.rename(self.listings.file)
        if self.has_changed() or force_reload:
            gtk.gdk.threads_enter()
            self.emit("loading")
            gtk.gdk.threads_leave()

            title = error_msg = ""
            try:
                self.__load_channels()
                self.__load_programs()
                self.listings.mtime = os.path.getmtime(self.path)
                if self.config.debug:
                    print("Loaded listings from XMLTV file: %s" % self.path)
            except IOError, ioe:
                title = _("Failed to load %s") % self.path
                if ioe.errno == 2:
                    error_msg = _("File not found")
                elif ioe.errno == 13:
                    error_msg = _("Access denied")
                else:
                    error_msg = _("Unknown error")
            except SyntaxError, se:
                title = _("Error while parsing %s") % self.path
                error_msg = _("The parser returned: \"%s\"") % se
                # error_msg = _("Not well formed at line %s, column %s") % \
                            # (se.lineno, se.offset)
            except Exception, e:
                title = _("Unknown error while loading %s") % self.path
                error_msg = str(e)

            if title and error_msg:
                gtk.gdk.threads_enter()
                ed = ErrorDialog(title, error_msg)
                ed.run()
                ed.destroy()
                gtk.gdk.threads_leave()
                self.emit("loading-done", (None))
                return

            for channel in self.listings.channels.values():
                channel.programs.sort()

            self.listings.save()
        else:
            for channel in self.listings.channels.values():
                gtk.gdk.threads_enter()
                self.emit("loaded-channel", (channel))
                gtk.gdk.threads_leave()

        gtk.gdk.threads_enter()
        self.emit("loading-done", (self.listings))
        gtk.gdk.threads_leave()

    def has_changed(self):
        if os.path.exists(self.path):
            return os.path.getmtime(self.path) != self.listings.mtime
        return True

    def __load_channels(self):
        for channel in xmltv.read_channels(open(self.path)):
            c = Channel(channel, self)
            self.__channel_names[c.id] = c.name
            self.listings.add_channel(c)
            gtk.gdk.threads_enter()
            self.emit("loaded-channel", (c))
            gtk.gdk.threads_leave()
            if self.config.debug:
                print("Added channel \"%s\" to Listings." % c.name)

    def __load_programs(self):
        for program in xmltv.read_programmes(open(self.path)):
            error_msg = ""
            if not program.has_key("title"):
                continue
            elif not program.has_key("start"):
                error_msg = "missing start time"
            elif not program.has_key("stop"):
                error_msg = "missing stop time"

            if error_msg:
                print >> sys.stderr, \
                      "Unable to add program \"%s\" to Listings: %s" % \
                      (program["title"][0][0], error_msg)
                continue

            channel_name = self.__get_channel_name(program)
            p = Program(program)
            self.listings.add_program(p, channel_name)
            if self.config.debug:
                print("Added program \"%s\" to Listings." % p.title)

    def __get_channel_name(self, program):
        return self.__channel_names[program["channel"]]

    def download(self, grabber_command=None):
        if not grabber_command:
            grabber_command = self.config.grabber_command
        grabber = subprocess.Popen(grabber_command.split())
        self.emit("downloading")
        gobject.child_watch_add(grabber.pid, self.__process_completed,
                                ("downloading",),
                                priority=gobject.PRIORITY_HIGH)
        if self.config.debug:
            print("Downloading with grabber command \"%s\"." % grabber_command)

    def sort(self, file=None):
        if not file:
            file = self.path
        tv_sort = subprocess.Popen(["tv_sort", file, "--output", file])
        self.emit("sorting")
        gobject.child_watch_add(tv_sort.pid, self.__process_completed,
                                ("sorting",), priority=gobject.PRIORITY_HIGH)
        if self.config.debug:
            print("Sorting \"%s\"." % file)

    def __process_completed(self, pid, condition, process):
        self.emit("%s-done" % process, pid, condition)

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