# -*- coding: utf-8 -*-
#
# File: gvsig.py
#
# Copyright (c) 2011 by Model Driven Development sl and Antonio Carrasco Valero
#
# GNU General Public License (GPL)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
#

"""
This module contains classes and functions to manage gvSIG Project, 
gvSIG DocumentView, gvSIG TableDocument and Layers.
Also contais functions to manage vectorial data and other utility 
functions
"""

__author__ = """Antonio Carrasco Valero
Model Driven Development sl and Antonio Carrasco Valero
<carrasco@modeldd.org>
Victor Acevedo Royer <vacevedor@gvsig.com>, <vacevedor@gmail.com>
"""

__docformat__ = 'plaintext'

from java.awt import Color

from java.lang import RuntimeException
from java.lang import Throwable
import java.lang.IndexOutOfBoundsException

from java.util.prefs import Preferences

from org.gvsig.app import ApplicationLocator
from org.gvsig.app.project.documents.view import ViewDocument 
from org.gvsig.app.project.documents.table import TableDocument

from org.gvsig.fmap.crs import CRSFactory

from org.gvsig.fmap.mapcontext import MapContextLocator
from org.gvsig.fmap.mapcontext.layers import FLayers

from org.gvsig.fmap.dal import DALLocator, DataTypes
from org.gvsig.fmap.dal.feature import EditableFeature, \
    EditableFeatureType, FeatureQueryOrder
from org.gvsig.fmap.dal.feature.impl import DefaultEditableFeature

from org.gvsig.fmap.geom import Geometry

from org.gvsig.tools import ToolsLocator

import thread
import random


def runTask(name, function, *args):
  manager = ToolsLocator.getTaskStatusManager()
  status = manager.createDefaultSimpleTaskStatus(name)
  status.add()
  args = list(args)
  args.append(status)
  try:
    thread.start_new_thread(function, tuple(args))
    status.terminate()
  except Exception,e:
    status.abort()
    raise e
     

class WrapperToJava(object):
  """Creates a wrapper that allows python object access to the Java object
  properties and methods
  """
  def __init__(self, javaobj):
    if javaobj == None:
      raise RuntimeException("Can't create "+ self.__class__.__name__)
    self._javaobj = javaobj

  def __call__(self):
    return self._javaobj

  def __getattr__(self,name):
    return getattr(self._javaobj,name)

class Project(WrapperToJava):
  """Represents a gvSIG project (org.gvsig.app.project.DefaultProject)"""

  def __init__(self, project):
    WrapperToJava.__init__(self, project)
  
  def getView(self, name=None):
    """
    Returns active view, or view called 'name'  or None
    :param name: view name
    :type name: string    
    """
    if name == None:
      try:
        activeDocument = self.getActiveDocument()
        if activeDocument == None:
            return None
        if isinstance(activeDocument, ViewDocument):
            return View(activeDocument)
      except Exception, ex:
        raise Exception("%s"%repr(ex))
    else:
      views = self.getViews()
      if len(views) >0:
        for view in views:
          if name == view.getName():
            return View(view)

    return None
    
  def getTable(self, name=None):
    """
    Returns active Table Document, or Table Document called 'name'  or None
    :param name: Table Document name
    :type name: string
    """
    if name == None:
      try:
        activeDocument = self.getActiveDocument()
        if activeDocument == None:
            return None
        if isinstance(activeDocument, TableDocument):
            return Table(activeDocument)
      except Exception, ex:
        raise Exception("%s"%repr(ex))
    else:
      tables = self.getDocuments("project.document.table")
      if len(tables) >0:
        for table in tables:
          if name == table.getName():
            return Table(table)
            
    return None   
    
  def getProjectionCode(self):
    """Returns Project projection code name string. This value is the 
    projects default projection that is fixed in Preferences tool, View 
    options. It's used when no view projection is defined.
    
    """
    return self.getProjection().getFullCode()
    

