from pkgutil import get_data

from gi.repository import Gtk

from . import plugin, plugin_loader
from .gtk_extras import optionTable


class PreferencesGui(plugin_loader.Pluggable):
    """The glue between our preferences dialog UI and our prefs modules.

    Instead of "connecting", as would be normal with pygtk objects, we set up handlers in the
    apply_prefs_dic which contains preference-handlers for each preference we wish.

    {'preference_name':lambda foo (name,val): bar(name,val)}
    """

    INDEX_PAGE = 0
    CARD_PAGE = 1
    SHOP_PAGE = 2

    def __init__(
        self,
        prefs,
        radio_options={"shop_handle_optional": {"optional_ask": 0, "optional_add": 1, "optional_dont_add": -1}},
        toggle_options={
            "remember_optionals_by_default": "remember_optionals_by_default",
            "readableUnits": "toggle_readable_units",
            "useFractions": "useFractions",
            "showToolbar": "show_toolbar",
            #'email_include_body':'email_body_checkbutton',
            #'email_include_html':'email_html_checkbutton',
            #'emailer_dont_ask':'remember_email_checkbutton',
        },
        number_options={"recipes_per_page": "recipesPerPageSpinButton"},
        buttons={},
        # buttons = {'clear_remembered_optional_button':
    ):
        """Set up our PreferencesGui

        radio_options is a dictionary of preferences controlled by radio buttons.
                      {preference_name: {radio_widget: value,
                                         radio_widget: value, ...}
                                         }

        toggle_options is a dictionary of preferences controlled by toggle buttons.
                      {preference_name: toggle_widget_name}
        buttons = {button_name : callback}

        """

        self.prefs = prefs
        self.ui = Gtk.Builder()
        self.ui.add_from_string(get_data("gourmand", "ui/preferenceDialog.ui").decode())
        self.notebook = self.ui.get_object("notebook")
        # pref name: {'buttonName':VALUE,...}
        self.radio_options = radio_options
        self.connect_radio_buttons()
        self.toggle_options = toggle_options
        self.apply_prefs_dic = {}
        self.widget_sensitivity_dic = {
            # pref : {'value':('widget':True,'widget':False,'widget':True...)}
            "shop_handle_optional": {
                0: {"remember_optionals_by_default": True, "clear_remembered_optional_button": True},
                1: {"remember_optionals_by_default": False, "clear_remembered_optional_button": False},
                -1: {"remember_optionals_by_default": False, "clear_remembered_optional_button": False},
            }
        }
        self.connect_toggle_buttons()
        self.buttons = buttons
        self.connect_buttons()
        self.number_options = number_options
        self.connect_number_options()
        self.build_pref_dictionary()
        self.set_widgets_from_prefs()
        self.pref_tables = {}
        self.ui.get_object("close_button").connect("clicked", lambda *args: self.hide_dialog())
        plugin_loader.Pluggable.__init__(self, [plugin.PrefsPlugin])

    def build_pref_dictionary(self):
        """Build our preferences dictionary pref_dic

        preference: BOOLEAN_WIDGET|{VALUE:RADIO_WIDGET,VALUE:RADIO_WIDGET...}
                    METHOD_TO_BE_HANDED_PREF_VALUE

        pref_dic will be used to e.g. set default values and watch
        changing preferences.
        """
        self.pref_dic = {}
        for pref, widget in list(self.toggle_options.items()):
            self.pref_dic[pref] = ("TOGGLE", widget)
        for pref, widgdic in list(self.radio_options.items()):
            self.pref_dic[pref] = {}
            # create a dictionary by value (reversed dictionary)...
            for widg, val in list(widgdic.items()):
                self.pref_dic[pref][val] = widg
        self.d = self.ui.get_object("dialog")
        self.d.connect("delete-event", self.hide_dialog)

    def set_widgets_from_prefs(self):
        for k in list(self.pref_dic.keys()):
            # print("---", k, self.prefs, self.prefs[k])
            if k in self.prefs:
                self.update_pref(k, self.prefs[k])

    def update_pref(self, pref, value):
        """Update GUI to reflect value 'value' of preference 'pref'."""
        if pref in self.pref_dic:
            action = self.pref_dic[pref]
            if isinstance(action, dict):
                # we fail if action is no
                widg = action[value]
                act, act_args = ("set_active", True)
            elif action[0] == "TOGGLE":
                act, act_args = ("set_active", value)
                widg = action[1]
            # in the future, we can handle Entries, etc...
            if isinstance(widg, str):
                widg = self.ui.get_object(widg)
            getattr(widg, act)(act_args)
            self.update_sensitivity_for_pref(pref, value)

    def show_dialog(self, page=None):
        """present our dialog."""
        self.d.present()
        if page:
            self.notebook.set_current_page(page)

    def hide_dialog(self, *args):
        self.d.hide()
        return True

    def connect_buttons(self):
        for b, cb in list(self.buttons.items()):
            self.ui.get_object(b).connect("clicked", cb)

    def connect_toggle_buttons(self):
        """Connect signals for toggle buttons in self.toggle_options."""
        for pref, widget in list(self.toggle_options.items()):
            self.ui.get_object(widget).connect("toggled", self.toggle_callback, pref)

    def toggle_callback(self, button, pref_name):
        """Set preference 'pref_name' in response to toggle event on button."""
        self.set_pref(pref_name, button.get_active())

    def connect_radio_buttons(self):
        """Connect radio button signals to properly set preferences on toggle."""
        for pref_name, pref_dic in list(self.radio_options.items()):
            for button, val in list(pref_dic.items()):
                self.ui.get_object(button).connect("toggled", self.radio_callback, pref_name, val)

    def radio_callback(self, button, pref_name, true_val=True):
        """Call back for radio button: if we are on, we set the pref to true_val."""
        if button.get_active():
            self.set_pref(pref_name, true_val)

    def connect_number_options(self):
        for pref_name, widgetname in list(self.number_options.items()):
            widget = self.ui.get_object(widgetname)
            if hasattr(widget, "get_value"):
                get_method = "get_value"
            elif hasattr(widget, "get_text"):

                def get_method(*args):
                    return float(widget.get_text())

            else:
                print("widget", widgetname, widget, "is not very numberlike!")
                return
            curval = self.prefs.get(pref_name, None)
            if curval:
                try:
                    widget.set_value(curval)
                except AttributeError:
                    widget.set_text(str(curval))
            if isinstance(widget, Gtk.SpinButton):
                widget.get_adjustment().connect("value-changed", self.number_callback, pref_name, get_method)
            else:
                widget.connect("changed", self.number_callback, pref_name, get_method)

    def number_callback(self, widget, pref_name, get_method="get_value"):
        self.set_pref(pref_name, getattr(widget, get_method)())

    def set_pref(self, name, value):
        """Set preference 'name' to value 'value'

        Possibly apply the preference using callback looked up in
        apply_prefs_dic (callback must take name and value of pref as
        arguments.
        """
        self.prefs[name] = value
        if name in self.apply_prefs_dic:
            self.apply_prefs_dic[name](name, value)

    def update_sensitivity_for_pref(self, name, value):
        try:
            for k, v in list(self.widget_sensitivity_dic[name][value].items()):
                self.ui.get_object(k).set_sensitive(v)
        except KeyError:
            pass

    def add_widget(self, target_widget, child_widget):
        """Add child_widget to target_widget"""
        if isinstance(target_widget, str):
            target_widget = self.ui.get_object(target_widget)
        if isinstance(child_widget, str):
            child_widget = self.ui.get_object(child_widget)
        target_widget.add(child_widget)
        target_widget.show_all()

    def add_pref_table(self, options, target_widget, callback=None):
        """Add a preference table based on an option list 'options' to the
        target_widget 'target_widget' (either a widget or a glade-reference)

        The options need to be appropriate for an OptionTable.

        The callback will be handed the options (as returned by
        OptionTable) each time OptionTable is changed.
        """
        table = optionTable.OptionTable(options=options, changedcb=self.preftable_callback)
        self.pref_tables[table] = callback
        self.add_widget(target_widget, table)

    def preftable_callback(self, widget):
        for table, cb in list(self.pref_tables.items()):
            if widget in table.get_children():
                # then we know who's preferences we care about...
                table.apply()
                if cb:
                    cb(table.options)
                return
        print("Oops: we couldn't handle widget %s" % widget)


if __name__ == "__main__":

    class FauxPrefs(dict):
        def __init__(self, *args, **kwargs):
            self.set_hooks = []
            dict.__init__(self, *args, **kwargs)

        def __setitem__(self, k, v):
            print("k:", k)
            print("v:", v)
            dict.__setitem__(self, k, v)
            for h in self.set_hooks:
                print("runnnig hook")
                h(k, v)

    gf = "/home/tom/Projects/grm-db-experiments/glade/preferenceDialog.ui"

    p = PreferencesGui(FauxPrefs(), gf)

    def printstuff(*args):
        print(args)

    p.add_pref_table([["Toggle Option", True], ["String Option", "Hello"], ["Integer Option", 1], ["Float Option", float(3)]], "cardViewVBox", printstuff)
    p.show_dialog()
    Gtk.main()
