class AjaxChosenSelectionWidget(AjaxChosenBase):
    """Autocomplete widget that allows single selection.
    """
    klass = u'ajaxchosen-selection-widget'
    js_template = ChosenBase.ajax_js_template
    width = "280px"
    promptMessage = _('Enter value')
class AjaxChosenMultiSelectionWidget(MultiAjaxChosenBase):
    """Autocomplete widget that allows multiple selection
    """
    klass = u'ajaxchosen-multiselection-widget'
    js_template = ChosenBase.ajax_js_template
    width = "100%"
    promptMessage = _('Enter values')
class ChosenMultiSelectionWidget(MultiChosenBase):
    """widget that allows multiple selection
    """
    populate_select = True
    klass = u'chosen-multiselection-widget'
    width = "100%"
    promptMessage = _('Select values')
class ChosenBase(select.SelectWidget, Explicit):
    implementsOnly(IChosenWidget)
    security = ClassSecurityInfo()
    security.declareObjectPublic()

    _bound_source = None

    input_template = ViewPageTemplateFile('input.pt')
    display_template = ViewPageTemplateFile('display.pt')
    hidden_template = ViewPageTemplateFile('hidden.pt')

    # load values from bounded source at initialisation
    populate_select = False

    # Options passed to jQuery auto-completer
    onselect = ""
    readonly = False
    prompt = True
    promptMessage = _('Select a value here')
    noValueMessage = _('no value')
    noValueToken = _(u'(nothing)')
    no_results_text = _("No results found")
    style = "width:280px;"
    multiple = False
    allow_single_deselect = False
    search_url = "%s/++widget++%s/@@chosen-autocomplete-search"

    # JavaScript template
    js_template = """\
    (function($) {
        $().ready(function() {
            $('#%(id)s-select').data('klass','%(klass)s').data('title','%(title)s');
            $('#%(id)s-select').chosen({
                allow_single_deselect: %(allow_single_deselect)s,
                no_results_text: '%(no_results_text)s'
            });
            %(js_extra)s
        });
    })(jQuery);
    """
    ajax_js_template = """\
    (function($) {
      $().ready(function() {
          $('#%(id)s-select').data('klass','%(klass)s').data('title','%(title)s');
          $('#%(id)s-select').ajaxChosen(
            {
                method: '%(method)s',
                url: '%(url)s',
                dataType: '%(datatype)s'
            },
            %(ajax_callback)s,
            null,
            {allow_single_deselect:%(allow_single_deselect)s,
             no_results_text: '%(no_results_text)s'}
          );
          %(js_extra)s
      });
    })(jQuery);
    """
    method = 'GET'
    datatype = 'json'
    ajax_callback = """\
    function (data) {
        var terms = {};
        $.each(data, function (i, val) {
            terms[val[0]] = val[1];
        });
        return terms;
    }"""

    @property
    def source(self):
        """We need to bind the field to the context so that vocabularies
        appear as sources"""
        return self.field.bind(self.context).source

    @property
    def placeholder(self):
        return self.promptMessage

    @property
    def bound_source(self):
        if self._bound_source is None:
            source = self.source
            if IContextSourceBinder.providedBy(source):
                source = source(self.context)
            assert ISource.providedBy(source)
            self._bound_source = source
        return self._bound_source

    @property
    def autocomplete_url(self):
        """Generate the URL that returns autocomplete results for this form
        """
        form_url = self.request.getURL()
        return self.search_url % (form_url, self.name)


    # Override this to insert additional JavaScript
    def js_extra(self):
        return ""

    def __call__(self):
        self.update()
        return self.render()

    def render(self):
        utemplate = None
        if self.mode == z3c.form.interfaces.DISPLAY_MODE:
            utemplate = self.display_template
        if self.mode == z3c.form.interfaces.INPUT_MODE:
            utemplate = self.input_template
        if self.mode == z3c.form.interfaces.HIDDEN_MODE:
            utemplate = self.hidden_template
        if bool(utemplate):
            return utemplate(self)

    def update(self):
        # Allow the source to provide terms until we have more specific ones
        # from the query.
        # Things do not go well if self.terms is None
        self._bound_source = None
        source = self.bound_source
        terms = OrderedDict()

        # populate select values if needed
        if self.populate_select:
            for t in self.source:
                if not t.token in terms:
                    terms[t.token] = t

        # pre defined terms from context+source
        self.terms = SourceTerms(self.context, self.request, self.form, self.field, self, source)

        # If we have values in the request,
        # use these to get the terms.
        request_values = z3c.form.interfaces.NOVALUE
        # extract selected value
        if not self.ignoreRequest:
            request_values = self.extract(
                default=z3c.form.interfaces.NOVALUE)

        if request_values is not z3c.form.interfaces.NOVALUE:
            if not isinstance(request_values, (tuple, set, list)):
                request_values = (request_values,)

            for token in request_values:
                if not token or token == self.noValueToken:
                    continue
                try:
                    t = source.getTermByToken(token)
                    terms[t.token] = t
                except LookupError:
                    # Term no longer available
                    if not self.ignoreMissing:
                        raise

        # take the value from the current saved value
        # if there is an existing adapter allowing it
        if not self.ignoreContext:
            selection = zope.component.getMultiAdapter(
                (self.context, self.field),
                z3c.form.interfaces.IDataManager).query()
            if selection is z3c.form.interfaces.NOVALUE:
                selection = []
            elif not isinstance(selection,
                                (tuple, set, list)):
                selection = [selection]
            for value in selection:
                if not value:
                    continue
                try:
                    t = source.getTerm(value)
                    terms[t.token] = t
                except LookupError:
                    # Term no longer available
                    if not self.ignoreMissing:
                        raise
        # re-set terms with values from request
        self.terms = QueryTerms(self.context, self.request, self.form, self.field, self, terms.values())

        # update widget selected value if any
        select.SelectWidget.update(self)

    def js(self):
        return self.js_template % dict(
            id=self.id,
            url=self.autocomplete_url,
            klass=self.klass,
            allow_single_deselect=jsbool( self.allow_single_deselect),
            title=self.title,
            method = self.method,
            datatype = self.datatype,
            ajax_callback = self.ajax_callback,
            no_results_text=self.no_results_text,
            js_extra=self.js_extra())

    @property
    def items(self):
        items = []
        if self.terms is not None:
            # update() has been called
            if ((not self.required or self.prompt)
                and not bool(self.multiple)):
                message = self.noValueMessage
                if self.prompt:
                    message = self.promptMessage
                items.append({
                    'id': self.id + '-novalue',
                    'value': self.noValueToken,
                    'content': message,
                    'selected': self.value == []
                    })
            for count, term in enumerate(self.terms):
                selected = self.isSelected(term)
                id = '%s-%i' % (self.id, count)
                content = term.token
                if ITitledTokenizedTerm.providedBy(term):
                    content = translate(
                        term.title,
                        context=self.request,
                        default=term.title)
                items.append(
                    {'id':id,
                     'value':term.token,
                     'content':content,
                     'selected':selected})
        return items