# -*- coding: utf-8 -*-
#
# File: geom.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.
#
#

"""
Utility functions to manage gvSIG geometries
"""

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

__docformat__ = 'plaintext'


from org.gvsig.fmap.geom import Geometry, GeometryLocator
from org.gvsig.tools import ToolsLocator
from org.gvsig.fmap.geom import Geometry
from org.gvsig.fmap.geom.primitive import Line, Polygon, Point

#GeometryTypes
AGGREGATE = Geometry.TYPES.AGGREGATE
ARC = Geometry.TYPES.ARC
CIRCLE = Geometry.TYPES.CIRCLE
CURVE = Geometry.TYPES.CURVE
ELLIPSE = Geometry.TYPES.ELLIPSE
ELLIPTICARC = Geometry.TYPES.ELLIPTICARC
GEOMETRY = Geometry.TYPES.GEOMETRY
MULTICURVE = Geometry.TYPES.MULTICURVE
MULTIPOINT = Geometry.TYPES.MULTIPOINT
MULTISOLID = Geometry.TYPES.MULTISOLID
MULTISURFACE = Geometry.TYPES.MULTISURFACE
NULL = Geometry.TYPES.NULL
POINT = Geometry.TYPES.POINT
SOLID =  Geometry.TYPES.SOLID
SPLINE = Geometry.TYPES.SPLINE
SURFACE = Geometry.TYPES.SURFACE

# Common named geometry types
POLYGON = Geometry.TYPES.SURFACE
LINE = Geometry.TYPES.CURVE
MULTILINE = Geometry.TYPES.MULTICURVE
MULTIPOLYGON = Geometry.TYPES.MULTISURFACE

# geometrySubTypes
D2 = Geometry.SUBTYPES.GEOM2D
D2M = Geometry.SUBTYPES.GEOM2DM
D3 = Geometry.SUBTYPES.GEOM3D
D3M = Geometry.SUBTYPES.GEOM3DM
UNKNOWN = Geometry.SUBTYPES.UNKNOWN

# Dimensions
DIMENSIONS = Geometry.DIMENSIONS

def createGeometry(type, subtype=D2):
    """
    Returns a new geometry with a concrete type and subtype or None if can't
    create geometry.
    :param type: geometry type
    :type type: string
    :param subtype: geometry subtype
    :type type: string
    :return: geometry
    :rtype: geometry
    """
    try:
        geometryManager = GeometryLocator.getGeometryManager()
        geometry = geometryManager.create(type, subtype)
    except:
        return None
    return geometry

def createPoint2D(x=None, y=None):
    if x==None and y==None:
        return createPoint(D2)
    elif x==None or y==None:
        return None
    else:
        return createPoint(D2, x, y)

def createLine2D(vertexes=None):
    if vertexes==None:
        return createLine(D2)
    else:
        return createLine(D2, vertexes)

def createPolygon2D(vertexes=None):
    if vertexes==None:
        return createPolygon(D2)
    else:
        return createPolygon(D2, vertexes)


def createPoint(subtype=D2,*coords):
    """
    Returns a new point with a subtype and sets the value for the X, Y, Z and M
    coordinates (default 0,0,0,0) or None if can't create point
    :param subtype: geometry type
    :type subtype: integer
    :param coords: list of coordinates
    :type coords: list
    :return: point
    :rtype: Geometry
    """

    geometryManager = GeometryLocator.getGeometryManager()
    point = geometryManager.create(POINT, subtype)

    try:
        #If is geometry, transform to a new point type.
        if len(coords)==0:
            return point
        else:
            # Coords with one parameter
            # Check if is a Geometry

            if len(coords)==1: #FIXME
                coords = coords[0]

            if isinstance(coords, Point):
                return coords

            #Check if object have GET attribute
            get = getattr(coords,"__getitem__",None)
            if get != None and len(coords)>=2:
                if subtype==D2:
                    point.setCoordinateAt(Geometry.DIMENSIONS.X, get(0))
                    point.setCoordinateAt(Geometry.DIMENSIONS.Y, get(1))
                elif subtype==D3:
                    point.setCoordinateAt(Geometry.DIMENSIONS.X, get(0))
                    point.setCoordinateAt(Geometry.DIMENSIONS.Y, get(1))
                    try:
                        z = get(2)
                    except:
                        z = 0
                    point.setCoordinateAt(Geometry.DIMENSIONS.Z, z)
                elif subtype==D2M:
                    point.setCoordinateAt(Geometry.DIMENSIONS.X, get(0))
                    point.setCoordinateAt(Geometry.DIMENSIONS.Y, get(1))
                    try:
                        m = get(2)
                    except:
                        m = 0
                    point.setCoordinateAt(point.getDimension()-1, m)
                elif subtype==D3M:
                    point.setCoordinateAt(Geometry.DIMENSIONS.X, get(0))
                    point.setCoordinateAt(Geometry.DIMENSIONS.Y, get(1))
                    try:
                        z = get(2)
                    except:
                        z = 0
                    point.setCoordinateAt(Geometry.DIMENSIONS.Z, z)
                    try:
                        m = get(3)
                    except:
                        m = 0
                    point.setCoordinateAt(point.getDimension()-1, m)
        return point
    except:
        return None





