from django.utils.datastructures import MultiValueDict
from django import newforms as forms
from tagging.models import Tag
from utils import get_tag_name_list, clean, tagnames2str
from validators import isMultiWordTagList, NumMultiWordTags
from itertools import chain


class SelectMultipleMultiWordTag(forms.SelectMultiple):
    def __init__(self, model, attrs=None, choices=()):
        self.model = model
        self.choices = choices
        super(SelectMultipleMultiWordTag, self).__init__(attrs, self.choices)
    def render(self, name, value, attrs=None, choices=()):
        if isinstance(value, (str, unicode)):
            value = get_tag_name_list(value)
        tags = Tag.objects.usage_for_model(self.model)
        c = sorted(set(chain(get_tag_name_list(
                                   clean(c[0] for c in choices)),
                             get_tag_name_list(
                                   clean(tag.name for tag in tags)),
                             (c[0] for c in self.choices),
                             value if value else [])))
        self.choices = zip(c, c)
        return super(SelectMultipleMultiWordTag, self).render(
            name, value, attrs, ())

    def value_from_datadict(self, data, files, name):
        if isinstance(data, MultiValueDict):
            return tagnames2str(data.getlist(name))
        return data.get(name)

class MultiSelectComboMultiWordTag(forms.MultiWidget):
    """
    """
    def __init__(self, model, attrs=None, select_attrs=None, text_attrs=None,
                 choices=()):
        widgets = (SelectMultipleMultiWordTag(model, select_attrs, choices),
                   forms.TextInput(attrs=text_attrs))
        super(MultiSelectComboMultiWordTag, self).__init__(widgets, attrs)

    def decompress(self, value):
        return [value, u'']
    def format_output(self, rendered_widgets):
        # RED_FLAG: the width300 is a hack to solve a problem in the pycon html
        return (u'<p><span style="vertical-align: top;">Existing:</span>' + rendered_widgets[0] +
                u'</p>\n<p style="width: 300px;"><span>Add new:</span>' +
                rendered_widgets[1] + u'</p>')

class MultiWordTagField(forms.CharField):
    def __init__(self, min_tags=None, max_tags=None, *args, **kwdargs):
        super(MultiWordTagField, self).__init__(*args, **kwdargs)
        self.min_tags = min_tags
        self.max_tags = max_tags
    def clean(self, value):
        """
        Validates that the input is a valid list of tag names.
        """
        value = super(MultiWordTagField, self).clean(value)
        if value == u'':
            return value
        isMultiWordTagList(value, {})
        if self.required and (min_tags is None or min_tags < 1):
            min_tags = 1
        if self.min_tags or self.max_tags:
            NumMultiWordTags(self.min_tags, self.max_tags)(value, {})
        return clean(value)

class MultiSelectComboMultiWordTagField(forms.MultiValueField):
    def __init__(self, model, choices=(), min_tags=0, max_tags=256,
                 min_length=None, max_length=None, attrs=None,
                 text_attrs=None, select_attrs=None,
                 *args, **kwargs):
        if max_length is not None:
            if text_attrs is None: text_attrs = {}
            text_attrs.setdefault('maxlength', unicode(max_length))

        fields = (MultiWordTagField(
                    min_length=min_length,
                    max_length=max_length,
                    widget=SelectMultipleMultiWordTag(model, choices=choices,
                                                      attrs=select_attrs)),
                  MultiWordTagField(
                    min_length=min_length,
                    max_length=max_length,
                    widget=forms.TextInput(attrs=text_attrs)))
        self.fields=fields
        self.widget = MultiSelectComboMultiWordTag(model, attrs=attrs,
                                                   select_attrs=select_attrs,
                                                   text_attrs=text_attrs,
                                                   choices=choices)
        super(MultiSelectComboMultiWordTagField, self).__init__(fields,
                    *args, **kwargs)
        self.tagged_model = model
        self.min_tags, self.max_tags = min_tags, max_tags
        self.min_length, self.max_length = min_length, max_length
    def clean(self, value):
        """
        """
        clean_data = []
        errors = []
        has_data = False
        for i, field in enumerate(self.fields):
            field_value = None
            try:
                if value is not None:
                    field_value = value[i]
                    has_data = has_data or bool(field_value)
            except IndexError: pass

            try:
                clean_data.append(field.clean(field_value))
            except forms.ValidationError, e:
                # Collect all validation errors in a single list, which we'll
                # raise at the end of clean(), rather than raising a single
                # exception for the first error we encounter.
                errors.extend(e.messages)
        if self.required and not has_data:
            errors.append(u'This field is required.')
        if errors:
            raise forms.ValidationError(errors)
        res = self.compress(clean_data)
        min_tags = self.min_tags
        if self.required and (min_tags is None or min_tags < 1):
            min_tags = 1
        if self.min_tags or self.max_tags:
            NumMultiWordTags(min_tags, self.max_tags)(res, {})
        return res
    def compress(self, data_list):
        if data_list:
            return clean(data_list)
        return None
