# Copyright (C) 2008 LottaNZB Development Team
# 
# This program 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; version 3.
# 
# This program 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

import sys

def die(message):
    """
    Try to display a message if the software requirements are not satisfied and
    shut down the application afterwards.
    
    @param message: The error message to be shown.
    @type message: str
    """
    
    try:
        from subprocess import call
        call(["zenity", "--error", "--text", message])
    except (ImportError, OSError):
        pass
    
    sys.stderr.write(message)
    sys.exit(1)

if sys.version_info < (2, 4):
    die("You need to install Python 2.4 or greater to run LottaNZB.")

try:
    import pygtk
    pygtk.require("2.0")
    
    import gtk
    import gtk.glade
except (ImportError, AssertionError):
    die("You need to install PyGTK to run LottaNZB.")

try:
    from kiwi.__version__ import version as kiwi_version
    from kiwi import log as kiwi_log
    from kiwi.environ import Application
except ImportError:
    die("You need to install Kiwi to run LottaNZB.\nThe package is usually "
    "called 'python-kiwi'.")

if kiwi_version < (1, 9, 9):
    die("Your Kiwi installation is out-of-date.\nLottaNZB requires version "
    "1.9.9 or greater.")

import os
import atexit
import threading

import logging
log = logging.getLogger(__name__)

from os.path import join, isdir, abspath, dirname, expanduser
from optparse import OptionParser

from lottanzb import __version__
from lottanzb.util import gproperty, Flock, setup_kiwi_conversion, _
from lottanzb.config import GConfigSection, Config as ConfigRoot

class Config(GConfigSection):
    config_revision = gproperty(type=int, default=1)

