import sys, os, itertools, operator
from collections import OrderedDict
import datetime



import shapely
from shapely.geometry import asShape as geoj2geom
from shapely.geometry import mapping as geom2geoj
import rtree



from . import loader
from . import saver




class Feature:
    def __init__(self, table, row, geometry, id=None):
        "geometry must be a geojson dictionary or a shapely geometry instance"
        self._table = table
        self.row  = list(row)
        
        if isinstance(geometry, dict): bbox = geometry.get("bbox")
        else: bbox = None
        self._cached_bbox = bbox

        self.geometry = geometry.copy()

        # ensure it is same geometry type as parent
        geotype = self.geometry["type"]
        if self._table.type: 
            if "Point" in geotype and self._table.type == "Point": pass
            elif "LineString" in geotype and self._table.type == "LineString": pass
            elif "Polygon" in geotype and self._table.type == "Polygon": pass
            else: raise TypeError("Each feature geometry must be of the same type as the file it is attached to")
        else: self._table.type = self.geometry["type"].replace("Multi", "")
        
        if id == None: id = next(self._table._id_generator)
        self.id = id

    def __getitem__(self, i):
        if isinstance(i, (str,unicode)):
            i = self._table.fields.index(i)
        return self.row[i]

    def __setitem__(self, i, setvalue):
        if isinstance(i, (str,unicode)):
            i = self._table.fields.index(i)
        self.row[i] = setvalue

    @property
    def bbox(self):
        if not self._cached_bbox:
            geotype = self.geometry["type"]
            coords = self.geometry["coordinates"]

            if geotype == "Point":
                x,y = coords
                bbox = [x,y,x,y]
            elif geotype in ("MultiPoint","LineString"):
                xs, ys = itertools.izip(*coords)
                bbox = [min(xs),min(ys),max(xs),max(ys)]
            elif geotype == "MultiLineString":
                xs = [x for line in coords for x,y in line]
                ys = [y for line in coords for x,y in line]
                bbox = [min(xs),min(ys),max(xs),max(ys)]
            elif geotype == "Polygon":
                exterior = coords[0]
                xs, ys = itertools.izip(*exterior)
                bbox = [min(xs),min(ys),max(xs),max(ys)]
            elif geotype == "MultiPolygon":
                xs = [x for poly in coords for x,y in poly[0]]
                ys = [y for poly in coords for x,y in poly[0]]
                bbox = [min(xs),min(ys),max(xs),max(ys)]
            self._cached_bbox = bbox
        return self._cached_bbox

    def copy(self):
        geoj = self.geometry
        if self._cached_bbox: geoj["bbox"] = self._cached_bbox
        return Feature(self._table, self.row, geoj)




def ID_generator():
    i = 0
    while True:
        yield i
        i += 1



class GeoTable:
    def __init__(self, filepath=None, type=None, **kwargs):
        self.filepath = filepath

        # type is optional and will make the features ensure that all geometries are of that type
        # if None, type enforcement will be based on first geometry found
        self.type = type
        
        if filepath:
            fields,rows,geometries,crs = loader.from_file(filepath, **kwargs)
        else:
            fields,rows,geometries,crs = [],[],[],"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"

        self.fields = fields

        self._id_generator = ID_generator()
        
        ids_rows_geoms = itertools.izip(self._id_generator,rows,geometries)
        featureobjs = (Feature(self,row,geom,id=id) for id,row,geom in ids_rows_geoms )
        self.features = OrderedDict([ (feat.id,feat) for feat in featureobjs ])
        self.crs = crs
        print self.crs

    def __len__(self):
        return len(self.features)

    def __iter__(self):
        for feat in self.features.itervalues():
            yield feat

    def __getitem__(self, i):
        """
        Get a Feature based on its feature id.
        """
        if isinstance(i, slice):
            raise Exception("Can only get one feature at a time")
        else:
            return self.features[i]

    def __setitem__(self, i, feature):