from durus.utils import str_to_int8
from qp.fill.html import javascript, div
from qp.fill.widget import StringWidget, CompositeWidget
from qp.lib.util import integer
from qp.pub.common import get_publisher


class CallbackSelectWidget (CompositeWidget):
    """
    This is a search-select widget that relies on a callback to provide the full 
    list of items from which one will be selected.
    """
    def __init__(self, name, *args, **kwargs):
        CompositeWidget.__init__(self, name, *args, **kwargs)
        self.key_id = "key_%s" % self.name
        self.description_id = "desc_%s" % self.name
        self.choices_id = "choices_%s" % self.name
        self.selected_id = "selected_%s" % self.name
        self.add(StringWidget, 'key', value=str(self.value_to_key(self.value)), id=self.key_id, style="display:none")
        self.add(StringWidget, 'description', value=self.value_to_description(self.value), id=self.description_id,
            autocomplete="off")

    callback_path = '/callback/user_select'

    @classmethod
    def callback:xml(klass):
        for user in sorted(get_publisher().gen_active_users(), key=lambda x: klass.value_to_description(x).lower()):
            div(klass.value_to_description(user), key=klass.value_to_key(user), css_class="callback_option")

    @classmethod
    def value_to_key(klass, value):
        if value is None:
            return ''
        return str_to_int8(value._p_durus_id)

    @classmethod
    def key_to_value(klass, key):
        if type(key) is not int:
            key = integer(key)
        if key is None or key < 0:
            return None
        return get_publisher().get_connection().get(key)

    @staticmethod
    def value_to_description(value):
        return str(value)

    def filter_js:xml(self):
        # Executed on focus and keyup events in the description widget.
        "function () {\n"
        "    var query = document.getElementById('%s').value.toLowerCase();" % self.description_id
        "    var choices = document.getElementById('%s');" % self.choices_id
        "    var match = function (n) {return -1 != genlib.node_get_text_data_string(n).toLowerCase().indexOf(query)};"
        "    genlib.classify_children(choices, match, 'matched');"
        "    choices.style.display = 'block';"
        "}"

    def initialize_js:xml(self):
        # Executed when the callback results are here.
        "function (e) {\n"
        "    var filter = %s;" % self.filter_js()
        "    var description = document.getElementById('%s');" % self.description_id
        "    var choices = document.getElementById('%s');" % self.choices_id
        "    var key = document.getElementById('%s');" % self.key_id
        "    description.onkeyup = filter;"
        "    description.onfocus = filter;"
        "    description.onclick = function () {this.value = '';};"
        "    description.onblur = function () { setTimeout(function () {choices.style.display = 'none';}, 500)};"
        "    var children = genlib.get_child_elements(e);"
        "    for (var i in children) {\n"
        "        var child = children[i];"
        "        genlib.node_add_class(child, 'not_matched');"
        "        child.onclick = function (e) {"
        "            key.value = this.attributes.key.value;"
        "            description.value = genlib.node_get_text_data_string(this);"
        "            choices.style.display = 'none';"
        "        };"
        "        genlib.install_hovering(child);"
        "    }"
        "}"

    def render_content:xml(self):
        div(self.get_widget('key').render(),
            div(id=self.selected_id),
            self.get_widget('description').render(),
            div(javascript(
                "genlib.load_from('%s', '%s', %s)\n" % (self.choices_id, self.callback_path, self.initialize_js())),
                css_class="choices",
                id=self.choices_id))

    def _parse(self, request=None):
        CompositeWidget._parse(self, request=request)
        if self.get('key') and self.get('description'):
            self.value = self.key_to_value(self.get('key'))
        else:
            self.value = None


