#!/usr/bin/python
# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2007-2008 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

"""
Initializing and running Elisa.
"""

import sys
import os
import site

# FIXME: horrible hack to work around the fact that when elisa.exe is built
# with py2exe the __file__ module variable is not defined.
import inspect
__file__ = os.path.abspath(inspect.currentframe().f_code.co_filename)


def _is_uninstalled():
    # Are we running Elisa development version (uninstalled)?
    script_dir = os.path.dirname(os.path.abspath(__file__))
    base_path = os.path.normpath(os.path.join(script_dir, '..'))
    return (os.path.basename(base_path) == 'elisa-core')


def _get_prefix():
    # Get the root path of the development version (uninstalled), or that of
    # the Windows installed version.
    checkout_path = os.environ.get('ELISA_BRANCH_PATH')
    if checkout_path:
        return checkout_path
    elif sys.argv[0].endswith('elisa.exe'):
        # Windows specific
        return sys.prefix
    else:
        # The script is elisa-core/bin/elisa
        file_path = os.path.abspath(__file__)
        core_path = os.path.dirname(file_path)
        prefix = os.path.join(core_path, '..', '..')
        return os.path.normpath(prefix)


def _fix_path():
    """
    When running the development version (uninstalled), add the elisa-core and
    elisa-plugins directories to the path.
    """
    prefix = _get_prefix()
    elisa_plugins = os.path.join(prefix, 'elisa-plugins')
    elisa_core = os.path.join(prefix, 'elisa-core')

    if os.path.exists(elisa_core):
        sys.path.insert(0, elisa_core)

    if os.path.exists(elisa_plugins):
        sys.path.insert(1, elisa_plugins)


def _add_site_dirs():
    """
    Add the directory containing locally installed plugin eggs to the path.
    """
    config_dir = os.path.join(os.path.expanduser('~'), '.elisa-0.5')
    local_plugins_dir = os.path.join(config_dir, 'plugins')
    if os.path.isdir(local_plugins_dir):
        path = sys.path
        sys.path = []
        site.addsitedir(local_plugins_dir)
        sys.path.extend(path)


def _setup_reactor():
    # Install the glib2 Twisted reactor
    from twisted.internet import glib2reactor
    glib2reactor.install()


def _setup_gstreamer():
    import gobject
    gobject.threads_init()
    import pygst
    pygst.require('0.10')


def _init_logging(log_to_file):
    from elisa.core import log
    log.init(log_to_file)
    log.logTwisted()


def _parse_options(args):
    from twisted.python import usage
    from elisa.core.options import Options

    options = Options()

    try:
        options.parseOptions(args[1:])
    except usage.UsageError, errortext:
        print '%s: %s' % (args[0], errortext)
        print '%s: Try --help for usage details.' % (args[0])
        sys.exit(1)

    return options


def _start_reactor_with_ipython():
    from twisted.internet import reactor

    reactor.simulate()
    reactor.startRunning()
    try:
        import IPython
        IPython.Shell.IPShellGTK([], locals()).mainloop()
    except ImportError:
        print "You need to install IPython in order to open an interactive " \
              "shell."


def _build_splash_screen():
    from elisa.core.utils.splash_screen import SplashScreen
    if SplashScreen:
        return SplashScreen()
    else:
        return None


def _start_reactor():
    from twisted.internet import reactor
    reactor.run()


def _start_application(options, splash):
    from elisa.core.application import Application
    from elisa.core import common
    from twisted.internet import reactor

    app = Application(options['config-file'],
                      show_tracebacks=options['tracebacks'], splash=splash,
                      options=options)
    common.set_application(app)

    def initialize_done(result):
        return app.start()

    def initialize_failure(failure):
        print 'Elisa failed to initialize:', failure

    dfr = app.initialize()
    dfr.addCallbacks(initialize_done, initialize_failure)

    # this could lead to an ugly "'NoneType' object is
    # unsubscriptable" in twisted, when the reactor gets killed from
    # the outside. Bug is reported and fixed in twisted-svn/trunk:
    # http://twistedmatrix.com/trac/ticket/2265
    reactor.addSystemEventTrigger('before', 'shutdown', app.stop)


