from __future__ import unicode_literals

from django import forms
from django.utils.translation import ugettext_lazy as _

# While this couples the geographic forms to the GEOS library,
# it decouples from database (by not importing SpatialBackend).
from django.contrib.gis.geos import GEOSException, GEOSGeometry

class GeometryField(forms.Field):
    """
    This is the basic form field for a Geometry.  Any textual input that is
    accepted by GEOSGeometry is accepted by this form.  By default,
    this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
    """
    widget = forms.Textarea

    default_error_messages = {
        'no_geom' : _('No geometry value provided.'),
        'invalid_geom' : _('Invalid geometry value.'),
        'invalid_geom_type' : _('Invalid geometry type.'),
        'transform_error' : _('An error occurred when transforming the geometry '
                              'to the SRID of the geometry form field.'),
        }

    def __init__(self, **kwargs):
        # Pop out attributes from the database field, or use sensible
        # defaults (e.g., allow None).
        self.srid = kwargs.pop('srid', None)
        self.geom_type = kwargs.pop('geom_type', 'GEOMETRY')
        self.null = kwargs.pop('null', True)
        super(GeometryField, self).__init__(**kwargs)

    def to_python(self, value):
        """
        Transforms the value to a Geometry object.
        """
        try:
            return GEOSGeometry(value)
        except (GEOSException, ValueError, TypeError):
            raise forms.ValidationError(self.error_messages['invalid_geom'])

    def clean(self, value):
        """
        Validates that the input value can be converted to a Geometry
        object (which is returned).  A ValidationError is raised if
        the value cannot be instantiated as a Geometry.
        """
        if not value:
            if self.null and not self.required:
                # The geometry column allows NULL and is not required.
                return None
            else:
                raise forms.ValidationError(self.error_messages['no_geom'])

        # Transform the value to a python object first
        geom = self.to_python(value)

        # Ensuring that the geometry is of the correct type (indicated
        # using the OGC string label).
        if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
            raise forms.ValidationError(self.error_messages['invalid_geom_type'])

        # Transforming the geometry if the SRID was set.
        if self.srid:
            if not geom.srid:
                # Should match that of the field if not given.
                geom.srid = self.srid
            elif self.srid != -1 and self.srid != geom.srid:
                try:
                    geom.transform(self.srid)
                except:
                    raise forms.ValidationError(self.error_messages['transform_error'])

        return geom