def createMultiPoint(subtype=D2, points=None):
    """
    Returns a new multipoint with a subtype from a list of Points instances.
    Also values of X and Y tuples like ([x1, y1], [x2, y2], ...., [xn, yn]) are
    allowed. If not points returns empty multipoint geometry. If can't create
    geometry return None.
    :param subtype: geometry type
    :type subtype: integer
    :param points: list of points
    :type points: list
    :return: multipoint
    :rtype: multipoint
    """

    multipoint = createGeometry(MULTIPOINT, subtype)
    if points!=None:
        try:
            for point in points:
                if isinstance(point, Geometry):
                    pgeom = point
                else:
                    pgeom = createPoint(subtype, point)
                multipoint.addPrimitive(pgeom)
        except:
            return None

    return multipoint

def createPolygon(subtype=D2, vertexes=None):
    """
    Returns a new polygon with a subtype. Or None if can't create the geometry
    :return: polygon
    :rtype: Geometry
    """
    polygon = createGeometry(SURFACE, subtype)
    if vertexes!= None:
        try:
            for vertex in vertexes:
                vgeom = createPoint(subtype, vertex)
                polygon.addVertex(vgeom)

        except:
            return None
    return polygon

def createMultiPolygon(subtype=D2, polygons=None):
    """
    Returns a new multipolygon with a subtype. or None if can't create geometry
    :return: multipolygon
    :rtype: Geometry
    """
    multipolygon = createGeometry(MULTISURFACE, subtype)
    if polygons!=None:
        try:
            for polygon in polygons:
                if isinstance(polygon, Polygon): #, Polygon)
                    pass
                else:
                    polygon = createPolygon(subtype=subtype,vertexes=polygon)
                multipolygon.addPrimitive(polygon)
        except:
            return None

    return multipolygon

def createLine(subtype=D2, vertexes=None):
    """
    Returns a new line with a subtype or None if can't create geometry
    :return: polygon
    :rtype: Geometry
    """
    line = createGeometry(CURVE, subtype)
    if vertexes!=None:
        try:
            for vertex in vertexes:
                v = createPoint(subtype, vertex)
                line.addVertex(v)
        except:
            return None

    return line

def createMultiLine(subtype=D2, lines=None):
    """
    Create a new multiline with a subtype or None if can't create geometry
    :return: multiline
    :rtype: Geometry
    """
    multiline = createGeometry(MULTILINE, subtype)
    if lines:
        try:
            for line in lines:
                if isinstance(line, Line): #,Line)
                    pass
                else:
                    line = createLine(subtype=subtype, vertexes=line)
                multiline.addPrimitive(line)
        except:
            return None

    return multiline

def createEnvelope(pointMin=None, pointMax=None, dimension=2):
    """
    Returns envelope as a minimum bounding box or rectangle. This envelope is
    equivalent to the GM_Envelope specified in ISO 19107.
    :param pointMax:
    :type pointMax: geometry POINT
    :param pointMin:
    :type pointMin: geometry POINT
    :return: envelope
    :rtype Geometry
    """
    # Coord min point
    get=getattr(pointMin,"__getitem__",None)
    if get != None:
        xmin = get(0)
        ymin = get(1)
    else:
        xmin = pointMin.getX()
        ymin = pointMin.getY()

    # Coord max point
    get=getattr(pointMax,"__getitem__",None)
    if get != None:
        xmax = get(0)
        ymax = get(1)
    else:
        xmax = pointMax.getX()
        ymax = pointMax.getY()

    geometryManager = GeometryLocator.getGeometryManager()
    if pointMax!=None and pointMin!=None:
        envelope = geometryManager.createEnvelope(xmin, ymin, xmax, ymax, dimension)
    else:
        envelope = geometryManager.createEnvelope()

    return envelope


def createGeometryFromWKT(WKT):
    """
    Create geometry from WKT string
    :param WKT
    :type WKT: string
    :return: geometry
    :rtype: Geometry
    """
    geometryManager = GeometryLocator.getGeometryManager()
    geometry = geometryManager.createFrom(WKT)
    return geometry
