#!/usr/bin/python
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.

"""Program entry point"""

import dbus
import dbus.service
import os
import sys
import logging
import optparse
import signal
import time
import json

# pylint: disable=E0611
from gi.repository import Gdk, Gtk, GObject, GLib

from dbus.mainloop.glib import DBusGMainLoop
from os.path import realpath, dirname, normpath

LAUNCH_PATH = dirname(realpath(__file__))
if LAUNCH_PATH != "/usr/bin":
    SOURCE_PATH = normpath(os.path.join(LAUNCH_PATH, '..'))
    sys.path.insert(0, SOURCE_PATH)

from lutris.gui import dialogs
from lutris.migrations import migrate

try:
    import yaml as _yaml  # noqa
except ImportError:
    q = dialogs.QuestionDialog({'title': "Dependency not available",
                                'question': "PythonYAML is not installed,\n"
                                            "do you want to install it now?"})
    if q.result == Gtk.ResponseType.YES:
        os.system('software-center python-yaml')
    else:
        sys.exit()

from lutris import pga, runtime
from lutris.config import check_config  # , register_handler
from lutris.util.log import logger
from lutris.game import Game
from lutris.gui.installgamedialog import InstallerDialog
from lutris.gui.lutriswindow import LutrisWindow
from lutris.settings import VERSION


DBUS_INTERFACE = 'org.lutris.main'

# Set the logging level to show debug messages.
console = logging.StreamHandler()
fmt = '%(levelname)-8s %(asctime)s [%(module)s]:%(message)s'
formatter = logging.Formatter(fmt)
console.setFormatter(formatter)
logger.addHandler(console)
logger.setLevel(logging.ERROR)

# Support for command line options.
parser = optparse.OptionParser(version="%prog " + VERSION)
parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
                  help="Verbose output")
parser.add_option("-d", "--debug", action="store_true", dest="debug",
                  help="Show debug messages")
parser.add_option("-i", "--install", dest="installer_file",
                  help="Install a game from a yml file")
parser.add_option("-l", "--list-games", action="store_true", dest="list_games",
                  help="List games in database")
parser.add_option("-o", "--installed", action="store_true", dest="list_installed",
                  help="Only list installed games")
parser.add_option("-s", "--list-steam", action="store_true",
                  help="List Steam (Windows) games")
parser.add_option("-j", "--json", action="store_true", dest="json",
                  help="Display the list of games in JSON format")
parser.add_option("--reinstall", action="store_true", help="Reinstall game")
(options, args) = parser.parse_args()

if options.verbose:
    logger.setLevel(logging.INFO)

if options.debug:
    logger.setLevel(logging.DEBUG)

if options.list_games:
    game_list = pga.get_games()
    if options.list_installed:
        game_list = [game for game in game_list if game['installed']]
    if options.json:
        games = []
        for game in game_list:
            games.append({
                'id': game['id'],
                'slug': game['slug'],
                'name': game['name'],
                'runner': game['runner'],
                'directory': game['directory'] or '-'
            })
        print json.dumps(games, indent=2).encode('utf-8')
    else:
        for game in game_list:
            print u"{:4} | {:<40} | {:<40} | {:<15} | {:<64}".format(
                game['id'],
                game['name'][:40],
                game['slug'][:40],
                game['runner'],
                game['directory'] or '-'
            ).encode('utf-8')
    exit()
if options.list_steam:
    from lutris.runners import winesteam
    steam_runner = winesteam.winesteam()
    print steam_runner.get_appid_list()
    exit()


check_config(force_wipe=False)

installer = False
game = None

signal.signal(signal.SIGINT, signal.SIG_DFL)


class LutrisService(dbus.service.Object):
    """Main D-Bus Lutris service."""
    def __init__(self, bus, path, name):
        dbus.service.Object.__init__(self, bus, path, name)
        self.running = False
        self.lutris_window = None

    @dbus.service.method(DBUS_INTERFACE, out_signature='b')
    def is_running(self):
        return self.running

    @dbus.service.method(DBUS_INTERFACE, in_signature='i')
    def run(self, timestamp):
        if self.is_running():
            self.lutris_window.window.present_with_time(timestamp)
        else:
            logger.info("Welcome to Lutris")
            self.running = True
            self.lutris_window = LutrisWindow()
            GObject.threads_init()
            Gtk.main()
            self.running = False

    @dbus.service.method(DBUS_INTERFACE, in_signature='s')
    def install_game(self, game_ref):
        self.lutris_window.on_install_clicked(game_ref=game_ref)

    @dbus.service.method(DBUS_INTERFACE, in_signature='i')
    def run_game(self, game_id):
        self.lutris_window.on_game_run(game_id=game_id)

# D-Bus init
DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

# Get lutris service
request = bus.request_name(DBUS_INTERFACE, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
    lutris = LutrisService(bus, '/', DBUS_INTERFACE)
else:
    proxy = bus.get_object(DBUS_INTERFACE, "/")
    lutris = dbus.Interface(proxy, DBUS_INTERFACE)

# Make sure the existing process is not frozen
if type(lutris) is dbus.Interface:
    try:
        lutris.is_running()
    except dbus.exceptions.DBusException as e:
        logger.debug(e)
        q = dialogs.QuestionDialog(
            {'title': "Error",
             'question': ("Lutris is already running \n"
                          "but seems unresponsive,\n"
                          "do you want to restart it?")}
        )
        if q.result == Gtk.ResponseType.NO:
            exit()
        try:
            # Get existing process' PID
            dbus_proxy = bus.get_object('org.freedesktop.DBus',
                                        '/org/freedesktop/DBus/Bus')
            dbus_interface = dbus.Interface(dbus_proxy, 'org.freedesktop.DBus')
            pid = dbus_interface.GetConnectionUnixProcessID(lutris.bus_name)

            os.kill(pid, signal.SIGKILL)
        except OSError, dbus.exceptions.DBusException:
            logger.debug(e)
            exit()  # Give up :(
        else:
            time.sleep(1)  # Wait for bus name to be available again
            lutris = LutrisService(bus, '/', DBUS_INTERFACE)

migrate()

game_slug = ""
for arg in args:
    if arg.startswith('lutris:'):
        game_slug = arg[7:]
        break

installer = options.installer_file

if game_slug or installer:
    if not game_slug and not os.path.isfile(installer):
        print("No such file: %s" % installer)
        exit()

    db_game = None
    if game_slug:
        db_game = (pga.get_game_by_field(game_slug, 'id')
                   or pga.get_game_by_field(game_slug, 'slug')
                   or pga.get_game_by_field(game_slug, 'installer_slug'))

    if db_game and db_game['installed'] and not options.reinstall:
        logger.info("Launching %s", db_game['name'])
        if lutris.is_running():
            lutris.run_game(db_game['id'])
        else:
            runtime.update()
            lutris_game = Game(db_game['id'])
            lutris_game.exit_main_loop = True
            lutris_game.play()
            GLib.MainLoop().run()
    else:
        logger.info("Installing %s", game_slug)
        if lutris.is_running():
            lutris.install_game(installer or game_slug)
        else:
            runtime.update()
            InstallerDialog(installer or game_slug)
            GObject.threads_init()
            Gtk.main()
    exit()

lutris.run(int(time.time()))
if lutris.is_running():
    Gdk.notify_startup_complete()
