import json

from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db import models
from django.utils.translation import ugettext_lazy as _
from shapely import validation
from shapely.geometry import LineString, Point, Polygon, mapping, shape
from shapely.geometry.base import BaseGeometry

from c3nav.mapdata.utils.geometry import clean_geometry
from c3nav.mapdata.utils.json import format_geojson

validate_bssid_lines = RegexValidator(regex=r'^([0-9a-f]{2}(:[0-9a-f]{2}){5}(\r?\n[0-9a-f]{2}(:[0-9a-f]{2}){5})*)?$',
                                      message=_('please enter a newline seperated lowercase list of BSSIDs'))


def validate_geometry(geometry: BaseGeometry):
    if not isinstance(geometry, BaseGeometry):
        raise ValidationError('GeometryField expected a Shapely BaseGeometry child-class.')

    if not geometry.is_valid:
        raise ValidationError('Invalid geometry: %s' % validation.explain_validity(geometry))


class GeometryField(models.TextField):
    default_validators = [validate_geometry]

    def __init__(self, geomtype=None, *args, **kwargs):
        if geomtype == 'polyline':
            geomtype = 'linestring'
        if geomtype not in (None, 'polygon', 'linestring', 'point'):
            raise ValueError(_('GeometryField.geomtype has to be None, "polygon", "linestring", "point"'))
        self.geomtype = geomtype
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        if self.geomtype is not None:
            kwargs['geomtype'] = self.geomtype
        return name, path, args, kwargs

    def from_db_value(self, value, expression, connection, context):
        if value is None:
            return value
        return shape(json.loads(value))

    def to_python(self, value):
        return clean_geometry(shape(json.loads(value)))

    def get_prep_value(self, value):
        if value == '':
            return None
        elif self.geomtype == 'polygon' and not isinstance(value, Polygon):
            raise TypeError(_('Expected Polygon instance, got %s instead.') % repr(value))
        elif self.geomtype == 'linestring' and not isinstance(value, LineString):
            raise TypeError(_('Expected LineString instance, got %s instead.') % repr(value))
        elif self.geomtype == 'point' and not isinstance(value, Point):
            raise TypeError(_('Expected Point instance, got %s instead.') % repr(value))
        return json.dumps(format_geojson(mapping(value)))


class JSONField(models.TextField):
    def from_db_value(self, value, expression, connection, context):
        if value is None:
            return value
        return json.loads(value)

    def to_python(self, value):
        return json.loads(value)

    def get_prep_value(self, value):
        return json.dumps(value)

@@ -1118,31 +1118,28 @@
 e=None, 
-*args, **kwargs
+default=None
 ):%0A     
@@ -1442,23 +1442,23 @@
 t__(
-*args, **kwargs
+default=default
 )%0A%0A 
@@ -1851,32 +1851,82 @@
 n(self, value):%0A
+        if value is None:%0A            return None%0A
         return c
@@ -2024,13 +2024,15 @@
 lue 
-== ''
+is None
 :%0A  

change default of geometryfield / support for null values