class View(WrapperToJava):
  """
  Represents gvSIG view document 
  (org.gvsig.app.project.documents.view.DefaultViewDocument).  
  """
  def __init__(self,view):
    WrapperToJava.__init__(self,view)
    
  def getLayer(self, name=None):
    """
    Returns one view layer. If name is None returns active 
    layer if the view has one, if name is not None returns layer called name 
    else returns None.
    :param name: view name in Table Of Contents
    :type name: string
    :return: View
    :return: None  
    """
    map = self.getMapContext();
    if name == None:
      activeLayers = map.getLayers().getActives()
      if len(activeLayers) != 1 :
        return None
      for layer in activeLayers:
        if not isinstance(layer, FLayers):
          return Layer(layer)
      return None
    
    ls = self.getLayers()
    for i in xrange(ls.getLayersCount()):
      l = ls.getLayer(i)
      if l.name == name:
        return Layer(l)
    
    return None
    
  def getMap(self):
    # org.gvsig.fmap.mapcontext.MapContext
    """Returns view mapContext"""
    return self.getMapContext();
 
  def addLayer(self, layer):
    """
    Adds a new layer to the view
    :param layer: layer to add
    :type layer: Layer  
    """
    if isinstance(layer, Layer):
      layer =layer()
    self.getMapContext().getLayers().addLayer(layer)
  
  def getLayers(self):
    """Returns iterable view layers set"""
    map = self.getMapContext()
    return Layers(map.getLayers())
    
  def getGraphicsLayer(self):
    """Returns view graphics layer 
    org.gvsig.fmap.mapcontext.layers.vectorial.impl.DefaultGraphicLayer
    """
    return self.getMapContext().getGraphicsLayer()
    
  def getProjectionCode(self):
    """Returns string view projection code name"""
    return self.getProjection().getFullCode()
    
  def isProjected(self):
    """Returns if view projection is projected."""
    self.getProjection().isProjected()
      
class Layers(WrapperToJava):
  """Iterable layers set 
  (org.gvsig.fmap.mapcontext.layers.FLayers)  
  """
  def __init__(self,layers):
    WrapperToJava.__init__(self, layers)

  def __len__(self):
    return self.getLayersCount()

  def __getitem__(self, index):
    return self.getLayer(index)

  def __iter__(self):
    return LayersIterator(self)

class LayersIterator(object):

  def __init__(self,layers):
    self.__layers = layers
    self.__index = -1

  def next(self):
    self.__index+=1
    if self.__index >= self.__layers.getLayersCount() :
      raise StopIteration()
    return Layer(self.__layers.getLayer(self.__index))    
  
class Store(WrapperToJava):
  """Represents gvsig store. It's used as Table/Layer objects store  
  (org.gvsig.fmap.dal.feature.impl.DefaultFeatureStore)
  """
  def __init__(self,store):
    WrapperToJava.__init__(self, store)
    #self.data = None
    self.fset = None
  
  def features(self, expresion = None, sortBy="", asc=True):
    """Returns layer features set (FeatureSet class), with all layer features, 
    or selected featured if there are (could be empty feature set) or None if
    gets an Exception.
    :param expresion: filter to apply to the feature set to select
        determinates features that match with expression
    :type expresion: string
    :param sortBy: name of attribute to sort
    :type sortby: string
    :param: asc: order
    :type asc: boolean
    :return: FeatureSet
    """   
    if expresion == None and sortBy =="":
      self.fset = self.getFeatureSet()         
    else:
      try:
        application = ApplicationLocator.getManager()
        datamanager =  application.getDataManager()
        query = self.createFeatureQuery()
        if sortBy != "":
            order = FeatureQueryOrder()
            order.add(sortBy, asc)
            query.setOrder(order)
        if expresion != None:
            query.setFilter(datamanager.createExpresion(expresion))
        self.fset = self.getFeatureSet(query)
      except Exception, e:
        return None
    
    return FeatureSet(self.fset)    

  def edit(self):
    """Set store in edition mode"""     
    if not self.isEditing():
        self().edit() 
     
  def append(self, *valuesList, **values):
    """
    Creates a new feature from given values and insert it in the feature 
    set. If an error occurs raises RuntimeException.
    :param values: dictionary with name property value or list named params
    :type values: dict
    """
    try:
      if len(valuesList) ==1:
        values.update(valuesList[0])
      
      if not self.isEditing():
        self.edit() 
      f = self.createNewFeature()
      
      if f == None:
        raise RuntimeError("Failed to create a new Feature")
      for k,v in values.iteritems():
        f.set(k,v)
        self.insert(f)
    except Throwable, ex:
      raise RuntimeException("Can't append values %s to layer %s (%s)" % (
        repr(values), 
        self.getName(), 
        str(ex)
        )
      )

  def updateSchema(self, schema):
    """
    Updates store FeatureType. If an error occurs raises 
    RuntimeException.
    """
    try:
      self().update(schema._javaobj)
    except Throwable, ex:
      raise RuntimeException(repr(ex))
    
  def update(self, feature):
    """
    Updates exist feature in the layer featureSet
    :param editableFeature: editableFeature
    :type editableFeature: Java editableFeature
    """
    if not self.isEditing():
      self.edit() 
    
    if not isinstance(feature, EditableFeature):
      feature = feature._javaobj
    self.fset.update(feature)
  
  def getSchema(self):
    """Returns store data attributtes"""
    return Schema(self.getDefaultFeatureType())

  def commit(self):
    """
    Finish store edition saving changes. If an error occurs raises Throwable 
    Exception.
    """
    try:
      self.finishEditing()          
    except Throwable, ex:
      self.abort()
      raise Throwable("Can't finish layer edition, cancelling changes. %s" % repr(ex))
  
  def abort(self):
    """Finish store edition withoout saving changes and disposes itself"""
    self.cancelEditing() 
    self.dispose()

  def getSelection(self):
    """Returns store features selected set"""
    return FeatureSet(self().getSelection())

