#!/usr/bin/env python3

import gi
import os
import sys

gi.require_version('Gtk', '3.0')

from gi.repository import Gtk, Gio, Gdk, GLib

adjustments = {
    "g3k": ["desktop-cursor-width-meters", "input-poll-rate-ms",
            "pointer-tip-pulse-alpha", "pointer-tip-width-meters"],
    "xrd": ["analog-threshold", "grab-window-threshold", "scroll-threshold",
            "scroll-to-push-ratio", "scroll-to-scale-ratio",
            "shake-compensation-duration-ms", "shake-compensation-threshold"]
}

switches = {
    "g3k": ["pointer-tip-keep-apparent-size", "pointer-ray-enabled",
            "enable-sound"],
    "xrd": ["shake-compensation-enabled", "pin-new-windows",
            "show-only-pinned-startup"]
}
color_buttons = {
    "g3k": ["pointer-tip-active-color", "pointer-tip-passive-color"],
    "xrd": []
}

used_keys = {
    "g3k": adjustments["g3k"] + switches["g3k"] + color_buttons["g3k"] + ["pointer-tip-resolution"],
    "xrd": adjustments["xrd"] + switches["xrd"],
}


def variant_to_rgba(var):
    r, g, b = var.unpack()
    return Gdk.RGBA(r, g, b, 1.0)


# noinspection PyArgumentList
def rgba_to_variant(rgba):
    return GLib.Variant.new_tuple(GLib.Variant.new_double(rgba.red),
                                  GLib.Variant.new_double(rgba.green),
                                  GLib.Variant.new_double(rgba.blue))


