
import os
import time
import sys
import StringIO
import random

from java.util.prefs import Preferences
from java.awt import Color
from java.io import File
from java.lang import Throwable, RuntimeException

from project import currentView, currentProject

from org.gvsig.app import ApplicationLocator
from org.gvsig.fmap.dal.feature import FeatureType
from org.gvsig.fmap.mapcontext import MapContextLocator
from org.gvsig.andami import Utilities
from org.gvsig.fmap.crs import CRSFactory
from org.gvsig.app.project.documents.table import TableManager
from org.gvsig.fmap.geom import Geometry
from org.gvsig.app.project.documents.view import DefaultViewDocument
from org.gvsig.scripting.app.extension import ScriptingExtension

LOGGER_INFO=ScriptingExtension.INFO
LOGGER_WARN=ScriptingExtension.WARN
LOGGER_ERROR=ScriptingExtension.ERROR


def logger(msg, mode=LOGGER_INFO, ex=None):
  if ex == None:
    ScriptingExtension.log(mode,msg,None)
  else:
    tb = sys.exc_info()[2]
    f = StringIO.StringIO()
    f.write("%s\n" % msg)
    f.write("Traceback (most recent call last):\n")
    while tb!=None:
      code = tb.tb_frame.f_code
      f.write("  File %r, line %d, %s\n" % (code.co_filename,tb.tb_frame.f_lineno,code.co_name))
      tb = tb.tb_next
    f.write("%s\n" % str(ex))
    f.getvalue()
    ScriptingExtension.log(mode,f.getvalue(),None)

def getResource(*args):
  base = args[0]
  if os.path.isfile(base):
    base = os.path.dirname(base)
  x = [ base,]
  x.extend(args[1:])
  return os.path.join(*x)

def openStore(type, **parameters):
  from gvsig.utils import copyToDynObject
  application = ApplicationLocator.getManager()
  datamanager =  application.getDataManager()
  store_parameters = datamanager.createStoreParameters(type)
  copyToDynObject(parameters, store_parameters)
  store = datamanager.openStore(type, store_parameters)
  return store

def createFeatureType(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, FeatureType):
    try:
      s = schema.getCopy().getEditable()
      return s
    except:
      pass

  application = ApplicationLocator.getManager()
  datamanager =  application.getDataManager()
  return datamanager.createFeatureType()

createSchema = createFeatureType