class __DefaultTable__(WrapperToJava):
  def __init__(self,document, data):
    self.data = Store(data)
    WrapperToJava.__init__(self,document)
    self.selection = None

  def features(self, expresion = None, sortBy="", asc=True):
    """
    Returns data features set
    :param expresion: filter to apply to the feature set to select
         determinates features that match with expression
    :type expresion: string
    :return: FeatureSet
    """
    return self.data.features(expresion, sortBy, asc)

  def edit(self):
    """Sets data in edition mode"""
     
    self.data.edit() 
     
  def append(self, *valuesList, **values):
    """
    Creates a new feature from given values and insert it in the feature set
    :param values: dictionary with name property value or list named params
    :type values: dict
    """
    self.data.append(*valuesList, **values)
    
  def updateSchema(self, schema):
    """
    Updates data data attributes properties with the given schema
    """
    self.data.updateSchema(schema)
    
  def update(self, feature):
    """
    Updates exist feature in the featureSet with the given feature
    :param editableFeature: editableFeature
    :type editableFeature: Java editableFeature
    """
    self.data.update(feature)
  
  def getSchema(self):
    """Returns data attributes definition"""
    return self.data.getSchema()

  def commit(self):
    """Finish edition saving changes"""
    self.data.commit()
  
  def abort(self):
    """Finish edition without saving changes"""
    self.data.abort()
    
  def getSelection(self):
    """Returns features selected set"""
    if self.selection == None:
      self.selection = Selection(self.data.getSelection())
      
    return self.selection
  
  def select(self, selection):
    """Inserts features in the features selection set"""
    self.getSelection().select(selection)

  def deselect(self, selection):
    """Remove features in the features selection set"""
    self.getSelection().deselect(selection)

  def isSelected(feature):
    """Returns True if given feature is selected"""
    self.getSelection().isSelect(feature)

  def getProjectionCode(self):
    """Returns projection code name"""
    return self.getProjection().getFullCode()


class Table(__DefaultTable__):
  """Represents gvsig table document 
  (org.gvsig.app.project.documents.table.TableDocument)
  """
  def __init__(self,table):
    __DefaultTable__.__init__(self, table, table.getStore())
    
  def getAssociatedLayer(self):
    """Returns table associated layer or None if there're not associated 
    layer
    """    
    if self._javaobj.getAssociatedLayer():
      return Layer(self._javaobj.getAssociatedLayer())
        
    return None

class Layer(__DefaultTable__):
  """Represents gvsig layer document. It's a wrapper from 
  org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect class
  """
  def __init__(self,layer):
    __DefaultTable__.__init__(self, layer, layer.getFeatureStore())
    
  def getTypeVectorLayer(self):
    """
    Returns layer DefaultGeometryType class 
    To get the geometryType use DefaultGeometryType getType() method
    To get the geometrySubType use DefaultGeometryType getSubType() method
    """
    return self().getTypeVectorLayer()
        
