import io
import os
import shutil
import json
import zipfile

from django import forms
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import SimpleUploadedFile
from django.contrib.gis.gdal import OGRGeometry
from django.test import SimpleTestCase, TestCase
from osgeo import ogr, osr

from spillway.forms.fields import (OGRGeometryField, GeometryFileField,
    GeoFormatField, SpatialReferenceField)
from spillway.collections import Feature, NamedCRS
from spillway.validators import GeometrySizeValidator
from .models import _geom

def write_shp(path, gjson):
    proj = osr.SpatialReference(osr.SRS_WKT_WGS84)
    g = ogr.CreateGeometryFromJson(json.dumps(gjson))
    vdriver = ogr.GetDriverByName('ESRI Shapefile')
    ds = vdriver.CreateDataSource(path)
    layer = ds.CreateLayer('', proj, g.GetGeometryType())
    featdef = layer.GetLayerDefn()
    feature = ogr.Feature(featdef)
    feature.SetGeometry(g)
    layer.CreateFeature(feature)
    feature.Destroy()
    ds.Destroy()


class OGRGeometryFieldTestCase(SimpleTestCase):
    def setUp(self):
        self.field = OGRGeometryField()

    def test_dict(self):
        geom = self.field.to_python(_geom)
        self.assertEqual(json.loads(geom.geojson), _geom)

    def test_extent(self):
        ex = (0, 0, 10, 10)
        geom = self.field.to_python(','.join(map(str, ex)))
        self.assertEqual(geom.extent, ex)

    def test_feature(self):
        feature = Feature(geometry=_geom)
        geojson = str(feature)
        geom = self.field.to_python(geojson)
        self.assertEqual(json.loads(geom.geojson), feature['geometry'])
        geom = self.field.to_python(feature)
        self.assertEqual(json.loads(geom.geojson), feature['geometry'])

    def test_feature_srid(self):
        srid = 3857
        feature = Feature(geometry=_geom, crs=NamedCRS(srid))
        geom = self.field.to_python(str(feature))
        self.assertEqual(geom.srid, srid)

    def test_invalid(self):
        self.assertRaises(forms.ValidationError, self.field.to_python, '3')

    def test_size_validator(self):
        validator = GeometrySizeValidator(3 ** 2, 4326)
        field = OGRGeometryField(srid=validator.srid, validators=[validator])
        self.assertRaises(forms.ValidationError, field.clean, '0,0,5,5')

    def test_srid(self):
        srid = 4269
        geom = OGRGeometryField(srid=srid).to_python('POINT(0 0)')
        self.assertEqual(geom.srid, srid)


class GeometryFileFieldTestCase(SimpleTestCase):
    def setUp(self):
        self.field = GeometryFileField()
        self.fp = SimpleUploadedFile(
            'geom.json', json.dumps(_geom).encode('ascii'))
        self.fp.seek(0)

    def test_to_python(self):
        self.assertIsInstance(self.field.to_python(self.fp), OGRGeometry)
        fp = SimpleUploadedFile('empty.json', b'{}')
        self.assertRaises(forms.ValidationError, self.field.to_python, fp)

    def test_feature_to_python(self):
        feature = Feature(geometry=_geom)
        self.fp.write(str(feature).encode('ascii'))
        self.fp.seek(0)
        v = self.field.to_python(self.fp)
        self.assertIsInstance(v, OGRGeometry)

    def test_shapefile(self):
        base = 'dir/geofield.shp'
        path = default_storage.path(base)
        os.mkdir(os.path.dirname(path))
        write_shp(path, _geom)
        b = io.BytesIO()
        with zipfile.ZipFile(b, 'w') as zf:
            for ext in ('dbf', 'prj', 'shp', 'shx'):
                fname = base.replace('shp', ext)
                with default_storage.open(fname) as fp:
                    zf.writestr(fname, fp.read())
        shutil.rmtree(os.path.dirname(path))
        upfile = SimpleUploadedFile('geofield.zip', b.getvalue())
        b.close()
        result = self.field.to_python(upfile)
        self.assertIsInstance(result, OGRGeometry)
        self.assertIsNotNone(result.srs)

    def test_zipfile(self):
        zfile = io.BytesIO()
        with zipfile.ZipFile(zfile, 'w') as zf:
            zf.writestr(self.fp.name, self.fp.read())
        zfile.seek(0)
        upfile = SimpleUploadedFile('geofield.zip', zfile.read())
        zfile.close()
        self.assertIsInstance(self.field.to_python(upfile), OGRGeometry)

    def tearDown(self):
        self.fp.close()


class GeoFormatFieldTestCase(SimpleTestCase):
    def test_to_python(self):
        field = GeoFormatField()
        self.assertRaises(forms.ValidationError, field.to_python, 'invalid')