def createLayer(schema, servertype, layertype=None, **parameters):
  """
  Returns new layer
  :param schema: layer data feature type
  :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

  except Throwable, ex:
    logger("Can't create layer of type %s, params=(%s), schema=%s " % (layertype,parameters,schema) , LOGGER_WARN, ex)
    raise RuntimeException("Can't create layer")

def loadShapeFile(shpFile, **parameters):
    """
    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
    :param gvViewName: name of the view where to be loaded
    :type gvViewName: string
    :param gvLayerName: name of the layer inside gvsig
    :type gvLayerName: string
    :return: the shape
    :type return: Layer
    """
    if parameters.get("CRS",None)==None:
        try:
            parameters["CRS"] = currentView().getProjectionCode()
        except:
            parameters["CRS"] = "EPSG:4326"
    parameters["shpFile"]=shpFile
    layer = loadLayer('Shape', **parameters)
    viewName = parameters.get("gvViewName",None)
    if viewName == None:
        view = currentView()
    elif isinstance(viewName, DefaultViewDocument):
        view = viewName
    else:
        project = currentProject()
        view = project.getView(viewName)
        if view == None:
            raise RuntimeException("Can't get a view with name: "+str(viewName))
    view.addLayer(layer)
    layer.setActive(False)
    return layer

def loadRasterFile(filename, **parameters):
    parameters["uri"]= File(filename).toURI()
    ext = os.path.splitext(filename)
    #if ext[1].lower() == ".jp2" or ext[1].lower()=="ecw":
    #  layer = loadLayer("Ermapper Store", **parameters)
    #else:
    #  layer = loadLayer('Gdal Store', **parameters)
    layer = loadLayer('Gdal Store', **parameters)
    viewName = parameters.get("gvViewName",None)
    print "parameters:", parameters
    print viewName
    if viewName == None:
        view = currentView()
    elif isinstance(viewName, DefaultViewDocument):
        view = viewName
    else:
        project = currentProject()
        view = project.getView(viewName)
        if view == None:
            raise RuntimeException("Can't get a view with name: "+str(viewName))
    view.addLayer(layer)
    layer.setActive(False)
    return 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)
        layerName = parameters.get("gvLayerName", store.getName())
        layer = mapcontextmanager.createLayer(layerName, store)
    except Throwable, ex:
	logger("Can't load layer of type %s, params=(%s)" % (layerType,parameters) , LOGGER_WARN, ex)
        raise RuntimeException("Can't load layer, "+ str(ex))

    return layer

def createShape(schema, filename=None, geometryType=None, CRS=None, prefixname="tmpshp"):
  """
  Return new shape layer
  :param schema: layer data definition
  :type schema: FeatureType
  :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
  """
  if CRS==None:
    try:
        CRS = currentView().getProjectionCode()
    except:
        CRS = "EPSG:4326"
  if filename == None:
            filename = getTempFile(prefixname, ".shp")
  geomattr = schema.getDefaultGeometryAttribute()
  if geomattr == None:
    raise RuntimeException("Can't create a shp without geometry attribute")
  if geometryType == None :
    geometryType = geomattr.getGeomType().getType()
  else:
    if geometryType != geomattr.getGeomType().getType():
      raise RuntimeException("Can't create a shp, geoemtry mismatch.")

  return createLayer(
    schema,
    "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 store

  except Throwable, ex:
    logger("Can't create table of type %s, params=(%s), schema=%s." % (tableType,parameters,repr(schema)) , LOGGER_WARN, ex)
    raise RuntimeException("Can't create table, "+ str(ex))

def createDBF(schema, DbfFile=None, prefixname="tmpdbf", CRS="CRS:84"):
      """
      Creates a new dbf document
      :param schema: layer data feature type
      :type schema: Schema
      :param DbfFile: absolute path for shape files.
      :type DbfFile: string
      :return: new dbf
      :rtype: Table
      """
      if DbfFile == None:
            DbfFile = getTempFile(prefixname, ".dbf")

      return createTable(
        schema,
        "FilesystemExplorer",
        "DBF",
        DbfFile=DbfFile,
        CRS=CRS,
      )

def loadTable(format, **parameters):

    try:
        application = ApplicationLocator.getManager()
        datamanager =  application.getDataManager()

        # Loding the data store
        store_parameters = datamanager.createStoreParameters(format)
        copyToDynObject(parameters, store_parameters)
        store = datamanager.openStore(format, store_parameters)

        # Creating a Table document and initialize with the data store
        project = currentProject()
        table = project().createDocument(TableManager.TYPENAME)
        table.setStore(store)
        table.setName(store.getName())

        # Add the Table document to the project
        project().addDocument(table)

    except Throwable, ex:
	logger("Can't load table of type %s, params=(%s)" % (format,parameters) , LOGGER_WARN, ex)
        raise RuntimeException("Can't load table, "+ str(ex))

    return table

def loadDBF(dbffile):
      table = loadTable(format="DBF",DbfFile=dbffile)
      return table

#=====================#
# 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():
  """Returns gvsig default symbol fill color or ramdom color"""
  if MapContextLocator.getMapContextManager().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):
  keys = values.keys()
  definition = target.getDynClass();
  fields = definition.getDynFields();
  for field in fields:
    name = field.getName()
    for k in keys:
      if k.lower() == name.lower():
	value = values[k] 
        target.setDynValue(name, value)
        break
# ====================================
#

"""
def cloneShape(layer, target=None, expresion = None, sortby="", asc=True):
  if target==None:
    #  una capa temporal en el temp de andami
    #...
  else:
    targetfile = File(target)
    if targetfile.isAbsolute():
      #La guardamos ahi mismo
    else:
     # Al temp de andami con ese nombre.

  # Crear la nueva capa

  return newLayer
"""

def getTempFile(name, ext, tempdir=None):
    if tempdir==None:
        tempdir = Utilities.TEMPDIRECTORYPATH
    if not os.path.isdir(tempdir):
      os.makedirs(tempdir)
    t = time.time()
    f = os.path.join(
      tempdir,
      "%s-%x%x%s" % (name,t,(t-int(t)) * 10000,ext)
    )
    return f
