﻿/// <reference path="../OpenLayers.js" />

/**
* Extensions to OpenLayers.Geometry Class
*  - OpenLayers.Geometry.validateGeometry
*      - OpenLayers.Geometry.validateLinearRing
*      - OpenLayers.Geometry.validatePolygon
*      - OpenLayers.Geometry.validateMultiPolygon
*      - other geometry types are automatically considered valid
*  - OpenLayers.Geometry.getVertices
*  - OpenLayers.Geometry.getEdges
*  - OpenLayers.Geometry.getBounds
*/

/**
* Method: OpenLayers.Geometry.validateLinearRing
* Validates a LinearRing, to ensure it has no intersecting line segments
*
* Parameters:
* geometry - {OpenLayers.Geometry.LinearRing}
*
* Returns:
* {Boolean} LinearRing is valid
*/
OpenLayers.Geometry.validateLinearRing = function(geometry) {
    //First check that input geometry is actually a linearring
    if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
        try {
            //Get all line segments in the linearring
            var segs = geometry.getSortedSegments();

            //Check that it is actually a ring and that the ring does not consist of only one line
            if (segs.length < 3 || OpenLayers.Geometry.isOneLine(geometry)) {
                return false;
            }

            //Enumerate all line segments
            for (var i = 0; i < segs.length; i++) {
                //Check this line segment (segs[i]) to see if it intersects with any other the segments (segs[j])
                for (var j = i + 1; j < segs.length; j++) {
                    //Only check for intersection if the two segments do not have intersecting end points (not quite sure that this is fool proof)
                    if (!((segs[j].x1 == segs[i].x2 && segs[j].y1 == segs[i].y2) ||
                        (segs[j].x2 == segs[i].x1 && segs[j].y2 == segs[i].y1) ||
                        (segs[j].x1 == segs[i].x1 && segs[j].y1 == segs[i].y1) ||
                        (segs[j].x2 == segs[i].x2 && segs[j].y2 == segs[i].y2))) {

                        //Check for intersection
                        if (OpenLayers.Geometry.segmentsIntersect(segs[j], segs[i])) {
                            return false;
                        }
                    }
                }
            }
        } catch (ex) {
            return false; //If an exception occurs the linearring can not be considered valid
        }
    } else {
        return false;
    }

    //If we got this far the linear ring should be valid
    return true;
}

/**
* Method: OpenLayers.Geometry.isOneLine
* Checks if Polygon geometry has two sets of points which are the same.
* This is to avoid a scenario where drawing of a polygon can be accomplished with only two clicks.
*
* Parameters:
* geometry - {OpenLayers.Geometry.LinearRing}
*
* Returns:
* {Boolean} does polygon geometry consist of only two points
*/
OpenLayers.Geometry.isOneLine = function(geometry) {
    if (geometry.getSortedSegments().length == 3) {
        var comps = geometry.components;
        if (comps[0].equals(comps[3]) && comps[1].equals(comps[2])) 
            return true;
    }
    return false;
}

/**
* Method: OpenLayers.Geometry.validatePolygon
* Validates a Polygon, to ensure that the linear rings that it is comprised of is not intersecting
* and that none of the linear rings has any intersecting line segments
*
* Parameters:
* geometry - {OpenLayers.Geometry.Polygon}
*
* Returns:
* {Boolean} Polygon is valid
*/
OpenLayers.Geometry.validatePolygon = function(geometry) {
    //First check that input geometry is actually a polygon
    if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
        //If the polygon contains holes check that none of the holes intersects with the outerring or one another
        //also make sure that all holes are contained by the outerring and that no hole contains other holes
        if (geometry.components.length > 1) {
            for (var i = 1; i < geometry.components.length; i++) {
                //Check if intersecting outerring
                if (geometry.components[i].intersects(geometry.components[0])) {
                    return false;
                }

                //Make sure the hole is contained by the outerring
                var contained = false;
                for (var j = 0; j < geometry.components[i].components.length; j++) {
                    if (geometry.containsPoint(geometry.components[i].components[j])) {
                        contained = true;
                        break;
                    }
                }
                if (!contained) {
                    return false;
                }

                //Check if intersecting or contained by other holes
                for (var j = i + 1; j < geometry.components.length; j++) {
                    if (geometry.components[j].intersects(geometry.components[i])) {
                        return false;
                    }

                    //Check that none of the points in hole is contained in other holes
                    for (var k = 0; k < geometry.components[j].components.length - 1; k++) {
                        //Make sure that the point exists before checking if it is contained
                        //(when dragging a virtual vertice this is necesary, not sure why)
                        if (geometry.components[i].components[k]) {
                            if (geometry.components[j].containsPoint(geometry.components[i].components[k])) {
                                return false;
                            }
                        }
                    }
                }
            }
        }

        //Validate each linear ring in the polygon (both innerrings and outerring)
        for (var i = 0; i < geometry.components.length; i++) {
            if (!OpenLayers.Geometry.validateLinearRing(geometry.components[i])) {
                return false;
            }
        }

    } else {
        return false;
    }

    //If we got this far the polygon should be valid
    return true;
}

