"""
The :mod:`layer.layer` module provides the classes for data access and manipulation.
"""
import sys, math
from java import io
from java import net
from cursor import Cursor
from geoscript import core, geom, proj, feature
from geoscript.filter import Filter
from geoscript.util.data import readFeatures
from org.geotools.data import FeatureSource, DefaultQuery, Query, Transaction
from org.geotools.factory import CommonFactoryFinder
from org.geotools.feature import FeatureCollection, FeatureCollections
from org.opengis.filter.sort import SortOrder

_filterFactory = CommonFactoryFinder.getFilterFactory(None)

class Layer(object):
  """
  A source of spatial data.
  """

  _id = -1
  """
  Internal counter for generating layer names
  """

  def __init__(self, name=None, workspace=None, fs=None, schema=None):
    if not workspace:
       from geoscript.workspace import Memory
       workspace = Memory()
 
    name = name if name else schema.name if schema else Layer._newname()
       
    if not fs:
       layer = None
       try:
         layer = workspace.get(name)
       except KeyError:
         pass
        
       if not layer:
         if schema:
           layer = workspace.create(schema.name, schema.fields)
         else:
           layer = workspace.create(name)

       fs = layer._source

    self.workspace = workspace
    self.schema = schema or feature.Schema(ft=fs.schema) 
    self._source = fs

    # keep a crs local to allow the native crs to be overriden, or to 
    # provide a crs for layers that don't have one specified
    self._proj = None

  def getformat(self):
    return self.workspace._format(self)

  format = property(getformat)
  """
  A ``str`` identifying the format of the layer.
  """

  def getname(self):
    return self._source.name.localPart

  name = property(getname)
  """
  The name of the layer as a ``str``.
  """

  def getproj(self):
    if self._proj:
      return self._proj
    else:
      crs = self._source.schema.coordinateReferenceSystem
      if crs:
        return proj.Projection(crs)

  def setproj(self, value):
    self._proj = proj.Projection(value) 

  proj = property(getproj, setproj)
  """
  The :class:`Projection <geoscript.proj.Projection>` of the layer. In cases where the projection of a layer is 
  unknown this attribute has the value ``None``.

  >>> import proj
  >>> l = Layer()
  >>> l.proj
  None
  >>> l.proj = proj.Projection('epsg:4326')
  >>> l.proj.id
  EPSG:4326
  """
  
  def count(self, filter=None):
    """
    The number of features in the layer as an ``int``.

    *filter* is an optional :class:`Filter <geoscript.filter.Filter>` to constrains the counted set of features.

    >>> l = Layer()
    >>> l.count()
    0
    >>> from geoscript import geom
    >>> l.add([geom.Point(1,2)])
    >>> l.add([geom.Point(3,4)])
    >>> l.count() 
    2
    >>> l.count('INTERSECTS(geom,POINT(3 4))')
    1
    """

    f = Filter(filter) if filter else Filter.PASS
    count = self._source.getCount(DefaultQuery(self.name, f._filter))
    if count == -1:
      count = 0
      # calculate manually 
      for f in self.features(filter):
        count += 1

    return count

  def bounds(self, filter=None):
    """
    The :class:`Bounds <geoscript.geom.Bounds>` of the layer.

    *filter* is an optional :class:`Filter <geoscript.filter.Filter>` to constrains the returned bounds.

    >>> l = Layer()
    >>> from geoscript import geom 
    >>> l.add([geom.Point(1.0, 2.0)])
    >>> l.add([geom.Point(3.0, 4.0)])

    >>> l.bounds()
    (1.0, 2.0, 3.0, 4.0, EPSG:4326)

    >>> l.bounds('INTERSECTS(geom,POINT(3 4))')
    (3.0, 4.0, 3.0, 4.0, EPSG:4326)
    """

    f = Filter(filter) if filter else Filter.PASS
    q = DefaultQuery(self.name, f._filter)
    e = self._source.getBounds(q)

    if not e:
      # try through feature collection
      fc = self._source.getFeatures(q)
      e = fc.getBounds()
    if e:
      if e.crs():
        return geom.Bounds(env=e)
      else:
        return geom.Bounds(env=e, prj=self.proj)
    else:
      # calculate manually
      fit = self._source.getFeatures(q).features()
      try:
        bounds = geom.Bounds(prj=self.proj)
        if fit.hasNext(): 
          bounds.init(fit.next().getBounds())