class FeatureSet(WrapperToJava):
  """Represents gvSIG FeatureSet 
  (org.gvsig.fmap.dal.feature.impl.featureset.DefaultFeatureSet)
  """
  def __init__(self,featureSet):
    WrapperToJava.__init__(self,featureSet)
    
  def getCount(self):
    """Returns the number of elements in the featureSet"""
    return self.getSize()
  
  def update(self, feature):
    """
    Updates exist feature in the featureSet
    :param editableFeature: editableFeature
    :type editableFeature: Java editableFeature
    """
    if not isinstance(feature, EditableFeature) and \
            not isinstance(feature, DefaultEditableFeature):
      feature = feature._javaobj
    self().update(feature)
  
  def __iter__(self):
    return  Iterator(self.fastIterator())
    
class Selection(FeatureSet):
  """Manage selected features set from a store. Represents gvSIG 
  org.gvsig.fmap.dal.feature.impl.DefaultFeatureSelection.  
  """
  def __init__(self, featureSet):
    FeatureSet.__init__(self, featureSet)
      
  def select(self, selection):
    """Inserts features in the features selection set"""
    if isinstance(selection,Feature) or isinstance(selection, FeatureSet):
      self._javaobj.select(selection._javaobj)
    else:
      self._javaobj.select(selection)

  def deselect(self, selection):
    """Removes features in the features selection set"""
    if isinstance(selection,Feature) or isinstance(selection, FeatureSet):
      self._javaobj.deselect(selection._javaobj)
    else:
      self._javaobj.deselect(selection)

  def isSelected(feature):
    """Returns True if given feature is selected"""
    if isinstance(feature, Feature):
      self._javaobj.isSelect(feature._javaobj)
    else:
      self._javaobj.isSelect(feature)
      
  def getCount(self):
    """Returns the number of elements in the selection"""
    return self.getSelectedCount()
    
    
class Iterator(WrapperToJava):

  def __init__(self,iterator):
    WrapperToJava.__init__(self,iterator)

  def next(self):
    if not self.hasNext():
      raise StopIteration()
    return Feature(self().next())
    
class Feature(WrapperToJava):
  """Represents feature data It's a wrapper from gvSIG 
  org.gvsig.fmap.dal.feature.impl.DefaultFeature class
  """
  def __init__(self,feature):
    WrapperToJava.__init__(self,feature)
    self.featureNoEditable = None

  def geometry(self):
    """Returns feature geometry"""
    return self.getDefaultGeometry()
   
  def getValues(self):
    """Returns dictionary with the pair name, value, feature attributes"""
    descriptor = self.getType()
    items = dict()
    for attr in descriptor.getAttributeDescriptors():
      name = attr.getName()
      value = self.get(attr.getName())
      items[name] = value
    return items 
  
  def edit(self):
    """Returns editable feature instance"""
    if not isinstance(self._javaobj, EditableFeature):
      self.featureNoEditable = self._javaobj
      self._javaobj = self._javaobj.getEditable()
  
  def __getitem__(self,key):
    return self.get(key)
  
  def __getattr__(self,name):
    #
    #FIX console error when try to introspect feature object
    if name in ('__methods__'):
        return dict()
    elif name in ('__members__'):
        return self.getValues().keys()
    elif name == '__dict__':
        return self.getValues()      
        
    try:
      v = getattr(self._javaobj, name, None)
      if v == None:
        v = self().get(name)        
      return v
    except Throwable, ex:
      raise RuntimeException("Can't access to attribute %s of feature (%s)" % (name, str(ex)))    

