"""
Custom forms-related types
"""

from django.core.exceptions import ValidationError
from django.forms import Field, MultipleHiddenInput, NullBooleanField, Select


class MultiValueField(Field):
    """
    Field class that supports a set of values for a single form field.

    The field input can be specified as:
        1. a comma-separated-list (foo:bar1,bar2,bar3), or
        2. a repeated field in a MultiValueDict (foo:bar1, foo:bar2, foo:bar3)
        3. a combination of the above (foo:bar1,bar2, foo:bar3)

    Note that there is currently no way to pass a value that includes a comma.

    The resulting field value is a python set of the values as strings.
    """
    widget = MultipleHiddenInput

    def to_python(self, list_of_string_values):
        """
        Convert the form input to a list of strings
        """
        values = super(MultiValueField, self).to_python(list_of_string_values) or set()

        if values:
            # combine all values if there were multiple specified individually
            values = ','.join(values)

            # parse them into a set
            values = set(values.split(',')) if values else set()

        return values

    def validate(self, values):
        """
        Ensure no empty values were passed
        """
        if values and "" in values:
            raise ValidationError("This field cannot be empty.")


class ExtendedNullBooleanField(NullBooleanField):
    """
    A field whose valid values are None, True, 'True', 'true', '1',
    False, 'False', 'false' and '0'.
    """

    NULL_BOOLEAN_CHOICES = (
        (None, ""),
        (True, True),
        (True, "True"),
        (True, "true"),
        (True, "1"),
        (False, False),
        (False, "False"),
        (False, "false"),
        (False, "0"),
    )

    widget = Select(choices=NULL_BOOLEAN_CHOICES)

    def to_python(self, value):
        """
        Explicitly checks for the string 'True', 'False', 'true',
        'false', '1' and '0' and returns boolean True or False.
        Returns None if value is not passed at all and raises an
        exception for any other value.
        """
        if value in (True, 'True', 'true', '1'):
            return True
        elif value in (False, 'False', 'false', '0'):
            return False
        elif not value:
            return None
        else:
            raise ValidationError("Invalid Boolean Value.")