# -----------------------------------------------------------------------------
# Getting Things GNOME! - a personal organizer for the GNOME desktop
# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------

""" Dialog for loading plugins """

from gi.repository import Gtk, Pango

from GTG.core import info
from GTG.core.plugins import GnomeConfig
from GTG.core.plugins.engine import PluginEngine
from gettext import gettext as _
from GTG.gtk import ViewConfig

# columns in PluginsDialog.plugin_store
PLUGINS_COL_ID = 0
PLUGINS_COL_ENABLED = 1
PLUGINS_COL_NAME = 2
PLUGINS_COL_SHORT_DESC = 3
PLUGINS_COL_ACTIVATABLE = 4


def plugin_error_short_text(plugin):
    """ Return small version of description of missing module dependencies
    for displaying in plugin markup """
    if not plugin.error:
        return ""

    # get lists
    modules = plugin.missing_modules
    dbus = plugin.missing_dbus

    # convert to strings
    if modules:
        modules = "<small><b>%s</b></small>" % ', '.join(modules)
    if dbus:
        ifaces = [f"{a}:{b}" for (a, b) in dbus]
        dbus = "<small><b>%s</b></small>" % ', '.join(ifaces)

    # combine
    if modules and not dbus:
        text = '\n'.join((GnomeConfig.miss2, modules))
    elif dbus and not modules:
        text = '\n'.join((GnomeConfig.dmiss2, dbus))
    elif modules and dbus:
        text = '\n'.join((GnomeConfig.bmiss2, modules, dbus))
    else:
        text = ""

    return text


def plugin_error_text(plugin):
    """ Generate some helpful text about missing module dependencies. """
    if not plugin.error:
        return GnomeConfig.CANLOAD

    # describe missing dependencies
    text = f"<b>{GnomeConfig.CANNOTLOAD}</b>. \n"
    # get lists
    modules = plugin.missing_modules
    dbus = plugin.missing_dbus

    # convert to strings
    if modules:
        modules = "<small><b>%s</b></small>" % ', '.join(modules)
    if dbus:
        ifaces = [f"{a}:{b}" for (a, b) in dbus]
        dbus = "<small><b>%s</b></small>" % ', '.join(ifaces)

    # combine
    if modules and not dbus:
        text += '\n'.join((GnomeConfig.MODULEMISSING, modules))
    elif dbus and not modules:
        text += '\n'.join((GnomeConfig.DBUSMISSING, dbus))
    elif modules and dbus:
        text += '\n'.join((GnomeConfig.MODULANDDBUS, modules, dbus))
    else:
        text += GnomeConfig.UNKNOWN

    return text


def plugin_markup(column, cell, store, iterator, self):

    """ Callback to set the content of a PluginTree cell.

    See PluginsDialog._init_plugin_tree().
    """
    name = store.get_value(iterator, PLUGINS_COL_NAME)
    desc = store.get_value(iterator, PLUGINS_COL_SHORT_DESC)

    plugin_id = store.get_value(iterator, PLUGINS_COL_ID)
    plugin = self.pengine.get_plugin(plugin_id)
    error_text = plugin_error_short_text(plugin)
    if error_text != "":
        text = f"<b>{name}</b>\n{desc}\n<i>{error_text}</i>"
    else:
        text = f"<b>{name}</b>\n{desc}"

    cell.set_property('markup', text)
    cell.set_property('sensitive',
                      store.get_value(iterator, PLUGINS_COL_ACTIVATABLE))