class App(Application):
    __shared_state = {}
    
    is_quitting = False
    debug = False
    
    backend = None
    mode_manager = None
    log = None
    main_window = None
    config = None
    
    lock = None
    lock_acquired = False
    
    def __init__(self):
        self.__dict__ = self.__shared_state
        
        if self.__shared_state:
            return
        
        Application.__init__(self, "lottanzb")
        
        self.set_application_domain("lottanzb")
        
        self.setup_resources()
        self.enable_translation()
        self.setup_logging()
        
        setup_kiwi_conversion()
    
    def setup_resources(self):
        if self.uninstalled:
            self.add_global_resource("glade", "data/glade")
            self.add_resource("help", "help")
        
        # FIXME: the difference between the resource names 'home' and 'user'
        # isn't obvious.
        self.add_resource("home", expanduser("~"))
        self.add_resource("user", self.home_dir(".lottanzb"))
        self.add_resource("temp", self.user_dir("tmp"))
        self.add_resource("code", dirname(abspath(__file__)))
    
    def add_global_resource(self, resource, path):
        self.add_resource(resource, path)
        
        Application.add_global_resource(self, resource, path)
    
    def add_resource(self, resource, path):
        joined_path = join(self.get_root(), path)
        
        if not isdir(joined_path):
            os.mkdir(joined_path)
        
        Application.add_resource(self, resource, path)
    
    def _join_dir(self, ressource, *subpaths):
        return join(self.get_resource_paths(ressource)[0], *subpaths)
    
    def home_dir(self, *subpaths):
        return self._join_dir("home", *subpaths)
    
    def user_dir(self, *subpaths):
        return self._join_dir("user", *subpaths)
    
    def temp_dir(self, *subpaths):
        return self._join_dir("temp", *subpaths)
    
    def code_dir(self, *subpaths):
        return self._join_dir("code", *subpaths)
    
    def help_dir(self, *subpaths):
        return self._join_dir("help", *subpaths)
    
    def glade_dir(self, *subpaths):
        return self._join_dir("glade", *subpaths)
    
    def setup_logging(self):
        kiwi_log.set_log_level("lottanzb.*", logging.INFO)
        
        try:
            kiwi_log.set_log_file(self.user_dir("log"))
        except AttributeError:
            logging.basicConfig(format="%(asctime)s %(name)-20s %(message)s",
                datefmt="%T", level=logging.INFO)
    
    def load_config(self):
        # Always apply the default configuration values first.
        # If the configuration file happens to be empty or incomplete, no
        # exception is raised when invoking config.load() and this would
        # eventually cause other exceptions to be raised.
        self.config = ConfigRoot(
            config_file=self.user_dir("lottanzb.conf"),
            main_sections=["modes", "gui", "backend", "core", "plugins"]
        )
        
        try:
            self.config.load()
        except Exception, error:
            log.error(str(error))
    
    def launch(self):
        # Lazy imports.
        from lottanzb.log import Log
        from lottanzb.modes import ModeManager
        from lottanzb.backend import Backend
        
        self.log = Log()
        logging.getLogger("lottanzb").addHandler(self.log)
        
        log.info(_("Starting LottaNZB %s...") % __version__)
        
        self.load_config()
        self.mode_manager = ModeManager(self.config.modes)
        
        self.backend = Backend(self.config.backend)
        self.log.connect_to_backend()
        
        atexit.register(self.on_quit)
        
        self.mode_manager.load()
    
    def show_gui(self):
        from lottanzb.gui import MainWindow
        
        log.debug(_("Launching GUI..."))
        
        self.main_window = MainWindow(self.config.gui)
        
        gtk.main()
    
    def get_active_threads(self):
        return threading.enumerate()
    
    def stop_all_threads(self, block=False):
        for thread in self.get_active_threads():
            try:
                thread.stop()
            except:
                pass
        
        if block:
            self.wait_for_all_threads()
    
    def wait_for_all_threads(self):
        for thread in self.get_active_threads():
            try:
                thread.join()
            except:
                pass
    
    def quit(self, code=0):
        sys.exit(code)
    
    def on_quit(self):
        self.is_quitting = True
        
        self.config.save()
        self.mode_manager.leave_mode()
        self.stop_all_threads(True)
        self.lock.release()
        
        log.debug(_("Exiting..."))
        
        if gtk.main_level():
            gtk.main_quit()
    
    def main(self, args):
        parser = OptionParser(
            usage="%prog [FILES and/or NEWZBINIDS...]",
            version="LottaNZB %s" % __version__
        )
        
        parser.add_option("-d", "--debug", action="store_true", dest="debug", \
            help="show debug messages", default=False)
        
        options, args = parser.parse_args()
        
        # Initialize console logging
        if options.debug:
            kiwi_log.set_log_level("*", logging.DEBUG)
            self.debug = True
        
        # Check if another instance of LottaNZB is already running
        self.lock = Flock("/tmp/lottanzb.lock")
        self.lock_acquired = self.lock.acquire()
        
        files = []
        newzbin_ids = []
        
        for arg in args:
            try:
                newzbin_ids.append(int(arg))
            except ValueError:
                files.append(arg)
        
        command_line_action = bool(len(files) or len(newzbin_ids))
        
        if command_line_action or self.lock_acquired:            
            self.launch()
            
            if command_line_action:
                def enqueue_items(mode_manager, *args):
                    if mode_manager.active_mode:
                        for file in files:
                            try:
                                self.backend.enqueue(file)
                            except:
                                pass
                        
                        for newzbin_id in newzbin_ids:
                            try:
                                self.backend.newzbinEnqueue(newzbin_id)
                            except:
                                pass
                        
                        if not self.lock_acquired:
                            self.quit()
                
                if self.mode_manager.active_mode:
                    enqueue_items(self.mode_manager)
                else:
                    self.mode_manager.connect("notify::active-mode", \
                        enqueue_items)
            
            if self.lock_acquired:
                self.show_gui()
            else:
                gtk.main()
        else:
            # We've got nothing to do
            log.error(_("LottaNZB is already running on this computer."))
            self.quit(1)
    
    def _get_main(self):
        return self.main
