"""Add, remove and configure runners"""
# Standard Library
# pylint: disable=too-many-instance-attributes,attribute-defined-outside-init
import os
from gettext import gettext as _

# Third Party Libraries
from gi.repository import GObject, Gtk

# Lutris Modules
from lutris import runners, settings
from lutris.gui.config.runner import RunnerConfigDialog
from lutris.gui.dialogs import ErrorDialog, GtkBuilderDialog
from lutris.gui.dialogs.download import simple_downloader
from lutris.gui.dialogs.runner_install import RunnerInstallDialog
from lutris.gui.widgets.utils import ICON_SIZE, get_builder_from_file, get_icon, open_uri
from lutris.util import datapath
from lutris.util.log import logger


class RunnersDialog(GtkBuilderDialog):

    """Dialog to manage the runners."""
    glade_file = "runners-dialog.ui"
    dialog_object = "runners_dialog"
    runner_entry_ui = os.path.join(datapath.get(), "ui", 'runner-entry.ui')

    __gsignals__ = {
        "runner-installed": (GObject.SIGNAL_RUN_FIRST, None, ()),
        "runner-removed": (GObject.SIGNAL_RUN_FIRST, None, ()),
    }

    def initialize(self, **kwargs):
        width = int(settings.read_setting("runners_manager_width") or 800)
        height = int(settings.read_setting("runners_manager_height") or 800)
        self.dialog_size = (width, height)
        self.dialog.resize(width, height)

        self.runner_listbox = self.builder.get_object('runners_listbox')
        self.runner_listbox.set_header_func(self._listbox_header_func)

        self.refresh_button = self.builder.get_object('refresh_button')

        self.runner_list = sorted(runners.__all__)
        # Run this after show_all, else all hidden buttons gets shown
        self.populate_runners()

    @staticmethod
    def _listbox_header_func(row, before):
        if not row.get_header() and before is not None:
            row.set_header(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL))

    def get_runner_hbox(self, runner_name):
        # Get runner details
        runner = runners.import_runner(runner_name)()
        platform_list = sorted(list(set(runner.platforms)))
        if len(platform_list) > 4:
            platform = _("Multiple platforms")
        else:
            platform = ", ".join(platform_list)

        builder = Gtk.Builder()
        builder.add_from_file(self.runner_entry_ui)
        hbox = builder.get_object('runner_entry')
        hbox.show()

        # Icon
        runner_icon = builder.get_object('runner_icon')
        runner_icon.set_from_pixbuf(get_icon(runner_name, icon_format='pixbuf', size=ICON_SIZE))

        # Label
        runner_name = builder.get_object('runner_name')
        runner_name.set_text(runner.human_name)
        runner_description = builder.get_object('runner_description')
        runner_description.set_text(runner.description)
        runner_platform = builder.get_object('runner_platform')
        runner_platform.set_text(platform)
        runner_label = builder.get_object('runner_label')
        if not runner.is_installed():
            runner_label.set_sensitive(False)

        # Buttons
        self.versions_button = builder.get_object('manage_versions')
        self.versions_button.connect("clicked", self.on_versions_clicked, runner, runner_label)
        self.install_button = builder.get_object('install_runner')
        self.install_button.connect("clicked", self.on_install_clicked, runner, runner_label)
        self.remove_button = builder.get_object('remove_runner')
        self.remove_button.connect("clicked", self.on_remove_clicked, runner, runner_label)
        self.configure_button = builder.get_object('configure_runner')
        self.configure_button.connect("clicked", self.on_configure_clicked, runner, runner_label)
        self.set_button_display(runner)

        return hbox

    def populate_runners(self):
        for runner_name in self.runner_list:
            hbox = self.get_runner_hbox(runner_name)
            self.runner_listbox.add(hbox)

    def set_button_display(self, runner):
        if runner.multiple_versions:
            self.versions_button.show()
            self.install_button.hide()
            if runner.can_uninstall():
                self.remove_button.show()
            else:
                self.remove_button.hide()
        else:
            self.versions_button.hide()
            self.remove_button.hide()
            self.install_button.show()

        if runner.is_installed():
            self.install_button.hide()
            if runner.can_uninstall():
                self.remove_button.show()
            else:
                self.remove_button.hide()

        self.configure_button.show()

    def on_versions_clicked(self, widget, runner, runner_label):
        dlg_title = _("Manage %s versions") % runner.name
        versions_dialog = RunnerInstallDialog(dlg_title, self.dialog, runner.name)
        versions_dialog.connect("destroy", self.set_install_state, runner, runner_label)

    def on_install_clicked(self, widget, runner, runner_label):
        """Install a runner."""
        if runner.depends_on:
            dependency = runner.depends_on()
            dependency.install(downloader=simple_downloader)
        try:
            runner.install(downloader=simple_downloader)
        except (
            runners.RunnerInstallationError,
            runners.NonInstallableRunnerError,
        ) as ex:
            ErrorDialog(ex.message, parent=self.dialog)
        if runner.is_installed():
            self.emit("runner-installed")
            self.refresh_button.emit("clicked")

    def on_configure_clicked(self, widget, runner, runner_label):
        config_dialog = RunnerConfigDialog(runner, parent=self.dialog)
        config_dialog.connect("destroy", self.set_install_state, runner, runner_label)

    def on_remove_clicked(self, widget, runner, runner_label):
        if not runner.is_installed():
            logger.warning("Runner %s is not installed", runner)
            return

        if runner.multiple_versions:
            logger.info("Removing multiple versions")
            builder = get_builder_from_file('runner-remove-all-versions-dialog.ui')
            builder.connect_signals(self)
            remove_confirm_button = builder.get_object('remove_confirm_button')
            remove_confirm_button.connect("clicked", self.on_remove_all_clicked, runner, runner_label)
            all_versions_label = builder.get_object('runner_all_versions_label')
            all_versions_label.set_markup(all_versions_label.get_label() % runner.human_name)
            self.all_versions_dialog = builder.get_object('runner_remove_all_versions_dialog')
            self.all_versions_dialog.set_parent(self.dialog)
            self.all_versions_dialog.show()
        else:
            builder = get_builder_from_file('runner-remove-confirm-dialog.ui')
            builder.connect_signals(self)
            remove_confirm_button = builder.get_object('remove_confirm_button')
            remove_confirm_button.connect("clicked", self.on_remove_confirm_clicked, runner, runner_label)
            runner_remove_label = builder.get_object('runner_remove_label')
            runner_remove_label.set_markup(runner_remove_label.get_label() % runner.human_name)
            self.remove_confirm_dialog = builder.get_object('runner_remove_confirm_dialog')
            self.remove_confirm_dialog.show()

    def on_remove_confirm_clicked(self, widget, runner, runner_label):
        runner.uninstall()
        self.refresh_button.emit("clicked")
        self.emit("runner-removed")
        self.remove_confirm_dialog.destroy()

    def on_remove_all_clicked(self, _widget, runner, _runner_label):
        runner.uninstall()
        self.refresh_button.emit("clicked")
        self.emit("runner-removed")
        self.all_versions_dialog.destroy()

    def on_cancel_confirm_clicked(self, _widget):
        self.remove_confirm_dialog.destroy()

    def on_cancel_all_clicked(self, _widget):
        self.all_versions_dialog.destroy()

    @staticmethod
    def on_folder_clicked(_widget):
        open_uri("file://" + settings.RUNNER_DIR)

    def on_refresh_clicked(self, _widget):
        for child in self.runner_listbox:
            child.destroy()
        self.populate_runners()

    def on_close_clicked(self, _widget):
        self.destroy()  # pylint: disable=no-member

    def set_install_state(self, _widget, runner, runner_label):
        if runner.is_installed():
            runner_label.set_sensitive(True)
            self.emit("runner-installed")
        else:
            runner_label.set_sensitive(False)

    def on_resize(self, _widget, *args):
        """Store the dialog's new size."""
        self.dialog_size = self.dialog.get_size()

    def on_destroy(self, _widget):
        # Save window size
        width, height = self.dialog_size
        settings.write_setting("runners_manager_width", width)
        settings.write_setting("runners_manager_height", height)
