/**
 * Handler to draw an N sided regular ploygon on the map.  A point is drawn on mouse down,
 * a line is drawn on mouse move (representing the radius), and the polygon is drawn on mouse up.
 * Use the "sides" option to set the number of polygon sides.
 * 
 * @class
 * @requires OpenLayers/Handler/Point.js
 * @requires OpenLayers/Geometry/Polygon.js
 */
OpenLayers.Handler.RegularPolygon = OpenLayers.Class.create();
OpenLayers.Handler.RegularPolygon.prototype = 
  OpenLayers.Class.inherit(OpenLayers.Handler.Point, {
    
    /**
     * @type OpenLayers.Feature.Vector
     * @private
     */
    line: null,

    /**
     * @type OpenLayers.Feature.Vector
     * @private
     */
    regularPolygon: null,

    /**
     * The number of sides the polygon will have
     * @type int
     */
    sides: 50,

    /**
     * @constructor
     *
     * @param {OpenLayers.Control} control
     * @param {Array} callbacks An object with a 'done' property whos value is
     *                          a function to be called when the path drawing is
     *                          finished. The callback should expect to recieve a
     *                          single argument, the polygon geometry.
     *                          If the callbacks object contains a 'point'
     *                          property, this function will be sent each point
     *                          as they are added.  If the callbacks object contains
     *                          a 'cancel' property, this function will be called when
     *                          the handler is deactivated while drawing.  The cancel
     *                          should expect to receive a geometry.
     * @param {Object} options
     */
    initialize: function(control, callbacks, options) {
        OpenLayers.Handler.Point.prototype.initialize.apply(this, arguments);
    },
    
    /**
     * Add temporary geometries
     */
    createFeature: function() {
        this.line = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString());
        this.point = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point());
        this.regularPolygon = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon());
    },

    /**
     * Destroy temporary geometries
     */
    destroyFeature: function() {
        this.line.destroy();
        this.point.destroy();
        this.regularPolygon.destroy();
        this.line = null;
        this.regularPolygon = null;
    },

    /**
     * Modify the existing geometry given the new point
     *
     */
    modifyFeature: function() {
        var index = this.line.geometry.components.length - 1;
        this.line.geometry.components[index].x = this.point.geometry.x;
        this.line.geometry.components[index].y = this.point.geometry.y;
    },


    /**
     * Render geometries on the temporary layer.
     */
    drawFeature: function() {
        this.layer.drawFeature(this.line,  this.style);
        this.layer.drawFeature(this.point, this.style);
    },

    /**
     * Add point to geometry.  Send the point index to override
     * the behavior of LinearRing that disregards adding duplicate points.
     */
    addPoint: function() {
        this.line.geometry.addComponent(this.point.geometry.clone(),
                                        this.line.geometry.components.length);
        this.callback("point", [this.point.geometry]);
    },

    /**
     * Return a clone of the relevant geometry.
     *
     * @type OpenLayers.Geometry.LineString
     */
    geometryClone: function() {
        return this.regularPolygon.geometry.clone();
    },

    createRegularPolygon: function() {
            var pointList = [];
        // the radius is the length of the line segment
        var radius = this.line.geometry.getLength();

        // draw the polygon, the center is the first point drawn at mousedown
        if (radius > 0) {
            var cenx = this.line.geometry.components[0].x;
            var ceny = this.line.geometry.components[0].y;
            var piNum = Math.PI * 2;
            var segments = piNum / this.sides;

            for (var a = 0; a < piNum; a += segments) {
                    var x = cenx + radius * Math.sin(a);
                    var y = ceny + radius * Math.cos(a);                 
                var newPoint = new OpenLayers.Geometry.Point(x,y);
                pointList.push(newPoint);
            }
            pointList.push(pointList[0]);
            var linearRing = new OpenLayers.Geometry.LinearRing(pointList);
            this.regularPolygon.geometry.addComponent(linearRing);            
        } 
    },

    /**
     * Return the radius of the polygon
     *
     * @type int
     */
    getRadius: function() {
        this.line.geometry.getLength();
    },

    /**
     * Handle mouse down.  Add the starting (center) point of the polygon and render it
     * Return determines whether to propagate the event on the map.
     *
     * @param {Event} evt
     * @type Boolean
     */
    mousedown: function(evt) {
        if(this.line == null) {
            this.createFeature();
        }
        this.mouseDown = true;
        var lonlat = this.control.map.getLonLatFromPixel(evt.xy);
        this.point.geometry.x = lonlat.lon;
        this.point.geometry.y = lonlat.lat;
        this.addPoint();
        this.drawFeature();
        return false;
    },

    /**
     * Handle mouse move.  Adjust the geometry and redraw the line.
     * Return determines whether to propagate the event on the map.
     *
     * @param {Event} evt
     * @type Boolean
     */
    mousemove: function (evt) {
        if ((this.line == null) || (this.line.geometry == null))
           return true;

        if(this.line.geometry.components.length > 0) {
            var lonlat = this.map.getLonLatFromPixel(evt.xy);
            if (this.line.geometry.components.length == 1) {
                this.point = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point());
                this.addPoint();
            }
            this.point.geometry.x = lonlat.lon;
            this.point.geometry.y = lonlat.lat;
            this.modifyFeature();
            this.drawFeature();
        }
        return false;
    },

    /**
     * Handle mouse up.  Send the last point in the geometry to the control. If we
     * have a valid line segment (radius), then draw the polygon.
     * Return determines whether to propagate the event on the map.
     *
     * @param {Event} evt
     * @type Boolean
     */
    mouseup: function (evt) {
        this.mouseDown = false;
        if (this.line.geometry.components.length == 2) {
            this.createRegularPolygon();
        }
        this.point.destroy();
        this.line.destroy();
        this.finalize();
        return false;
     },

    /** @final @type String */
    CLASS_NAME: "OpenLayers.Handler.RegularPolygon"
});