class Schema(WrapperToJava):
  """Stores data properties definition. Represents gvSIG FeatureType
  (org.gvsig.fmap.dal.feature.impl.DefaultFeatureType)
  """

  def __init__(self, featureType):
    WrapperToJava.__init__(self,featureType)
    self.featureTypeNoEditable = None
  
  def append(self, name, type, size=None, default=None, precision=4):
    """Adds new property to feature properties definition. If error occurs 
    raises RuntimeError.    
    :param name: Feature property name
    :type name: String
    :param type: Feature property type
    :type name: String
    :param size: Feature property size
    :type size: int
    :param default: Feature property default value
    :return: new atribute
    """
    if not isinstance(self._javaobj, EditableFeatureType):
        self.modify()
    
    if isinstance(type, str):
      try:
        application = ApplicationLocator.getManager()
        datamanager =  application.getDataManager()
        dataTypes = application.getDataTypesManager()
        type = dataTypes.getType(type) #dataType constant value from string
      except:
        raise RuntimeError(
            "Feature Property Data type (%s) is not valid.  name=%s, type=%s, size=%s, default=%s)" % (
                type, 
                name, 
                type, 
                size, 
                default
            )
        )
    if isinstance(type, int):
      try:
        type = dataTypes.get(type)
      except:
        raise RuntimeError(
            "Data type (%s) is not valid.  name=%s, type=%s, size=%s, default=%s)" % (
                type, 
                name, 
                type, 
                size, 
                default
            )
        )
      
    attribute = self.add(name, type.getType())
 
    if size != None: 
      attribute.setSize(size)
    
    if default != None:
      attribute.setDefaultValue(default)
      
    if precision != None and type.getType() in (DataTypes.DOUBLE, DataTypes.FLOAT):
      attribute.setPrecision(precision)
    
    if type.getType() == DataTypes.GEOMETRY and self.getDefaultGeometryAttributeName()==None:
      self.setDefaultGeometryAttributeName(name)
    
    return attribute

  def __getitem__(self, name):
    try:
      return self.ftype.getAttributeDescriptor(name)
    except java.lang.IndexOutOfBoundsException:
      raise StopIteration

  def get(self, name, default=None):
    """Returns a feature attribute descriptor that contains information about 
    one feature attribute, such as its name, data type or precision. 
    :param name: Attribute name
    :type name: string
    :param default: Value to return if no attribute name found. 
    :return: AttributeDescriptor
    """
    x = self.getAttributeDescriptor(name)
    if x == None:
      return default
    return x
  
  def getAttrNames(self):
    """Returns a list with attributes names"""
    if not self.getAttributeDescriptors():
      return None
        
    return [attr.getName() for attr in self.getAttributeDescriptors()]
    
    
  def getCopy(self):
    """Returns a itself clone"""
    return Schema(self().getCopy())
  
  def modify(self):
    """Sets edit mode"""
    if not isinstance(self._javaobj, EditableFeatureType):
      self.featureTypeNoEditable = self._javaobj
      self._javaobj = self._javaobj.getEditable()

      
#=====================#
# Vectorial Functions #
#=====================#

def createSchema(schema = None):
  """Returns attributes definition. If Schema is recived then makes a copy and 
  returns editable instance. Otherwise returns empty Schema.
  :param schema: Schema to make a copy
  :type schema: Schema
  """
  if isinstance(schema, Schema):
    try:  
      s = schema.getCopy()
      s.modify()
      return s
    except:
      pass
    
  application = ApplicationLocator.getManager()
  datamanager =  application.getDataManager()
  return Schema(datamanager.createFeatureType())

def createLayer(schema, servertype, layertype=None, **parameters):
  """
  Returns new layer
  :param schema: layer data definition
  :type schema: Schema
  :param servertype: 
  :type servertype: string
  :return: new layer
  :rtype: Layer
  :raise: RuntimeException
  """
  if layertype == None:
    layertype = servertype
    servertype = "FilesystemExplorer"

  if layertype == "Shape" :
    if schema.get("GEOMETRY",None) == None:
      raise RuntimeException("Shape need a field named GEOMETRY in the schema")
    
  if parameters["geometryType"] == None:
    raise RuntimeException("Invalid geometry type for new layer")
      
  try:
    application = ApplicationLocator.getManager()
    datamanager =  application.getDataManager()

    mapcontextmanager = application.getMapContextManager()

    server_parameters = datamanager.createServerExplorerParameters(servertype)
    copyToDynObject(parameters, server_parameters)

    server = datamanager.openServerExplorer(servertype, server_parameters)

    store_parameters = server.getAddParameters(layertype)
    copyToDynObject(parameters, store_parameters)
    store_parameters.setDefaultFeatureType(schema())

    server.add(layertype, store_parameters, True)

    store = datamanager.openStore(layertype, store_parameters)

    layer = mapcontextmanager.createLayer(store.getName(), store)

    return Layer(layer)
  except Throwable, ex:
    raise RuntimeException("Can't create layer, "+ str(ex))

