# from ga_ows.views import wms, wfs
import shutil
import json
from zipfile import ZipFile

import pandas
from django.contrib.gis.geos import Polygon
import os
import sh
from osgeo import osr, ogr
from . import Driver
from pandas import DataFrame
from shapely import wkb
from django.template.defaultfilters import slugify
import re


def ogrfield(elt):
    return re.sub('-', '_', slugify(elt).encode('ascii'))[0:10]

def identity(x):
    return '"' + x + '"' if isinstance(x, basestring) else str(x)


dtypes = {
    'int64': ogr.OFTInteger,
    'float64': ogr.OFTReal,
    'object': ogr.OFTString,
    'datetime64[ns]': ogr.OFTDateTime
}
geomTypes = {
    'GeometryCollection': ogr.wkbGeometryCollection,
    'LinearRing': ogr.wkbLinearRing,
    'LineString': ogr.wkbLineString,
    'MultiLineString': ogr.wkbMultiLineString,
    'MultiPoint': ogr.wkbMultiPoint,
    'MultiPolygon': ogr.wkbMultiPolygon,
    'Point': ogr.wkbPoint,
    'Polygon': ogr.wkbPolygon
}

def transform(geom, crx):
    if crx:
        geom.Transform(crx)
    return geom


class ShapefileDriver(Driver):
    @classmethod
    def supports_multiple_layers(cls):
        return False

    @classmethod
    def supports_configuration(cls):
        return False

    def ready_data_resource(self, **kwargs):
        """Other keyword args get passed in as a matter of course, like BBOX, time, and elevation, but this basic driver
        ignores them"""

        slug, srs = super(ShapefileDriver, self).ready_data_resource(**kwargs)
        return slug, srs, {
            'type': 'shape',
            "file": self.cached_basename + '.shp'
        }

    def clear_cached_files(self):
        sh.rm('-f', sh.glob(os.path.join(self.cache_path, '*.shp')))
        sh.rm('-f', sh.glob(os.path.join(self.cache_path, '*.shx')))
        sh.rm('-f', sh.glob(os.path.join(self.cache_path, '*.dbf')))
        sh.rm('-f', sh.glob(os.path.join(self.cache_path, '*.prj')))

    def compute_spatial_metadata(self, **kwargs):
        """Other keyword args get passed in as a matter of course, like BBOX, time, and elevation, but this basic driver
        ignores them"""

        super(ShapefileDriver, self).compute_spatial_metadata(**kwargs)
        self.clear_cached_files()

        archive = ZipFile(self.cached_basename + self.src_ext)
        projection_found = False
        for name in archive.namelist():
            xtn = name.split('.')[-1].lower()
            if xtn in {'shp', 'shx', 'dbf', 'prj'} and "__MACOSX" not in name:
                projection_found = projection_found or xtn == 'prj'
                with open(self.cached_basename + '.' + xtn, 'wb') as fout:
                    with archive.open(name) as fin:
                        chunk = fin.read(65536)
                        while chunk:
                            fout.write(chunk)
                            chunk = fin.read(65536)

        if not projection_found:
            with open(self.cached_basename + '.prj', 'w') as f:
                srs = osr.SpatialReference()
                srs.ImportFromEPSG(4326)
                f.write(srs.ExportToWkt())

        ds = ogr.Open(self.cached_basename + '.shp')
        lyr = ds.GetLayerByIndex(0) if 'sublayer' not in kwargs else ds.GetLayerByName(kwargs['sublayer'])
        xmin, xmax, ymin, ymax = lyr.GetExtent()
        crs = lyr.GetSpatialRef()


        self.resource.spatial_metadata.native_srs = crs.ExportToProj4()
        e4326 = osr.SpatialReference()
        e4326.ImportFromEPSG(4326)
        crx = osr.CoordinateTransformation(crs, e4326)
        x04326, y04326, _ = crx.TransformPoint(xmin, ymin)
        x14326, y14326, _ = crx.TransformPoint(xmax, ymax)
        self.resource.spatial_metadata.bounding_box = Polygon.from_bbox((x04326, y04326, x14326, y14326))
        self.resource.spatial_metadata.native_bounding_box = Polygon.from_bbox((xmin, ymin, xmax, ymax))
        self.resource.spatial_metadata.three_d = False

        self.resource.spatial_metadata.save()
        self.resource.save()


    def get_data_fields(self, **kwargs):
        _, _, result = self.ready_data_resource(**kwargs)
        ds = ogr.Open(result['file'])
        lyr = ds.GetLayerByIndex(0) if 'layer' not in kwargs else ds.GetLayerByName(kwargs['sublayer'])
        return [(field.name, field.GetTypeName(), field.width) for field in lyr.schema]

    def get_data_for_point(self, wherex, wherey, srs, **kwargs):
        result, x1, y1, epsilon = super(ShapefileDriver, self).get_data_for_point(wherex, wherey, srs, **kwargs)
        ds = ogr.Open(result['file'])
        lyr = ds.GetLayerByIndex(0) if 'sublayer' not in kwargs else ds.GetLayerByName(kwargs['sublayer'])

        if epsilon==0:
            lyr.SetSpatialFilter(ogr.CreateGeometryFromWkt("POINT({x1} {y1})".format(**locals())))
        else:
            from django.contrib.gis import geos
            wkt = geos.Point(x1,y1).buffer(epsilon).wkt
            print wkt
            lyr.SetSpatialFilter(ogr.CreateGeometryFromWkt(wkt))
        return [f.items() for f in lyr]

    def attrquery(self, key, value):
        key, op = key.split('__')
        op = {
            'gt' : ">",
            'gte' : ">=",
            'lt' : "<",
            'lte' : '<=',
            'startswith' : 'LIKE',
            'endswith' : 'LIKE',
            'istartswith' : 'ILIKE',
            'iendswith' : 'ILIKE',
            'icontains' : "ILIKE",
            'contains' : "LIKE",
            'in' : 'IN',
            'ne' : "<>"
        }[op]

        value = {
            'gt': identity,
            'gte': identity,
            'lt': identity,
            'lte': identity,
            'startswith': lambda x : '%' + x,
            'endswith': lambda x : x + '%',
            'istartswith': lambda x : '%' + x,
            'iendswith': lambda x : x + '%',
            'icontains': lambda x : '%' + x + '%',
            'contains': lambda x: '%' + x + '%',
            'in': lambda x: x if isinstance(x, basestring) else '(' + ','.join(identity(a) for a in x) + ')',
            'ne': identity