/**
* Method: OpenLayers.Geometry.validateMultiPolygon
* Validates a MultiPolygon, to ensure that the polygons that it is comprised of are valid
*
* Parameters:
* geometry - {OpenLayers.Geometry.MultiPolygon}
*
* Returns:
* {Boolean} MultiPolygon is valid
*/
OpenLayers.Geometry.validateMultiPolygon = function(geometry) {
    //First check that input geometry is actually a multi polygon
    if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") {
        //Validate each polygon in the multipolygon
        for (var i = 0; i < geometry.components.length; i++) {
            if (!this.validatePolygon(geometry.components[i])) {
                return false;
            }
        }
    } else {
        return false;
    }

    return true;
}

/**
* Method: OpenLayers.Geometry.validateGeometry
* Validates a geometry.
* TBD: Only validation of MultiPolygon, Polygon and LinearRing is implemented. Returns true for other OpenLayers.Geometry.* objects
*
* Parameters:
* geometry - {OpenLayers.Geometry.*}
*
* Returns:
* {Boolean} Geometry is valid
*/
OpenLayers.Geometry.validateGeometry = function(geometry) {
    if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
        return OpenLayers.Geometry.validateLinearRing(geometry);
    } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
        return OpenLayers.Geometry.validatePolygon(geometry);
    } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") {
        return OpenLayers.Geometry.validateMultiPolygon(geometry);
    } else if (geometry.CLASS_NAME.substr(0, "OpenLayers.Geometry".length) == "OpenLayers.Geometry") {
        //Return true for other OpenLayers.Geometry objects
        return true;
    } else {
        return false;
    }
}

/**
* Method: OpenLayers.Geometry.getVertices
* Get all vertices in a geometry
*
* Parameters:
* geometry - {OpenLayers.Geometry.*}
*
* Returns:
* {Array[{OpenLayers.Geometry.Point}]}
*/
OpenLayers.Geometry.getVertices = function(geometry) {
    var vertices = [];

    if (geometry.componentTypes) {
        if (geometry.componentTypes.length == 1 && geometry.componentTypes[0] == "OpenLayers.Geometry.Point") {
            return geometry.components;
        } else {
            for (var i = 0; i < geometry.components.length; i++) {
                vertices = vertices.concat(this.getVertices(geometry.components[i]));
            }
        }
    } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
        vertices = vertices.concat([geometry]);
    }

    return vertices;
}

/**
* Method: OpenLayers.Geometry.getEdges
* Get all edges in a geometry
*
* Parameters:
* geometry - {OpenLayers.Geometry.*}
*
* Returns:
* {Array[{OpenLayers.Geometry.LineString}]}
*/
OpenLayers.Geometry.getEdges = function(geometry) {
    var edges = [];

    if (geometry.componentTypes) {
        if (geometry.componentTypes.length == 1 && geometry.componentTypes[0] == "OpenLayers.Geometry.Point") {
            for (var i = 0; i < geometry.components.length - 1; i++) {
                var start = geometry.components[i].clone();
                var stop = geometry.components[i + 1].clone();
                edges = edges.concat(new OpenLayers.Geometry.LineString([start, stop]));
            }
            return edges;
        } else {
            for (var i = 0; i < geometry.components.length; i++) {
                edges = edges.concat(this.getEdges(geometry.components[i]));
            }
        }
    }

    return edges;
}

/**
* Method: OpenLayers.Geometry.getBounds
* Gets the bounds of one or more features or geometries
*
* Parameters:
* features - {OpenLayers.Feature.Vector} | Array[{OpenLayers.Feature.Vector}] | {OpenLayers.Geometry.*} | Array[{OpenLayers.Geometry.*}]
*
* Returns:
* {OpenLayers.Bounds}
*/
OpenLayers.Geometry.getBounds = function(features) {
    if (features instanceof Array && features.length > 0 && features[0].CLASS_NAME && features[0].CLASS_NAME.substr(0, OpenLayers.Feature.Vector.prototype.CLASS_NAME.length) == OpenLayers.Feature.Vector.prototype.CLASS_NAME) {
        var resultBounds = new OpenLayers.Bounds();
        for (var i = 0; i < features.length; i++) {
            if (features[i].geometry) {
                resultBounds.extend(features[i].geometry.getBounds());
            }
        }
        return resultBounds;

    } else if (features.CLASS_NAME && features.CLASS_NAME.substr(0, OpenLayers.Feature.Vector.prototype.CLASS_NAME.length) == OpenLayers.Feature.Vector.prototype.CLASS_NAME) {
        return OpenLayers.Geometry.getBounds([features]);

    } else if (features instanceof Array && features.length > 0 && features[0].CLASS_NAME && features[0].CLASS_NAME.substr(0, OpenLayers.Geometry.CLASS_NAME.length) == OpenLayers.Geometry.prototype.CLASS_NAME) {
        var geometries = features;
        features = [];
        for (var i = 0; i < geometries.length; i++) {
            features.push(new OpenLayers.Feature.Vector(geometries[i]));
        }
        return OpenLayers.Geometry.getBounds(features);

    } else {
        return null;

    }
}