def loadShapeFile(shpFile, CRS="CRS:84"):
    """
    Add existing shape file to the view. Returns Layer shape file
    :param shpFile: absolute file path
    :type: shpFile: string
    :param CRS: projection code
    :type CRS: string
    :return: the shape
    :type return: Layer
    """
    layer = loadLayer('Shape', shpFile=shpFile, CRS=CRS)
    currentView().addLayer(layer)
    layer.setActive(True)
    return Layer(layer)
    
def loadLayer(layerType, **parameters):
    try:
        application = ApplicationLocator.getManager()
        datamanager =  application.getDataManager()
        mapcontextmanager = application.getMapContextManager()
        store_parameters = datamanager.createStoreParameters(layerType)
        copyToDynObject(parameters, store_parameters)
        store = datamanager.openStore(layerType, store_parameters)        
        layer = mapcontextmanager.createLayer(store.getName(), store)
    except Throwable, ex:
        raise RuntimeException("Can't load layer, "+ str(ex))  

    return layer
    
def createShape(definition, filename, geometryType, CRS="CRS:84"):
  """
  Return new shape layer 
  :param definition: layer data definition
  :type definition: Schema
  :param filename: absolute path for shape files. 
  :type filename: string
  :param geometryType: geometry type for shape
  :type geometryType: string
  :return: new shape layer
  :rtype: Layer
  """
  return createLayer(
    definition, 
    "FilesystemExplorer", 
    "Shape", 
    shpFile=filename, 
    CRS=CRS,
    geometryType = geometryType
  )

def createTable(schema, servertype, tableType=None, **parameters):
  """Creates a new Table document"""
  if tableType == None:
    tableType = servertype
    servertype = "FilesystemExplorer"

  try:
    application = ApplicationLocator.getManager()
    datamanager =  application.getDataManager()
    
    server_parameters = datamanager.createServerExplorerParameters(servertype)
    copyToDynObject(parameters, server_parameters)

    server = datamanager.openServerExplorer(servertype, server_parameters)
    
    store_parameters = server.getAddParameters(tableType)
    copyToDynObject(parameters, store_parameters)
    store_parameters.setDefaultFeatureType(schema())
    
    server.add(tableType, store_parameters, True)

    store = datamanager.openStore(tableType, store_parameters)
    
    return Table(store)
  
  except Throwable, ex:
    raise RuntimeException("Can't create table, "+ str(ex))
  
def createDBF(definition, DbfFile, CRS="CRS:84"):
  """
  Creates a new dbf document 
  :param definition: layer data definition
  :type definition: Schema
  :param DbfFile: absolute path for shape files. 
  :type DbfFile: string
  :return: new dbf
  :rtype: Table    
  """
  return createTable(
    definition, 
    "FilesystemExplorer", 
    "DBF", 
    DbfFile=DbfFile, 
    CRS=CRS,
  )

#=====================#
# Documents Functions #
#=====================#  

def currentProject():
  """
  Returns current gvSIG proyect
  :return: Proyect
  """
  application = ApplicationLocator.getManager()
  project = application.getCurrentProject()
  return Project(project)

def currentDocument(documentClass = None):
  """
  Returns the current active document if it's a DocumentTable or 
  DocumentView2D return None
  :return: Active document, None
  """
  application = ApplicationLocator.getManager()
  
  if documentClass == None:
    doc = application.getActiveDocument()
  else:
    doc = application.getActiveDocument(documentClass)
  if isinstance(doc, TableDocument):
    return Table(doc)
  if isinstance(doc, ViewDocument):
    return View(doc)  

  return None  
  
def currentTable():
  """
  Returns active table document or None 
  :return: Table or None
  """  
  return currentDocument(TableDocument)
  
def currentView():
  """
  Returns the current active view document or None 
  :return: View or None
  """ 
  return currentDocument(ViewDocument)

def currentLayer():
  """
  Returns current view active layer or None
  :return: gvSIG active layer
  """
  try:
    return currentView().getLayer()
  except:
    return None