def _setup_environment():
    import platform
    system = platform.system()

    # 'Fix' the PYTHONPATH. We need to do that:
    #   - for the development version (uninstalled)
    #   - for the Windows installed version (run from elisa.exe)
    if _is_uninstalled() or system == 'Windows':
        _fix_path()

    if system == 'Windows':
        from mswin32 import win_set_env
        win_set_env.set_runtime_env()

    _add_site_dirs()

    # FIXME: importing pkg_resources is needed just after new site dirs
    #        have been added so that the right egg distributions are
    #        chosen when importing elisa.core and the plugins
    import pkg_resources

def _exit_if_running(options):
    import platform
    system = platform.system()
    if system == 'Linux':
        _exit_if_running_linux(options)
    elif system == 'Windows':
        _exit_if_running_win32(options)
    return

def _exit_if_running_linux(options):
    try:
        import dbus
        import dbus.mainloop.glib

        dbus.set_default_main_loop(dbus.mainloop.glib.DBusGMainLoop())
    except ImportError:
        return
    
    bus = dbus.SessionBus()
    daemon = bus.get_object(dbus.BUS_DAEMON_NAME, dbus.BUS_DAEMON_PATH)
    daemon_iface = dbus.Interface(daemon, dbus.BUS_DAEMON_IFACE)

    if options.get('force-startup', False):
        return

    if 'com.fluendo.Elisa' in daemon_iface.ListNames():
        print 'Elisa is already running, exiting'
        
        if len(options['files']) > 0:
            bus = dbus.SessionBus()
            file_player = bus.get_object('com.fluendo.Elisa',
                                               '/com/fluendo/Elisa/Plugins/Poblesec/FilePlayer')
            file_player_iface = dbus.Interface(file_player,
                                        'com.fluendo.Elisa.Plugins.Poblesec.FilePlayer')
            file_player_iface.play_file(options['files'][0])
            
            # Set focus to the window
            frontend = bus.get_object('com.fluendo.Elisa',
                                               '/com/fluendo/Elisa/Plugins/Pigment/Frontend')
            frontend_iface = dbus.Interface(frontend,
                                        'com.fluendo.Elisa.Plugins.Pigment.Frontend')
            frontend_iface.show()
            
        sys.exit(1)
        
def _exit_if_running_win32(options):
    from win32gui import FindWindow
    from win32api import SendMessage
    
    try:
        handle = FindWindow("elisa_win32_message_window", "Win32 message window")
    except:
        return
    
    if len(options['files']) > 0:
        import ctypes
        from ctypes import Structure, POINTER, byref, c_void_p, c_char, sizeof, cast
        from ctypes.wintypes import ULONG
        from mswin32.structures import COPYDATASTRUCT
        
        command_line = ' '.join(options['files'])
        
        class COMMANDLINEDATA(Structure):
            _fields_ = [
                        ("data", c_char * len(command_line)),
                       ]
        
        commandlinedata = COMMANDLINEDATA()
        commandlinedata.data = command_line
        copydata = COPYDATASTRUCT()
        COMMAND_LINE = POINTER(ULONG)()
        COMMAND_LINE.value = 1
        copydata.dwData = COMMAND_LINE
        copydata.cbData = sizeof(commandlinedata)
        copydata.lpData = cast(byref(commandlinedata), c_void_p)
        from win32con import WM_COPYDATA
        SendMessage(handle, WM_COPYDATA, 0, copydata)
    
    print 'Elisa is already running, exiting'
    sys.exit(1)

def main():
    _setup_environment()

    # Done as early as possible
    _setup_reactor()
    
    options = _parse_options(sys.argv)
    
    _exit_if_running(options)
    _setup_gstreamer()

    if not options['headless'] and not options['nosplash']:
        splash = _build_splash_screen()
    else:
        splash = None

    if options['log']:
        _init_logging(True)
    else:
        _init_logging(False)

    from twisted.internet import reactor
    
    # First go to the main loop so that the splash screen can show up
    reactor.callLater(0.01, _start_application, options, splash=splash)

    if options['shell']:
        _start_reactor_with_ipython()
    else:
        _start_reactor()


if __name__ == '__main__':
    main()