class PluginsDialog():
    """ Dialog for Plugins configuration """

    def __init__(self, requester):
        self.req = requester
        self.config = self.req.get_config("plugins")
        builder = Gtk.Builder()

        builder.add_from_file(ViewConfig.PLUGINS_UI_FILE)

        self.dialog = builder.get_object("PluginsDialog")
        self.dialog.set_title(_("Plugins"))
        self.plugin_tree = builder.get_object("PluginTree")
        self.plugin_configure = builder.get_object("plugin_configure")
        self.plugin_about = builder.get_object("PluginAboutDialog")
        self.plugin_depends = builder.get_object('PluginDepends')

        self.pengine = PluginEngine()

        # see constants PLUGINS_COL_* for column meanings
        self.plugin_store = Gtk.ListStore(str, bool, str, str, bool)

        builder.connect_signals({
                                'on_PluginsDialog_delete_event': self.on_close,
                                'on_PluginTree_cursor_changed': self.on_plugin_select,
                                'on_plugin_about': self.on_plugin_about,
                                'on_plugin_configure': self.on_plugin_configure,
                                'on_PluginAboutDialog_close': self.on_plugin_about_close,
                                })

    def _init_plugin_tree(self):
        """ Initialize the PluginTree Gtk.TreeView.

        The format is modelled after the one used in gedit; see
        http://git.gnome.org/browse/gedit/tree/gedit/gedit-plugin-mapnager.c
        """
        # force creation of the Gtk.ListStore so we can reference it
        self._refresh_plugin_store()

        # renderer for the toggle column
        renderer = Gtk.CellRendererToggle()
        renderer.set_property('xpad', 6)
        renderer.connect('toggled', self.on_plugin_toggle)
        # toggle column
        column = Gtk.TreeViewColumn(None, renderer, active=PLUGINS_COL_ENABLED,
                                    activatable=PLUGINS_COL_ACTIVATABLE,
                                    sensitive=PLUGINS_COL_ACTIVATABLE)
        self.plugin_tree.append_column(column)

        # plugin name column
        column = Gtk.TreeViewColumn()
        column.set_spacing(6)
        # text renderer for the plugin name column
        name_renderer = Gtk.CellRendererText()
        name_renderer.set_property('ellipsize', Pango.EllipsizeMode.END)
        column.pack_start(name_renderer, True)
        column.set_cell_data_func(name_renderer, plugin_markup, self)

        self.plugin_tree.append_column(column)

        # finish setup
        self.plugin_tree.set_model(self.plugin_store)
        self.plugin_tree.set_search_column(2)

    def _refresh_plugin_store(self):
        """ Refresh status of plugins and put it in a Gtk.ListStore """
        self.plugin_store.clear()
        self.pengine.recheck_plugin_errors(True)
        for name, plugin in self.pengine.plugins.items():
            # activateable if there is no error
            self.plugin_store.append((name, plugin.enabled, plugin.full_name,
                                      plugin.short_description,
                                      not plugin.error))

    def activate(self):
        """ Refresh status of plugins and show the dialog """
        if len(self.plugin_tree.get_columns()) == 0:
            self._init_plugin_tree()
        else:
            self._refresh_plugin_store()
        self.dialog.show_all()

    def on_close(self, widget, data=None):
        """ Close the plugins dialog."""
        self.dialog.hide()
        return True

    def on_plugin_toggle(self, widget, path):
        """Toggle a plugin enabled/disabled."""
        iterator = self.plugin_store.get_iter(path)
        plugin_id = self.plugin_store.get_value(iterator, PLUGINS_COL_ID)
        plugin = self.pengine.get_plugin(plugin_id)
        plugin.enabled = not self.plugin_store.get_value(iterator, PLUGINS_COL_ENABLED)
        plugins_enabled = self.config.get("enabled")
        plugins_disabled = self.config.get("disabled")
        if plugin.enabled:
            self.pengine.activate_plugins([plugin])
            plugins_enabled.append(plugin.module_name)
            if plugin.module_name in plugins_disabled:
                plugins_disabled.remove(plugin.module_name)
        else:
            self.pengine.deactivate_plugins([plugin])
            plugins_disabled.append(plugin.module_name)
            if plugin.module_name in plugins_enabled:
                plugins_enabled.remove(plugin.module_name)

        self.config.set("enabled", plugins_enabled)
        self.config.set("disabled", plugins_disabled)
        self.plugin_store.set_value(iterator, PLUGINS_COL_ENABLED, plugin.enabled)
        self._update_plugin_configure(plugin)

    def on_plugin_select(self, plugin_tree):
        """ Callback when user select/unselect a plugin

        Update the button "Configure plugin" sensitivity """
        model, iterator = plugin_tree.get_selection().get_selected()
        if iterator is not None:
            plugin_id = model.get_value(iterator, PLUGINS_COL_ID)
            plugin = self.pengine.get_plugin(plugin_id)
            self._update_plugin_configure(plugin)

    def _update_plugin_configure(self, plugin):
        """ Enable the button "Configure Plugin" appropriate. """
        configurable = plugin.active and plugin.is_configurable()
        self.plugin_configure.set_property('sensitive', configurable)

    def on_plugin_configure(self, widget):
        """ Show the dialog for plugin configuration """
        _, iterator = self.plugin_tree.get_selection().get_selected()
        if iterator is None:
            return
        plugin_id = self.plugin_store.get_value(iterator, PLUGINS_COL_ID)
        plugin = self.pengine.get_plugin(plugin_id)
        plugin.instance.configure_dialog(self.dialog)

    def on_plugin_about(self, widget):
        """ Display information about a plugin. """
        _, iterator = self.plugin_tree.get_selection().get_selected()
        if iterator is None:
            return
        plugin_id = self.plugin_store.get_value(iterator, PLUGINS_COL_ID)
        plugin = self.pengine.get_plugin(plugin_id)

        # FIXME About plugin dialog looks much more different than
        # it is in the current trunk
        # FIXME repair it!
        # FIXME Author is not usually set and is preserved from
        # previous plugin... :/
        self.plugin_about.set_program_name(plugin.full_name)
        self.plugin_about.set_version(plugin.version)
        authors = plugin.authors
        if isinstance(authors, str):
            authors = "\n".join(author.strip()
                                for author in authors.split(','))
            authors = [authors, ]
        self.plugin_about.set_authors(authors)
        description = plugin.description.replace(r'\n', "\n")
        self.plugin_about.set_comments(description)
        self.plugin_depends.set_label(plugin_error_text(plugin))
        self.plugin_about.show_all()

    def on_plugin_about_close(self, widget, data=None):

        """ Close the PluginAboutDialog. """
        self.plugin_about.hide()
        return True