from gi.repository import Gtk
import os.path
from . import gglobals
from .gtk_extras import optionTable
from . import plugin_loader, plugin

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,
        uifile=os.path.join(gglobals.uibase,
                            'preferenceDialog.ui'),
        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

        uifile points us to our UI file

        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_file(uifile)
        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.prefs.set_hooks.append(self.update_pref)
        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'):
                get_method=lambda *args: 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'
    import sys
    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()
