#!/usr/bin/python
# coding=UTF-8

## Copyright (C) 2012 ABRT team <abrt-devel-list@redhat.com>
## Copyright (C) 2001-2005 Red Hat, Inc.

## 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; either version 2 of the License, or
## (at your option) any later version.

## 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 Street, Suite 500, Boston, MA  02110-1335  USA

import os
import sys
import logging
import signal
from argparse import ArgumentParser

from ctypes import cdll, util

XLIB_PATH = util.find_library('X11')
if not XLIB_PATH:
    sys.stderr.write("Could not load X11 library\n")
    sys.exit(1)

XLIB = cdll.LoadLibrary(XLIB_PATH)
DISPLAY = XLIB.XOpenDisplay(None)
if DISPLAY == 0:
    sys.stderr.write("Cannot connect to X server\n")
    sys.exit(2)

XLIB.XCloseDisplay(DISPLAY)

# pygobject
#pylint: disable=E0611
from gi.repository import Gtk
#pylint: disable=E0611
from gi.repository import GLib
#pylint: disable=E0611
from gi.repository import Gio

# gnome-abrt
import gnome_abrt
import gnome_abrt.url
from gnome_abrt.views import OopsWindow
from gnome_abrt.controller import Controller
from gnome_abrt.signals import glib_sigchld_signal_handler
from gnome_abrt.problems import MultipleSources
from gnome_abrt.directory_problems import DirectoryProblemSource
from gnome_abrt.dbus_problems import (get_standard_problems_source,
                                      get_foreign_problems_source)
from gnome_abrt.errors import UnavailableSource
from gnome_abrt.l10n import _
from gnome_abrt.wrappers import (show_events_list_dialog,
                                 show_system_config_abrt_dialog)
from gnome_abrt.config import get_configuration
from gnome_abrt.dialogs import show_report_problem_with_abrt
from gnome_abrt.url.gliburltitle import (GetURLTitleSourcePool,
                                         GetURLTitleSourceCache)

# dbus
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop

GNOME_ABRT_APPLICATION_ID = 'org.freedesktop.GnomeAbrt'
GNOME_ABRT_INTERFACE = 'org.freedesktop.GnomeAbrt'
GNOME_ABRT_OBJECT_PATH = '/org/freedesktop/GnomeAbrt'
GNOME_ABRT_URL_POOL_CAPACITY = 10


# because of https://bugzilla.gnome.org/show_bug.cgi?id=682331
class GtkAppDBUSImpl(object):
    """A proxy for primary application
    """

    #pylint: disable=R0923
    class Service(dbus.service.Object):
        """DBus service sitting on session bus
        """

        def __init__(self, bus, bus_name, application):
            bus_name = dbus.service.BusName(bus_name, bus)
            dbus.service.Object.__init__(self, bus_name, GNOME_ABRT_OBJECT_PATH)
            self._app = application

        @dbus.service.method(dbus_interface=GNOME_ABRT_INTERFACE,
                             in_signature='as', out_signature='')
        def command_line(self, argv):
            """DBus service method accepting a new command line arguments
            """

            class Arguments(object):
                """Adapter for Gtk class
                """

                def __init__(self, argv):
                    self._argv = argv

                def get_arguments(self):
                    return self._argv

            args = Arguments([str(a) for a in argv])
            self._app._parse_command_line(self, args)


    def __init__(self, bus_name, application):
        self._app = application
        self.mainloop = DBusGMainLoop()

        self._primary_obj = None
        self._primary_iface = None
        self._service = None

        try:
            bus = dbus.SessionBus(mainloop = self.mainloop)
            if bus.name_has_owner(bus_name):
                self._primary_obj = bus.get_object(bus_name,
                                                   GNOME_ABRT_OBJECT_PATH)
            else:
                self._service = GtkAppDBUSImpl.Service(bus, bus_name, self._app)
        except dbus.exceptions.DBusException as ex:
            logging.warning("Can't initialize gnome-abrt's DBus services: {0}"
                    .format(ex))
            self._primary_obj = None
            self._service = None

    def is_remote(self):
        """Application is remote if connection to primary application succeeded
        """

        return self._primary_obj is not None


    def send_command_line(self, argv):
        """Sends command line arguments to the primary application.
           Raises RuntimeError if an instance is not remote.
        """

        if not self.is_remote():
            raise RuntimeError("Can't send command-line message because "
                               "instance is not a remote application")

        try:
            if not self._primary_iface:
                self._primary_iface = dbus.Interface(self._primary_obj,
                        GNOME_ABRT_INTERFACE)

            if len(argv) == 0:
                argv = dbus.Array([], signature='s')

            self._primary_iface.command_line(argv)
            return True
        except dbus.exceptions.DBusException as ex:
            logging.debug("Can't send command line over DBus: {0}"
                    .format(ex))
            return False


