from django.forms import ValidationError
from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.tests.utils import HAS_SPATIALREFSYS
from django.test import SimpleTestCase
from django.utils import six
from django.utils.html import escape
from django.utils.unittest import skipUnless

if HAS_SPATIALREFSYS:
    from django.contrib.gis import forms
    from django.contrib.gis.geos import GEOSGeometry

@skipUnless(HAS_GDAL and HAS_SPATIALREFSYS, "GeometryFieldTest needs gdal support and a spatial database")
class GeometryFieldTest(SimpleTestCase):

    def test_init(self):
        "Testing GeometryField initialization with defaults."
        fld = forms.GeometryField()
        for bad_default in ('blah', 3, 'FoO', None, 0):
            self.assertRaises(ValidationError, fld.clean, bad_default)

    def test_srid(self):
        "Testing GeometryField with a SRID set."
        # Input that doesn't specify the SRID is assumed to be in the SRID
        # of the input field.
        fld = forms.GeometryField(srid=4326)
        geom = fld.clean('POINT(5 23)')
        self.assertEqual(4326, geom.srid)
        # Making the field in a different SRID from that of the geometry, and
        # asserting it transforms.
        fld = forms.GeometryField(srid=32140)
        tol = 0.0000001
        xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140)
        # The cleaned geometry should be transformed to 32140.
        cleaned_geom = fld.clean('SRID=4326;POINT (-95.363151 29.763374)')
        self.assertTrue(xform_geom.equals_exact(cleaned_geom, tol))

    def test_null(self):
        "Testing GeometryField's handling of null (None) geometries."
        # Form fields, by default, are required (`required=True`)
        fld = forms.GeometryField()
        with six.assertRaisesRegex(self, forms.ValidationError,
                "No geometry value provided."):
            fld.clean(None)

        # This will clean None as a geometry (See #10660).
        fld = forms.GeometryField(required=False)
        self.assertIsNone(fld.clean(None))

    def test_geom_type(self):
        "Testing GeometryField's handling of different geometry types."
        # By default, all geometry types are allowed.
        fld = forms.GeometryField()
        for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'):
            self.assertEqual(GEOSGeometry(wkt), fld.clean(wkt))

        pnt_fld = forms.GeometryField(geom_type='POINT')
        self.assertEqual(GEOSGeometry('POINT(5 23)'), pnt_fld.clean('POINT(5 23)'))
        # a WKT for any other geom_type will be properly transformed by `to_python`
        self.assertEqual(GEOSGeometry('LINESTRING(0 0, 1 1)'), pnt_fld.to_python('LINESTRING(0 0, 1 1)'))
        # but rejected by `clean`
        self.assertRaises(forms.ValidationError, pnt_fld.clean, 'LINESTRING(0 0, 1 1)')

    def test_to_python(self):
        """
        Testing to_python returns a correct GEOSGeometry object or
        a ValidationError
        """
        fld = forms.GeometryField()
        # to_python returns the same GEOSGeometry for a WKT
        for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'):
            self.assertEqual(GEOSGeometry(wkt), fld.to_python(wkt))
        # but raises a ValidationError for any other string
        for wkt in ('POINT(5)', 'MULTI   POLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'BLAH(0 0, 1 1)'):
            self.assertRaises(forms.ValidationError, fld.to_python, wkt)


@skipUnless(HAS_GDAL and HAS_SPATIALREFSYS,
    "SpecializedFieldTest needs gdal support and a spatial database")
class SpecializedFieldTest(SimpleTestCase):
    def setUp(self):
        self.geometries = {
            'point': GEOSGeometry("SRID=4326;POINT(9.052734375 42.451171875)"),
            'multipoint': GEOSGeometry("SRID=4326;MULTIPOINT("
                                       "(13.18634033203125 14.504356384277344),"
                                       "(13.207969665527 14.490966796875),"
                                       "(13.177070617675 14.454917907714))"),
            'linestring': GEOSGeometry("SRID=4326;LINESTRING("
                                       "-8.26171875 -0.52734375,"
                                       "-7.734375 4.21875,"
                                       "6.85546875 3.779296875,"
                                       "5.44921875 -3.515625)"),
            'multilinestring': GEOSGeometry("SRID=4326;MULTILINESTRING("
                                            "(-16.435546875 -2.98828125,"
                                            "-17.2265625 2.98828125,"
                                            "-0.703125 3.515625,"
                                            "-1.494140625 -3.33984375),"
                                            "(-8.0859375 -5.9765625,"
                                            "8.525390625 -8.7890625,"
                                            "12.392578125 -0.87890625,"
                                            "10.01953125 7.646484375))"),
            'polygon': GEOSGeometry("SRID=4326;POLYGON("
                                    "(-1.669921875 6.240234375,"
                                    "-3.8671875 -0.615234375,"
                                    "5.9765625 -3.955078125,"
                                    "18.193359375 3.955078125,"
                                    "9.84375 9.4921875,"
                                    "-1.669921875 6.240234375))"),
            'multipolygon': GEOSGeometry("SRID=4326;MULTIPOLYGON("
                                         "((-17.578125 13.095703125,"
                                         "-17.2265625 10.8984375,"
                                         "-13.974609375 10.1953125,"
                                         "-13.359375 12.744140625,"
                                         "-15.732421875 13.7109375,"
                                         "-17.578125 13.095703125)),"
                                         "((-8.525390625 5.537109375,"
                                         "-8.876953125 2.548828125,"
                                         "-5.888671875 1.93359375,"
                                         "-5.09765625 4.21875,"
                                         "-6.064453125 6.240234375,"
                                         "-8.525390625 5.537109375)))"),
            'geometrycollection': GEOSGeometry("SRID=4326;GEOMETRYCOLLECTION("
                                               "POINT(5.625 -0.263671875),"
                                               "POINT(6.767578125 -3.603515625),"
                                               "POINT(8.525390625 0.087890625),"
                                               "POINT(8.0859375 -2.13134765625),"
                                               "LINESTRING("
                                               "6.273193359375 -1.175537109375,"
                                               "5.77880859375 -1.812744140625,"
                                               "7.27294921875 -2.230224609375,"
                                               "7.657470703125 -1.25244140625))"),
        }

    def assertMapWidget(self, form_instance):
        """
        Make sure the MapWidget js is passed in the form media and a MapWidget
        is actually created
        """
        self.assertTrue(form_instance.is_valid())
        rendered = form_instance.as_p()
        self.assertIn('new MapWidget(options);', rendered)
        self.assertIn('gis/js/OLMapWidget.js', str(form_instance.media))

    def assertTextarea(self, geom, rendered):
        """Makes sure the wkt and a textarea are in the content"""
        
        self.assertIn('<textarea ', rendered)
        self.assertIn('required', rendered)
        self.assertIn(geom.wkt, rendered)

    def test_pointfield(self):
        class PointForm(forms.Form):
            p = forms.PointField()