class CallbackMultipleSelectWidget (CallbackSelectWidget):
    """
    This is a search-select widget that relies on a callback to provide the full 
    list of items from which a subset will be selected.
    """
    def __init__(self, name, *args, **kwargs):
        CompositeWidget.__init__(self, name, *args, **kwargs)
        self.key_id = "key_%s" % self.name
        self.description_id = "desc_%s" % self.name
        self.choices_id = "choices_%s" % self.name
        self.add(StringWidget, 'key', value="".join(str(self.value_to_key(v)) + " " for v in self.value or []), id=self.key_id, style="display:none")
        self.add(StringWidget, 'description', value=None, id=self.description_id,
            autocomplete="off")
        self.selected_id = "selected_%s" % self.name

    def initialize_js:xml(self):
        # Executed when the callback results are here.
        "function (e) {\n"
        "    var filter = %s;" % self.filter_js()
        "    var description = document.getElementById('%s');" % self.description_id
        "    var choices = document.getElementById('%s');" % self.choices_id
        "    var key = document.getElementById('%s');" % self.key_id
        "    var current_keys = key.value.split(' ');"
        "    var selected = document.getElementById('%s');" % self.selected_id
        "    var choices_children = genlib.get_child_elements(choices);"
        "    description.onkeyup = filter;"
        "    description.onfocus = filter;"
        "    description.onclick = function () {this.value = '';};"
        "    description.onblur = function () { setTimeout(function () {choices.style.display = 'none';}, 500)};"
        "    for (var i in choices_children) {\n"
        "        var child = choices_children[i];"
        "        genlib.install_hovering(child);"
        "        genlib.node_add_class(child, 'not_matched');"
        "        var child_key = child.attributes.key.value;"
        "        for (j in current_keys) {"
        "            if (current_keys[j] == child_key) {"
        "                genlib.node_add_class(child, 'already_selected');"
        "            }"
        "        };"
        "        child.onclick = function (e) {"
        "            genlib.node_add_class(this, 'already_selected');"
        "            var this_key = this.attributes.key.value;"
        "            key.value = key.value + this_key + ' ';"
        "            description.value = '';"
        "            choices.style.display = 'none';"
        "            var selected_children = genlib.get_child_elements(selected);"
        "            for (var k in selected_children) {"
        "                var selected_child = selected_children[k];"
        "                if (selected_child.attributes.key.value == this_key) {"
        "                    genlib.node_remove_class(selected_child, 'not_matched');"
        "                    genlib.node_add_class(selected_child, 'matched');"
        "                    break;"
        "                 }"
        "            }"
        "        }"
        "    };"
        "    genlib.node_replace_children(selected, genlib.get_child_elements(e.cloneNode(true)));"
        "    var selected_children = genlib.get_child_elements(selected);"
        "    for (var i in selected_children) {\n"
        "        var child = selected_children[i];"
        "        genlib.install_hovering(child);"
        "        var child_key = child.attributes.key.value;"
        "        for (j in current_keys) {"
        "            if (current_keys[j] == child_key) {"
        "                genlib.node_add_class(child, 'matched');"
        "                genlib.node_remove_class(child, 'not_matched');"
        "                genlib.node_remove_class(child, 'already_selected');"
        "            }"
        "        };"
        "        child.onclick = function (e) {"
        "            var this_key = this.attributes.key.value;"
        "            key.value = key.value.replace(this_key + ' ', '');"
        "            this.style.display = 'none';"
        "            for (var k in genlib.get_child_elements(choices)) {"
        "                var choices_child = choices_children[k];"
        "                if (choices_child.attributes.key.value == this_key) {"
        "                    genlib.node_remove_class(choices_child, 'already_selected');"
        "                    break;"
        "                }"
        "             }"
        "        }"
        "    }"
        "}"

    def render_content:xml(self):
        div(self.get_widget('key').render(),
            div(id=self.selected_id, css_class="selected_choices"),
            self.get_widget('description').render(),
            div(javascript(
                "genlib.load_from('%s', '%s', %s)\n" % (self.choices_id, self.callback_path, self.initialize_js())),
                css_class="choices",
                id=self.choices_id))

    def _parse(self, request=None):
        CompositeWidget._parse(self, request=request)
        if self.get('key'):
            self.value = [v for v in map(self.key_to_value, self.get('key').split()) if v is not None]
        else:
            self.value = None