class OopsApplication(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)

        self._dbus_srv = GtkAppDBUSImpl(GNOME_ABRT_APPLICATION_ID, self)
        self.connect("command-line", self._parse_command_line)
        self.set_flags(Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
        self.gcontext = None
        self.all_sources = None
        self._url_pool = GetURLTitleSourcePool(GNOME_ABRT_URL_POOL_CAPACITY)
        self._url_cache = GetURLTitleSourceCache(self._url_pool)
        #gnome_abrt.url.set_async_worker(self._url_cache.get_url_title_async)
        # Disable because of https://bugzilla.redhat.com/show_bug.cgi?id=959811
        gnome_abrt.url.set_async_worker(lambda *args: None)

    #pylint: disable=W0613
    def _parse_command_line(self, sender, gcmdargs):
        argv = gcmdargs.get_arguments()

        if self._dbus_srv.is_remote():
            if self._dbus_srv.send_command_line(argv):
                return 0

        if len(argv) > 0:
            conf = get_configuration()
            conf['problemid'] = argv[0]

        self.activate()
        return 0

    def do_activate(self):
        try:
            windows = self.get_windows()
            if windows:
                windows[0].present()
            else:
                conf = get_configuration()
                conf.add_option("all_problems", default_value=False)

                sources = []
                try:
                    sources.append(
                          get_standard_problems_source(self._dbus_srv.mainloop))
                except UnavailableSource as ex:
                    logging.warning(str(ex))

                self.gcontext = GLib.main_context_default()
                try:
                    path = os.path.join(GLib.get_user_cache_dir(), "abrt/spool")
                    dps = DirectoryProblemSource(path, context=self.gcontext)
                    sources.append(dps)
                except UnavailableSource:
                    logging.warning(str(ex))

                if len(sources) == 0:
                    raise UnavailableSource("No available problem source.")

                self.all_sources = [(_("My"), MultipleSources(sources))]

                try:
                    self.all_sources.append((_("System"),
                                get_foreign_problems_source()))
                except UnavailableSource as ex:
                    logging.warning(str(ex))

                controller = Controller(self.all_sources,
                                        glib_sigchld_signal_handler)
                main_window = OopsWindow(self, self.all_sources, controller)
                main_window.show_all()
                self.add_window (main_window)
        #pylint: disable=W0703
        except Exception as ex:
            logging.exception(ex)
            sys.exit(1)

    def do_startup(self):
        Gtk.Application.do_startup(self)

        menu = Gio.Menu()
        menu.append(_("_Preferences"), "app.preferences")
        menu.append(_("_ABRT Configuration"), "app.abrt-configuration")
        menu.append(_("_Report problem with ABRT"),
                    "app.report-problem-with-abrt")
        menu.append(_("_About"), "app.about")
        menu.append(_("_Quit"), "app.quit")

        action = Gio.SimpleAction.new("preferences", None)
        action.connect("activate", self.on_action_prefrences)
        self.add_action(action)

        action = Gio.SimpleAction.new("abrt-configuration", None)
        action.connect("activate", self.on_action_abrt_configuration)
        self.add_action(action)

        action = Gio.SimpleAction.new("report-problem-with-abrt", None)
        action.connect("activate", self.on_action_report_problem_with_abrt)
        self.add_action(action)

        action = Gio.SimpleAction.new("about", None)
        action.connect("activate", self.on_action_about)
        self.add_action(action)

        action = Gio.SimpleAction.new("quit", None)
        action.connect("activate", self.on_action_quit)
        self.add_action(action)

        self.set_app_menu(menu)

    #pylint: disable=W0613
    def on_action_prefrences(self, action, parameter):
        windows = self.get_windows()
        if windows:
            show_events_list_dialog(windows[0])
        else:
            show_events_list_dialog()

    def on_action_abrt_configuration(self, action, parameter):
        windows = self.get_windows()
        if windows:
            show_system_config_abrt_dialog(windows[0])
        else:
            show_system_config_abrt_dialog()

    #pylint: disable=W0613
    def on_action_about(self, action, parameter):
        dialog = Gtk.AboutDialog.new()
        dialog.set_icon_name("abrt")
        dialog.set_version(gnome_abrt.VERSION)
        dialog.set_logo_icon_name("abrt")
        dialog.set_program_name("ABRT")
        dialog.set_copyright("Copyright © 2012 Red Hat, Inc")
        dialog.set_license(
    "This program is free software; you can redistribut"
    "e 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 Li"
    "cense, or (at your option) any later version.\n\nThis program is distrib"
    "uted in the hope that it will be useful, but WITHOUT ANY WARRANTY; witho"
    "ut even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICU"
    "LAR PURPOSE.  See the GNU General Public License for more details.\n\nYo"
    "u should have received a copy of the GNU General Public License along wi"
    "th this program.  If not, see <http://www.gnu.org/licenses/>.")
        dialog.set_wrap_license(True)
        dialog.set_website("https://fedorahosted.org/abrt/")
        dialog.set_authors(["ABRT Team"])
        #dialog.set_artists(artists)
        #dialog.set_translator_credits(_("translator-credits"))

        windows = self.get_windows()
        if windows:
            dialog.set_transient_for(windows[0])

        dialog.run()
        dialog.destroy()

    #pylint: disable=W0613
    def on_action_quit(self, action, parameter):
        self.quit()

    #pylint: disable=W0613
    def on_action_report_problem_with_abrt(self, action, parameter):
        show_report_problem_with_abrt()


if __name__ == "__main__":
    signal.signal(signal.SIGINT, lambda signum, frame: sys.exit(1))

    gnome_abrt.init()

    CMDARGS = ArgumentParser(
            description = _('View and report application crashes'))
    CMDARGS.add_argument('-v', '--verbose', action='count',
            help=_('Be verbose'))
    CMDARGS.add_argument('-p', '--problem',
            help=_('Selected problem ID'))
    CMDARGS.add_argument('-x', '--expert', action='store_true',
            help=_('Expert mode'))

    OPTIONS = CMDARGS.parse_args()

    if OPTIONS.verbose > 0:
        logging.getLogger().setLevel(logging.DEBUG)

    VARS = vars(OPTIONS)

    CONF = get_configuration()
    # TODO : mark this option as hidden or something like that
    CONF.add_option('problemid', default_value=None)
    CONF.add_option('expert', default_value=(OPTIONS.expert))

    APP_CMDLINE = []
    if 'problem' in VARS:
        APP_CMDLINE.append(VARS['problem'])

    EXIT_CODE = 1
    APP = OopsApplication()
    try:
        EXIT_CODE = APP.run(APP_CMDLINE)
    except UnavailableSource as ex:
        logging.error(ex)

    sys.exit(EXIT_CODE)
