# -*- coding: utf-8 -*-
from django import forms
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.encoding import force_unicode
from django.utils.text import capfirst


class MultiSelectField(models.TextField):
    __metaclass__ = models.SubfieldBase

    def get_db_prep_value(self, value):
        if isinstance(value, basestring):
            return value
        elif isinstance(value, list):
            return ','.join(value)
        return ''

    def to_python(self, value):
        if isinstance(value, basestring):
            return value.split(',')
        elif isinstance(value, list):
            return value
        return ''

    def value_to_string(self, obj):
        # We need this to proper dump data.
        return self.get_db_prep_value(self._get_val_from_obj(obj))

    def get_choices_default(self):
        return self.get_choices(include_blank=False)

    def formfield(self, form_class=forms.MultipleChoiceField, **kwargs):
        # Using super() won't work because this would replace the form_class.
        defaults = {
            'required': not self.blank,
            'label': capfirst(self.verbose_name),
            'help_text': self.help_text,
            'choices': self.get_choices(include_blank=False),
        }
        if self.has_default():
            if callable(self.default):
                defaults['initial'] = self.default
                defaults['show_hidden_initial'] = True
            else:
                defaults['initial'] = self.get_default()
        defaults.update(kwargs)

        return form_class(**defaults)

    def validate(self, value, model_instance):
        if isinstance(value, list):
            valid_choices = [k for k, v in self.choices]
            for choice in value:
                if choice not in valid_choices:
                    raise ValidationError(
                        self.error_messages['invalid_choice'] % choice)

    def _get_display(field):
        def _inner(self):
            values = getattr(self, field.attname)
            return ', '.join([force_unicode(
                field.choices_dict.get(value, value),
                strings_only=True
            ) for value in values])
        return _inner

    def contribute_to_class(self, cls, name):
        self.set_attributes_from_name(name)
        self.model = cls
        cls._meta.add_field(self)
        self.choices_dict = dict(self.choices)
        setattr(cls, 'get_%s_display' % self.name, self._get_display())