// TODO: convert to class
(function($) {
    $.placeDrawer = function(options) {
        var opts = $.extend({
            lineColor: "#000000",
            lineWeight: 4,
            lineOpacity: 0.6,
            fillColor: "#E6E6FA",
            fillOpacity: 0.4,
            polyline: {
                weight: 1,
                zoomFactor: 18,
                numLevels: 4,
                color: "black"
            },
            polygon: {
                fill: true,
                opacity: 0.3,
                color: "#D02A26",
                outline: true
            },
            attached: [],
            initialGetParameters: null,
            mode: 'adjust'
        }, options),
            poly = null,
            markers = [],
            polygonField = opts.form.find("#id_geopoly"),
            places = [],
            placesIDs = [];

        var icon = new GIcon();
        icon.image = opts.iconUrl + "positioning_marker.png";
        icon.shadow = opts.iconUrl + "positioning_marker_shadow.png";
        icon.iconSize = new GSize(12,20);
        icon.shadowSize = new GSize(22,20);
        icon.iconAnchor = new GPoint(6,20);
        icon.infoWindowAnchor = new GPoint(5,1);

        function createMarker(point, mode) {
            var marker=new GMarker(point,{
                icon: icon,
                draggable: true,
                bouncy: false,
                dragCrossMove: true
            });
            opts.map.addOverlay(marker);
            insertMarker(marker, mode);
            GEvent.addListener(marker, "drag", drawOverlay);
            GEvent.addListener(marker, "dblclick", function() {deleteMarker(marker)});
            if (markers.length == 3) {
                $(".modes").show();
            }
        }

        function insertMarker(marker, mode) {
            var mode = mode || opts.mode;
            if (mode == 'create') {
                markers.push(marker);
            } else {
                var positions = [[0, pointToSegmentDistance(markers[0].getLatLng(), markers[markers.length - 1].getLatLng(), marker.getLatLng())]];
                $.each(markers, function(i, m){
                    if (i < markers.length - 1) {
                        positions.push([i + 1, pointToSegmentDistance(markers[i].getLatLng(), markers[i+1].getLatLng(), marker.getLatLng())]);
                    }
                });
                positions.sort(function(a, b){return a[1] - b[1];});
                markers.splice(positions[0][0], 0, marker);
            }
        }

        function deleteMarker(marker) {
            $.each(markers, function(index, marker_){
                if (marker === marker_) {
                    markers.splice(index, 1);
                    opts.map.removeOverlay(marker);
                    drawOverlay();
                    return false;
                }
            });
        }

        function drawOverlay(){
            if(poly) {
                opts.map.removeOverlay(poly);
            };
            var points = $.map(markers, function(m){ return m.getLatLng(); });
            points.push(markers[0].getLatLng()); //closes poly

            poly = new GPolygon(
                points,
                opts.lineColor,
                opts.lineWeight,
                opts.lineOpacity,
                opts.fillColor,
                opts.fillOpacity
            );
            opts.map.addOverlay(poly);
            updateInputs();
        }

        function updateInputs() {
            var geojson = JSON.stringify({
                type: "Polygon",
                coordinates: [
                    $.map(markers, function(m) {
                        return [[m.getLatLng().lng(), m.getLatLng().lat()]];
                    }).concat([[markers[0].getLatLng().lng(), markers[0].getLatLng().lat()]])
                ]
            });

            var bounds = new GLatLngBounds();
            $.each(markers, function() {bounds.extend(this.getLatLng()); });

            polygonField.attr("value", geojson);
        }

        function clear() {
            opts.mode = 'create';
            $(".modes").hide();
            $.each(markers, function() {opts.map.removeOverlay(this)});
            if(poly) {
                opts.map.removeOverlay(poly);
            };
            markers.length = 0;
            polygonField.attr("value", "");
        }

        function init() {
            GEvent.addListener(opts.map, "click", function(overlay, latlng, overlaylatlng) {
                // we go through all markers here because map doesn't have double click event
                // thus, double click on marker sends two 'click' events to map.

                var markerClick = false;
                $.each(markers, function() {
                    if (overlay === this) {
                        markerClick = true;
                        return false;
                    }
                });
                if (markerClick) return;
                createMarker(latlng || overlaylatlng, 'add');
                drawOverlay();
            });

            opts.clearMapLink.click(function() {clear(); return false;});


            try {
                var initial_value = JSON.parse(polygonField.val()),
                    points = initial_value.coordinates[0],
                    bounds = new GLatLngBounds(),
                    point = null;

                points.pop();


                $.each(points, function(index, point) {
                    point = new GLatLng(point[1], point[0]);
                    createMarker(point, 'create');
                    bounds.extend(point);
                });

                opts.map.setCenter(bounds.getCenter(), opts.map.getBoundsZoomLevel(bounds));
                drawOverlay();
            } catch (x) { clear(); }
        }

        init();
        return {
            opts: opts,
            setPoly: function(poly){
                clear();

                for (var i=0; i<poly.getVertexCount(); i++) {
                    createMarker(poly.getVertex(i), 'create');
                }

                drawOverlay();
            }
        }
    };

    function pointToSegmentDistance(p1, p2, p3) {
        var xDelta = p2.lng() - p1.lng(),
            yDelta = p2.lat() - p1.lat(),
            u = ((p3.lng() - p1.lng()) * xDelta + (p3.lat() - p1.lat()) * yDelta) / (xDelta * xDelta + yDelta * yDelta);

        if (u < 0) {
            return p3.distanceFrom(p1);
        } else if (u > 1) {
            return p3.distanceFrom(p2);
        } else {
            return p3.distanceFrom(new GLatLng(p1.lat() + u * yDelta, p1.lng() + u * xDelta));
        }
    }

})(jQuery);

