/*
* Class makes routing throgh different draw situations
*/

L.Control.DrawRoute = L.Control.extend({

    routeOptions: {
        routeContainer: 'routeContainer'
    },

    // features group layer
    drawnItems: null,

    // features group layers separately
    drawnLayers: [],

    wayPoints: [],

    initialize: function () {

        // drawnItems
        this.drawnItems = new L.FeatureGroup();

        // extend options
        var options = this.routeOptions;

        // base constructor
        var ret = L.Control.prototype.initialize.call(this, options);

        L.Control.DrawRoute.Singleton.routeControl = this;

        // create select container, if it is in parameters and exists
        if (options.routeContainer != null && $('#' + options.routeContainer).length > 0) {

            // selected items
            this.$container = $('<div class="dataContainer"></div>');

            // buttons container
            this.$buttonsContainer =
                $(['<div class="btn-group btn-group-sm" role="group">',
                        '<button type="button" class="btn btn-default zoomResults">Zoom All</button>',
                        '<button type="button" class="btn btn-default clearResults">Clear</button>',
                        '<button type="button" class="btn btn-default printRoute">Print Route</button>',
                        '</div>'].join(''));

            // select container
            $('#' + options.routeContainer).append(this.$buttonsContainer).append(this.$container);

            // zoom results click
            $('.zoomResults', this.$buttonsContainer).click(function() {this.zoomResults.call(this)});
            $('.clearResults', this.$buttonsContainer).click(function() {this.clearResults.call(this)});
            $('.printRoute', this.$buttonsContainer).click(function() {this.printRoute.call(this)});

            // create table, this will render select results in table
            if (L.RoutingTable != null) {

                this._table = new L.RoutingTable(this);
                this._table.draw();
            }
        };

        return ret;
    },

    onAdd: function (map) {
        // call base method
        var container = L.Control.Draw.prototype.onAdd.call(this, map);

        var me = this;

        // add drawn items to the map
        map.addLayer(this.drawnItems);

        return container;
    },

    _drawnHandler: function (e) {
        var layer = e.layer;

        // call drawn function
        this.drawn(layer);

        // send drawn event with geometry parameter
        this._map.fire('drawRoute:drawn', e);
    },

    addPoint: function (wayPoint) {
        this.wayPoints.push(wayPoint);
        
        if (wayPoint.polyline) {
            this._drawn(wayPoint.polyline);
        }

        if (wayPoint.latLng)
        {
            wayPoint.marker = new L.WayPointMarker(wayPoint);
            this._drawn(wayPoint.marker);
        }
    },

    _drawn: function (layer) {

        var me = this;
        // add to the map
        if (layer) {
            this.drawnItems.addLayer(layer);
        }

        // prepare output sets and working set me.drawnLayers
        this._setOutputs();

        // remove handlers
        _.forEach(this.drawnLayers, function (l, i) {
            l.off('mouseover mouseout click dblclick, remove');
            if (L.WayPointMarker.prototype.isPrototypeOf(l)) {
                l.on(
                {
                    mouseover: function () {
                        me.waypointOver(me.wayPoints.indexOf(l.wayPoint));
                    },
                    mouseout: function () {
                        me.waypointOut(me.wayPoints.indexOf(l.wayPoint));
                    },
                    click: function () {
                        me.waypointClick(me.wayPoints.indexOf(l.wayPoint));
                    },
                    dblclick: function () {
                        me.featureDblClick(me.wayPoints.indexOf(l.wayPoint));
                    },
                    remove: function () {
                        me.waypointOut(me.wayPoints.indexOf(l.wayPoint));
                    }
                });
            } else {
                l.on(
                {
                    mouseover: function () {
                        me.polylineOver(me.wayPoints.indexOf(l.wayPoint));
                    },
                    mouseout: function () {
                        me.polylineOut(me.wayPoints.indexOf(l.wayPoint));
                    },
                    click: function () {
                        me.polylineClick(me.wayPoints.indexOf(l.wayPoint));
                    },
                    dblclick: function () {
                        me.featureDblClick(me.wayPoints.indexOf(l.wayPoint));
                    },
                    remove: function () {
                        me.polylineOut(me.wayPoints.indexOf(l.wayPoint));
                    }
                });
            }
        });

        // if table drawn
        if (this._table) {
            this._table.draw();
        }
    },

    // prepares output sets and working set this.drawnLayers
    _setOutputs: function () {
        // ==============
        var layers = [];
        this.drawnLayers = layers;

        // ============== fill segments array
        this.drawnItems.eachLayer(
            function (lyr) {
                layers.push(lyr);
            }
        );
    },

    _selected: function (e) {

        // send drawn event with geometry parameter
        this._map.fire('drawRoute:selected', e);
    },

    onRemove: function (map) {

        // on remove base
        L.Control.Draw.prototype.onRemove.call(this, map);
    },

    polylineOver: function (index) {
        index = index * 1;

        // get index of vector
        if (this._table) this._table.highlightPolyline(index);

        var draw = this;
        if (draw._map) {
            var wp = draw.wayPoints[index];

            if (wp && wp.polyline) {
                // get drawn feature vector
                var feat = wp.polyline;
                draw._highlightVector(wp.polyline);
            }
        }
    },

    waypointOver: function (index) {
        index = index * 1;

        // get index of vector
        if (this._table) this._table.highlightWaypoint(index);

        var draw = this;
        if (draw._map) {
            var wp = draw.wayPoints[index];

            if (wp && wp.marker) {
                draw._highlightVector(wp.marker)
            }
        }
    },

    _highlightVector: function (vector) {

        if (!vector._prevColor) {
            vector._prevColor = L.MSUtils.getCurrentColor(vector);
        }

        // for markers we don't have setStyle
        if (vector.setStyle) {
            vector.setStyle({ color: 'green' });
        }
    },

    polylineOut: function (index) {
        index = index * 1;

        if (this._table) this._table.deHighlightPolyline(index);

        var draw = this;
        if (draw._map) {
            var wp = draw.wayPoints[index];

            if (wp && wp.polyline) {
                var feat = wp.polyline;

                // for markers we don't have setStyle
                if (feat.setStyle) {
                    feat.setStyle({ color: feat._prevColor });
                }
            }
        }
    },

    waypointOut: function (index) {
        index = index * 1;

        if (this._table) this._table.deHighlightWaypoint(index);

        var draw = this;
        if (draw._map) {
            var wp = draw.wayPoints[index];

            if (wp && wp.marker) {
                var feat = wp.marker;

                // for markers we don't have setStyle
                if (feat.setStyle) {
                    feat.setStyle({ color: feat._prevColor });
                }
            }
        }
    },

    polylineClick: function (index) {
        index = index * 1;

        // get index of vector
        if (this._table) {
            this._table.scrollHighlightPolyline(index);
        };
    },

    waypointClick: function (index) {
        index = index * 1;
        // get index of vector
        if (this._table) {
            this._table.scrollHighlightWaypoint(index);
        };
    },

    featureDblClick: function (index) {

    },

    zoomToWp: function (index) {
        // get layer
        if (this._map) {
            var wp = this.wayPoints[index * 1];

            var layer = wp.marker;

            if (layer) {
                // get center
                var center = layer.getLatLng();
                this._map.panTo(center);

                var tempMarker = new L.WayPointMarker(layer.wayPoint, true);
                tempMarker.addTo(this._map);
            }
        }
    },

    zoomToPolyline: function (index) {
        // get layer
        if (this._map) {

            var wp = this.wayPoints[index * 1];

            // get selected feature vector
            var layer = wp.polyline;

            if (layer) {
                // get center
                var bounds = layer.getBounds();
                var center = bounds.getCenter();
                
                this._map.panTo(center);

                this._zoomBounds(bounds);
            }
        }
    },

    zoomResults: function () {
        // sometimes we removed layer but callback came and called zoomResults	
        if (this._map == null) return;

        // if active, do zoom, otherwise skip. other active layer will do zoom itself
        if (this.drawnLayers.length > 0)
        // this handler is global, it will trigger for each select layer, but we want only once
        {
            var bounds = new L.LatLngBounds();

            // iterate throgh all select controls
            for (var j = 0; j < this.drawnLayers.length; j++) {

                // extend bounds
                var v = this.drawnLayers[j];
                if (v.getBounds !== undefined) {
                    bounds.extend(v.getBounds());
                }
                else {
                    bounds.extend(v.getLatLng());
                }
            }

            // we got the bounds, from all select layers

            // increase bounds
            this._zoomBounds(bounds);
        }
    },

    _zoomBounds: function (bounds) {
        if (bounds != null) {

            // make bounds not less then 150 meters. at least we need to see 150 meters, otherwise it is too close
            var c = bounds.getCenter();
            var latLngR = L.Util.metersToLatLng(c, 50);

            // make 150 meters bounds
            var metersBounds = new L.LatLngBounds(new L.LatLng(c.lat - latLngR[0], c.lng + latLngR[1]), new L.LatLng(c.lat + latLngR[0], c.lng - latLngR[1]));

            // extend current bounds with 150 meters bounds
            bounds.extend(metersBounds);

            this._map.fitBounds(bounds);
        }
    },

    clearResults: function () {
        // redraw
        var me = this;

        // remove features from map (feature groups)
        _.forEach(this.drawnLayers, function (x) { me.drawnItems.removeLayer(x); });
        me.wayPoints = [];

        this._setOutputs();

        // it is bad variant the whole table redraw, we do remove one row only, which is done by code who, calls this function.
        if (this._table) {
            //  redraw table
            this._table.draw();
        }
    },

    printRoute: function () {
        var me = this;
        mapRouter.StoreRoute(me.wayPoints);
        var win = window.open(HttpQuery.path() + "#/route", '_blank');
        win.focus();
    }
});


L.Control.DrawRoute.Singleton = {

    // draw control 
    routeControl: null,

    clearResults: function () {
        if (this.routeControl) {
            this.routeControl.clearResults();
        }
    }
};