# noinspection PyArgumentList
class XrdSettings(Gtk.Application):
    def reset(self, button):
        for s in ["g3k", "xrd"]:
            for k in self.settings[s].keys():
                key = self.schema[s].get_key(k)
                self.settings[s].set_value(k, key.get_default_value())

    def update_shake_compensation_rows(self):
        enabled = self.settings["xrd"].get_value("shake-compensation-enabled")

        rows = [self.rows[x] for x in ["shake-compensation-duration-ms", "shake-compensation-threshold"]]
        for row in rows:
            row.set_visible(enabled)

    def store_color_setting(self, button, key):
        rgba = button.get_rgba()
        variant = rgba_to_variant(rgba)
        self.settings["g3k"].set_value(key, variant)

    def store_pointer_tip_resolution_setting(self, combo_box):
        it = combo_box.get_active_iter()
        store = combo_box.get_model()

        width = store[it][1]
        var = GLib.Variant.new_tuple(GLib.Variant.new_int32(width),
                                     GLib.Variant.new_int32(width))

        self.settings["g3k"].set_value('pointer-tip-resolution', var)

    def store_radio_setting(self, button, key, value):
        if button.get_active():
            self.settings.set_string(key, value)

    def update_color_widget(self, name):
        active_tip_color = self.settings["g3k"].get_value(name)
        rgba = variant_to_rgba(active_tip_color)
        self.color_buttons[name].set_rgba(rgba)

    def update_pointer_tip_resolution_widget(self):
        pointer_tip_resolution = self.settings["g3k"].get_value('pointer-tip-resolution')
        store = self.pointer_tip_resolution_combo_box.get_model()

        for row in store:
            name, width = row
            if pointer_tip_resolution[0] == width:
                self.pointer_tip_resolution_combo_box.set_active_iter(row.iter)

    def _changed_cb(self, settings, key):
        if key in color_buttons:
            self.update_color_widget(key)
        elif key == "pointer-tip-resolution":
            self.update_pointer_tip_resolution_widget()
        elif key == "shake-compensation-enabled":
            self.update_shake_compensation_rows()

    def init_settings(self):
        available_keys = {}
        unused_keys = {}
        for s in ["g3k", "xrd"]:
            available_keys[s] = self.settings[s].keys()
            unused_keys[s] = [k for k in available_keys[s] if k not in used_keys[s]]
            if unused_keys[s]:
                print("Unused keys", unused_keys[s])

            for k in used_keys[s]:
                key = self.schema[s].get_key(k)
                self.summaries[k].set_text(key.get_summary())
                self.rows[k].set_tooltip_text(key.get_description())

        self.update_pointer_tip_resolution_widget()
        self.pointer_tip_resolution_combo_box.connect("changed",
                                                      self.store_pointer_tip_resolution_setting)

        for s in ["g3k", "xrd"]:
            for b in color_buttons[s]:
                self.update_color_widget(b)

            for a in adjustments[s]:
                self.settings[s].bind(a, self.adjustments[a], "value",
                                      Gio.SettingsBindFlags.DEFAULT)

            for sw in switches[s]:
                self.settings[s].bind(sw, self.switches[sw], "active",
                                      Gio.SettingsBindFlags.DEFAULT)

        self.update_shake_compensation_rows()

        for s in ["g3k", "xrd"]:
            self.settings[s].connect("changed", self._changed_cb)

    def do_activate(self):
        self.window.present()
        Gtk.main()

    @staticmethod
    def init_widgets(builder, d, names, suffix):
        for n in names:
            glade_name = n.replace("-", "_") + "_" + suffix
            d[n] = builder.get_object(glade_name)

    def __init__(self):
        Gtk.Application.__init__(self, application_id="org.xrdesktop.settings",
                                 flags=Gio.ApplicationFlags.FLAGS_NONE)
        builder = Gtk.Builder()

        script_path = os.path.realpath(__file__)
        script_dir = os.path.dirname(script_path)
        ui_path = os.path.join(script_dir, "xrd-settings.ui")
        if not os.path.isfile(ui_path):
            from xrdesktop.config import pkgdatadir
            resource_file = os.path.join(pkgdatadir, "org.xrdesktop.settings.gresource")
            resources = Gio.resource_load(resource_file)
            Gio.resources_register(resources)
            builder.add_from_resource("/org/xrdesktop/xrd-settings.ui")
        else:
            builder.add_from_file(ui_path)

        self.adjustments = {}
        self.switches = {}
        self.color_buttons = {}
        self.rows = {}
        self.summaries = {}

        for s in ["g3k", "xrd"]:
            self.init_widgets(builder, self.adjustments, adjustments[s], "adjustment")
            self.init_widgets(builder, self.switches, switches[s], "switch")
            self.init_widgets(builder, self.color_buttons, color_buttons[s], "button")
            self.init_widgets(builder, self.rows, used_keys[s], "row")
            self.init_widgets(builder, self.summaries, used_keys[s], "summary")

        self.pointer_tip_resolution_combo_box = builder.get_object("pointer_tip_resolution_combo_box")
        self.reset_button = builder.get_object("reset")

        self.window = builder.get_object("window")

        self.settings = {
            "xrd": Gio.Settings.new(schema_id="org.xrdesktop"),
            "g3k": Gio.Settings.new(schema_id="org.g3k")
        }
        src = Gio.SettingsSchemaSource.get_default()

        self.schema = {
            "xrd": src.lookup("org.xrdesktop", True),
            "g3k": src.lookup("org.g3k", True)
        }
        if self.schema["xrd"] is None or self.schema["g3k"] is None:
            dialog = Gtk.MessageDialog(
                transient_for=self.window,
                flags=0,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text="ERROR: GLib settings schema not found",
            )
            dialog.format_secondary_text("Schema \"org.xrdesktop\" not found.")
            dialog.run()
            dialog.destroy()
            sys.exit(1)

        self.init_settings()

        for s in ["g3k", "xrd"]:
            for b in color_buttons[s]:
                self.color_buttons[b].connect("color-set", self.store_color_setting, b)
        self.window.connect("destroy", Gtk.main_quit)
        self.reset_button.connect("clicked", self.reset)

    def on_about(self, action, param):
        from xrdesktop.config import version
        about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True,
                                       logo_icon_name="org.xrdesktop.settings",
                                       license_type="mit-x11",
                                       program_name="xrdesktop",
                                       copyright="Copyright 2019 - 2020 Collabora Ltd.",
                                       website="https://gitlab.freedesktop.org/xrdesktop",
                                       website_label="Freedesktop Gitlab",
                                       authors=["Lubosz Sarnecki", "Christoph Haag"],
                                       version=version)
        about_dialog.run()
        about_dialog.destroy()

    def on_help(self, action, param):
        wiki_url = "https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/wikis/home"
        Gtk.show_uri_on_window(self.window, wiki_url, Gdk.CURRENT_TIME)

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

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

        action = Gio.SimpleAction.new("help", None)
        action.connect("activate", self.on_help)
        self.add_action(action)

        self.window.set_application(self)


if __name__ == "__main__":
    app = XrdSettings()
    app.run(sys.argv)
