from gettext import ngettext
from pkgutil import get_data

from gi.repository import Gtk

import gourmand.gglobals as gglobals
from gourmand.gtk_extras import cb_extras as cb
from gourmand.gtk_extras import dialog_extras as de
from gourmand.i18n import _


class FieldEditor:
    """A generic "value" editor for mucking about with the database."""

    values = "category", "cuisine", "source", "link"

    def __init__(self, rd, rg):
        self.field = None
        self.other_field = None
        self.rd = rd
        self.rg = rg
        self.ui = Gtk.Builder()
        self.ui.add_from_string(get_data("gourmand", "ui/valueEditor.ui").decode())
        self.__setup_widgets__()
        self.__setup_treeview__()
        self.ui.connect_signals(
            {
                "on_changeValueButton_toggled": self.changeValueButtonToggledCB,
                "on_fieldToEditCombo_changed": self.fieldChangedCB,
                "on_otherChangeCheckButton_toggled": self.otherChangeToggleCB,
                "on_otherExpander_activate": self.otherChangeToggleCB,
                "on_otherFieldCombo_changed": self.otherFieldComboChangedCB,
            }
        )

    def __setup_widgets__(self):
        for w in [
            "valueDialog",
            "treeview",
            "fieldToEditCombo",
            "newValueComboBoxEntry",
            "newValueEntry",
            "changeValueButton",
            "deleteValueButton",
            "forEachLabel",
            "otherExpander",
            "otherFieldCombo",
            "otherNewValueEntry",
            "otherNewValueComboBoxEntry",
            "otherValueBlurbLabel",
            "otherChangeCheckButton",
            "leaveValueButton",
        ]:
            setattr(self, w, self.ui.get_object(w))
        self.act_on_selection_widgets = [self.deleteValueButton, self.changeValueButton, self.newValueEntry, self.otherChangeCheckButton, self.leaveValueButton]
        # Set up the combo-widget at the top with the
        self.fields = [gglobals.REC_ATTR_DIC[v] for v in self.values]
        cb.set_model_from_list(self.fieldToEditCombo, self.fields)
        cb.set_model_from_list(self.otherFieldCombo, self.fields)
        self.newValueComboBoxEntry.set_sensitive(False)
        self.otherValueBlurbLabel.hide()
        self.newValueEntryCompletion = Gtk.EntryCompletion()
        self.newValueEntry.set_completion(self.newValueEntryCompletion)
        self.otherNewValueEntryCompletion = Gtk.EntryCompletion()
        self.otherNewValueEntry.set_completion(self.otherNewValueEntryCompletion)
        self.valueDialog.connect("response", self.dialog_response_cb)
        self.valueDialog.set_response_sensitive(Gtk.ResponseType.APPLY, False)

    def __setup_treeview__(self):
        renderer = Gtk.CellRendererText()
        # If we have gtk > 2.8, set up text-wrapping
        try:
            renderer.get_property("wrap-width")
        except TypeError:
            pass
        else:
            renderer.set_property("wrap-mode", Gtk.WrapMode.WORD)
            renderer.set_property("wrap-width", 400)
        col = Gtk.TreeViewColumn("Value", renderer, text=0)
        self.treeview.append_column(col)
        self.treeview.get_selection().connect("changed", self.treeViewSelectionChanged)
        self.treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)

    def changeValueButtonToggledCB(self, tb):
        if tb.get_active():
            self.newValueComboBoxEntry.set_sensitive(True)
        else:
            self.newValueComboBoxEntry.set_sensitive(False)

    def treeViewSelectionChanged(self, tvSelection):
        vals = self.get_selected_values(tvSelection)
        if len(vals) == 1:
            val_string = vals[0]
        elif len(vals) == 2:
            val_string = " or ".join(vals)
        elif len(vals) > 0:
            val_string = " or ".join([", ".join(vals[:-1]), vals[-1]])
        else:  # len(vals)==0
            self.forEachLabel.set_text(_("For each selected value"))
        if vals:
            self.val_string = val_string
            self.forEachLabel.set_text(_("Where %(field)s is %(value)s") % {"value": val_string, "field": self.field})
        self.valueDialog.set_response_sensitive(Gtk.ResponseType.APPLY, (vals and True or False))

    def fieldChangedCB(self, combobox):
        name = cb.cb_get_active_text(combobox)
        self.field = gglobals.NAME_TO_ATTR.get(name, name)
        self.populate_treeview()
        other_fields = self.fields[:]
        if self.field != "category":
            other_fields.remove(gglobals.REC_ATTR_DIC[self.field])
        cb.set_model_from_list(self.otherFieldCombo, other_fields)

    def otherFieldComboChangedCB(self, combobox):
        name = cb.cb_get_active_text(combobox)
        self.other_field = gglobals.NAME_TO_ATTR.get(name, name)
        if self.other_field == "category":
            self.otherValueBlurbLabel.hide()
        else:
            self.otherValueBlurbLabel.show()
        mod = self.make_model_for_field(self.other_field)
        self.otherNewValueComboBoxEntry.set_model(mod)

        if self.otherNewValueComboBoxEntry.get_entry_text_column() == -1:
            self.otherNewValueComboBoxEntry.set_entry_text_column(0)

        self.otherNewValueEntryCompletion.set_model(mod)
        self.otherNewValueEntryCompletion.set_text_column(0)

    def populate_treeview(self):
        assert self.field
        mod = self.make_model_for_field(self.field)
        self.treeview.set_model(mod)
        self.newValueComboBoxEntry.set_model(mod)

        if self.newValueComboBoxEntry.get_entry_text_column() == -1:
            self.newValueComboBoxEntry.set_entry_text_column(0)

        self.newValueEntryCompletion.set_model(mod)
        self.newValueEntryCompletion.set_text_column(0)

    def make_model_for_field(self, field):
        vals = self.rd.get_unique_values(field)
        mod = Gtk.ListStore(str)
        for v in vals:
            mod.append((v,))
        return mod

    def run(self):
        return self.valueDialog.run()

    def show(self):
        return self.valueDialog.show()

    def hide(self):
        return self.valueDialog.hide()

    def dialog_response_cb(self, dialog, response_id):
        if response_id == Gtk.ResponseType.CLOSE:
            self.valueDialog.hide()
        if response_id == Gtk.ResponseType.APPLY:
            criteria, table = self.get_criteria_and_table()
            count = self.rd.fetch_len(table, **criteria)
            count_text = ngettext("Change will affect %s recipe", "Change will affect %s recipes", count) % count
            if self.deleteValueButton.get_active():
                label = _("Delete %s where it is %s?") % (self.field, self.val_string)
                yes = Gtk.STOCK_DELETE
            else:
                label = _('Change %s from %s to "%s"?') % (self.field, self.val_string, self.newValueEntry.get_text())
                yes = "_Change"
            if de.getBoolean(
                label=label,
                sublabel="\n\n".join([count_text, _("<i>This change is not reversable.</i>")]),
                custom_yes=yes,
                custom_no=Gtk.STOCK_CANCEL,
                cancel=False,
            ):

                self.apply_changes(criteria, table)
                self.populate_treeview()

    def otherChangeToggleCB(self, widg):
        if widg != self.otherChangeCheckButton:
            self.otherChangeCheckButton.activate()
        if self.otherChangeCheckButton.get_active():
            self.otherExpander.set_expanded(True)
        else:
            self.otherExpander.set_expanded(False)

    def get_changes(self):
        if self.deleteValueButton.get_active():
            return {self.field: None}
        elif self.changeValueButton.get_active():
            value = self.newValueEntry.get_text()
            return {self.field: value}
        return {}  # 'Leave value as is' selected, another field is being set

    def get_other_changes(self):
        if self.otherChangeCheckButton.get_active():
            return {self.other_field: self.otherNewValueEntry.get_text()}
        else:
            return {}

    def get_selected_values(self, ts=None):
        if not ts:
            ts = self.treeview.get_selection()
        mod, paths = ts.get_selected_rows()
        values = []
        for p in paths:
            values.append(mod.get_value(mod.get_iter(p), 0))
        return values

    def get_criteria_and_table(self):
        values = self.get_selected_values()
        if len(values) > 1:
            criteria = {self.field: ("==", ("or", values))}
        elif len(values) == 1:
            criteria = {self.field: values[0]}
        if self.field == "category":
            table = self.rd.categories_table
        else:
            table = self.rd.recipe_table
        return criteria, table

    def apply_changes(self, criteria, table):
        changes = self.get_changes()
        other_changes = self.get_other_changes()
        if self.field != "category" and self.other_field != "category":
            changes.update(other_changes)
        elif other_changes:
            if self.other_field == "category":
                # Inefficient, but works with our current backend
                # interface... and shouldn't be called often, so we'll
                # deal with the ugliness for now
                for r in self.rd.fetch_all(self.rd.recipe_table, **criteria):
                    if not self.rd.fetch_one(self.rd.categories_table, {"id": r.id}):
                        self.rd.do_add_cat({"id": r.id, "category": other_changes["category"]})
            else:
                if self.field == "category":
                    IDs = [r.id for r in self.rd.fetch_all(self.rd.categories_table, **criteria)]
                    new_criteria = {"id": ("==", ("or", IDs))}
                    self.rd.update_by_criteria(self.rd.recipe_table, new_criteria, other_changes)
                else:
                    self.rd.update_by_criteria(self.rd.recipe_table, criteria, other_changes)
        if self.field == "category" and not changes["category"]:
            self.rd.delete_by_criteria(table, criteria)
        else:
            if self.field == "category":
                table = self.rd.categories_table
            else:
                table = self.rd.recipe_table
            self.rd.update_by_criteria(table, criteria, changes)
        self.rg.reset_search()


if __name__ == "__main__":
    import recipeManager

    rm = recipeManager.default_rec_manager()

    class DummyRG:
        def reset_search(self):
            pass

    w = Gtk.Window()
    b = Gtk.Button(label="edit me now")
    w.add(b)
    w.show_all()
    ve = FieldEditor(rm, DummyRG())
    b.connect("clicked", lambda *args: ve.run())
    w.connect("delete-event", Gtk.main_quit)
    Gtk.main()