#=====================#
# Simbology Functions #
#=====================#  
COLORS = {
    'black': Color.black,
    'blue': Color.blue,
    'cyan': Color.cyan,
    'darkGray': Color.darkGray,
    'gray': Color.gray,
    'green': Color.green,
    'lightGray': Color.lightGray,
    'magenta': Color.magenta,
    'orange': Color.orange,
    'pink': Color.pink,
    'red': Color.red,
    'white': Color.white,
    'yellow': Color.yellow,
}
  
def simplePointSymbol(color=None):
  """
  Returns simple point symbol using parameter color. If no color 
  use a ramdom color
  :param color: String color name or Java awt Color
  :return: gvSIG point symbol
  """
  if isinstance(color, str) and COLORS.has_key(color.lower()):
    color = COLORS.get(color)
    
  if not isinstance(color, Color):
      color = getDefaultColor()
      
  return MapContextLocator.getSymbolManager().createSymbol(
        Geometry.TYPES.POINT, color)

def simpleLineSymbol(color=None):
  """
  Returns simple line symbol using parameter color. If no color use a 
  ramdom color  
  :param color: String color name or Java awt Color
  :return: gvSIG line symbol
  
  """
  if isinstance(color, str) and COLORS.has_key(color.lower()):
    color = COLORS.get(color)

  if not isinstance(color, Color):
      color = getDefaultColor()
      
  return MapContextLocator.getSymbolManager().createSymbol(
        Geometry.TYPES.CURVE, color)

def simplePolygonSymbol(color = None):
  """
  Returns simple polygon symbol using parameter color. If no color 
  use a ramdom color.  
  :param color: String color name or Java awt Color
  :return: gvSIG polygon symbol
  """
  if isinstance(color, str) and COLORS.has_key(color.lower()):
    color = COLORS.get(color)

  if not isinstance(color, Color):
      color = getDefaultColor()
  
  return MapContextLocator.getSymbolManager().createSymbol(
        Geometry.TYPES.SURFACE, color)
  

#=========================================#
# gvSIG Application Preferences Functions #
#=========================================#  

def getDataFolder():
  """
  Returns gvSIG data folder. This folder is defined in application 
  preferences. If is not defined returns None.
  """
  return Preferences.userRoot().node("gvsig.foldering").get('DataFolder', None)
  
def getProjectsFolder():
  """
  Returns gvSIG projects folder. This folder is defined in application 
  preferences. If is not defined returns None.
  """
  return Preferences.userRoot().node("gvsig.foldering").get(
        'ProjectsFolder', None)

def getColorFromRGB(r, g, b, a=None):
  """
  Returns an sRGB color with the specified red, green, blue, and alpha 
  (optional) values in the range (0 - 255).
  """
  if a:
    color = Color(r, g, b, a)
  else:
    color = Color(r, g, b)
  
  return color
  
def getDefaultColor(c = None):
  """Returns gvsig default symbol fill color or ramdom color"""
  if MapContextLocator.getSymbolManager().isDefaultSymbolFillColorAleatory():
    color = Color(random.randint(0-255), 
                random.randint(0-255), 
                random.randint(0-255)
            )
  else:
    sp = MapContextLocator.getSymbolManager().getSymbolPreferences()
    color = sp.getDefaultSymbolFillColor()    

  return color  
  
#================#
# OTHER          #
#================#

def getCRS(crs):
  """Returns Projection from string code (i.e. CRS:84) if exist or None if 
  not.  
  """
  try:
    return CRSFactory.getCRS(crs)
  except:
    return None

def copyToDynObject(values, target):
  definition = target.getDynClass();
  fields = definition.getDynFields();
  for field in fields:
    name = field.getName()
    keys = values.keys()
    for k in keys:
      if k.lower() == name.lower():
        target.setDynValue(name, values[name])
        break
# ====================================
#

def main():
  layer = currentLayer()
  schema = createSchema()
  schema.append("ID", "String", 50)
  schema.append("GEOMETRY", "Geometry")
  
  output = createLayer(
      schema, 
      "FilesystemExplorer", 
      "Shape", 
      shpFile="/tmp/pp.shp", 
      CRS="EPSG:23030", 
      geometryType=POINT
  )
  
  for feature in layer.features():
    point = feature.geometry().centroid()
    output.append(ID=feature.ID, GEOMETRY=point)
      
  output.commit() 
  
  currentView().addLayer(output()) 
