﻿var map = null;
var _pinInfobox = null;
var _measureLayer = new Microsoft.Maps.EntityCollection();
var _wellLayer = new Microsoft.Maps.EntityCollection();
var _lateralLayer = new Microsoft.Maps.EntityCollection();
var _lastMeasureTool = null;
var _currMeasureTool = null;
var _isMeasuring = false;
var _hMouseDown = null;
var _hMouseMove = null;
var _hMouseUp = null;
var _zoneColorHash = new Hash();
var _zoneLayerHash = new Hash();
var _zoneLayerVisibleHash = new Hash();
var _baseUrl;
var _dataUrl;
var _zoneUrl;
var _pushpinUrl;

$('#measureTool').click(
function (event)
{
    _isMeasuring = !_isMeasuring;
    enableMeasuring(_isMeasuring);
});

function enableMeasuring(state)
{
    _isMeasuring = state;

    if (state)
    {
        map.setOptions({ disablePanning: true });

        _hMouseDown = Microsoft.Maps.Events.addHandler(map, 'mousedown', onMeasureBegin);
        _hMouseMove = Microsoft.Maps.Events.addHandler(map, 'mousemove', onMeasureMove);
        _hMouseUp = Microsoft.Maps.Events.addHandler(map, 'mouseup', onMeasureEnd);

        map.getRootElement().style.cursor = "crosshair";
    }
    else
    {
        _measureLayer.clear();
        // Disable panning while using measure tool
        map.setOptions({ disablePanning: false });

        if (_hMouseDown != null)
        {
            Microsoft.Maps.Events.removeHandler(_hMouseDown);
        }

        if (_hMouseMove != null)
        {
            Microsoft.Maps.Events.removeHandler(_hMouseMove);
        }

        if (_hMouseUp != null)
        {
            Microsoft.Maps.Events.removeHandler(_hMouseUp);
        }

        map.getRootElement().style.cursor = "default";
    }
}

function loadMap(baseUrl, dataUrl, zoneUrl, pushpinUrl)
{
    _baseUrl = baseUrl;

    getMap();

    _wellLayer.setOptions({ zIndex: 999 });
    map.setView({ animate: false, center: new Microsoft.Maps.Location(30.092550, -96.825270), zoom: 12 });

    _dataUrl = dataUrl;
    _zoneUrl = zoneUrl;
    _pushpinUrl = pushpinUrl;

    setTimeout(postLoad, 5000);
}

function postLoad()
{
    var baseUrl = _baseUrl;
    var dataUrl = _dataUrl;
    var zoneUrl = _zoneUrl;
    var pushpinUrl = _pushpinUrl;

    $.getJSON(zoneUrl, null, function (data)
    {
        $.each(data, function (i, zone)
        {
            var argb = zone.ColorARGB.split(",");
            _zoneColorHash.setItem(zone.TypeCode, new Microsoft.Maps.Color(parseInt(argb[0]), parseInt(argb[1]), parseInt(argb[2]), parseInt(argb[3])));
        });
    });

    $.getJSON(dataUrl, null, function (data)
    {
        var lastLocation = null;


        $.each(data, function (i, well)
        {
            var pin = new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(well.WellLocationLat, well.WellLocationLon),
                                                    { icon: pushpinUrl + "/pin?pinText=" + escape(well.FacilityName),
                                                        height: 23, width: well.FacilityName.length * 10,
                                                        anchor: new Microsoft.Maps.Point(10, 23),
                                                        draggable: false
                                                    });

            pin.wellName = well.FacilityName;
            pin.wellId = well.WellID;

            Microsoft.Maps.Events.addHandler(pin, 'click', pinDoubleClick);

            _wellLayer.push(pin);

            if (well.Completions != null)
            {
                $.each(well.Completions, function (j, completion)
                {
                    var thickness = 3;
                    var color = null;

                    if (_zoneColorHash.hasItem(completion.Zone))
                        color = _zoneColorHash.getItem(completion.Zone);
                    else
                        color = new Microsoft.Maps.Color(255, 116, 191, 82);

                    var pl = new Microsoft.Maps.Polyline([new Microsoft.Maps.Location(well.WellLocationLat, well.WellLocationLon),
                                                                new Microsoft.Maps.Location(completion.BottomHoleLocationLat, completion.BottomHoleLocationLon)], { strokeColor: color, strokeThickness: parseInt(thickness) });

                    if (_zoneLayerHash.hasItem(completion.Zone))
                        _zoneLayerHash.getItem(completion.Zone).push(pl);
                    else
                    {
                        _zoneLayerHash.setItem(completion.Zone, new Microsoft.Maps.EntityCollection());
                        _zoneLayerHash.getItem(completion.Zone).push(pl);
                    }
                });
            }
        });
    });

    map.entities.push(_wellLayer);

    for (var i in _zoneLayerHash.items)
    {
        map.entities.push(_zoneLayerHash.items[i]);
    }


    handleSelectiveVisibility();
}

function handleSelectiveVisibility()
{

    var i;
    var bounds = map.getBounds();
    var zoomLevel = map.getZoom();

    // Handle lateral layer visibility
    for (var zoneLayer in _zoneLayerHash.items)
    {
        var layerEntities = _zoneLayerHash.items[zoneLayer];
        var visible = true;

        if (_zoneLayerVisibleHash.hasItem(zoneLayer))
        {
            visible = _zoneLayerVisibleHash.getItem(zoneLayer);
        }

        layerEntities.setOptions({ visible: (zoomLevel >= 13) && visible, zIndex: 1 });

        if (zoomLevel >= 13)
        {
            for (i = 0; i < layerEntities.getLength(); i++)
            {
                var o = layerEntities.get(i);

                // Handles poly lines
                if (o.getLocations != null)
                {
                    var locations = o.getLocations();
                    o.setOptions({ visible: (bounds.contains(locations[0]) || bounds.contains(locations[1])) && visible });
                }
            }
        }
    }

    // Handle well layer visibility
    for (i = 0; i < _wellLayer.getLength(); i++)
    {
        var o = _wellLayer.get(i);

        //Handles push pins
        if (o.getLocation != null)
            o.setOptions({ visible: bounds.contains(o.getLocation()) });
    }
}

function setLateralLayerVisibility(layerName, state)
{
    if (_zoneLayerHash.hasItem(layerName))
    {
        _zoneLayerVisibleHash.setItem(layerName, state);
        _zoneLayerHash.getItem(layerName).setOptions({ visible: state });
    }

    handleSelectiveVisibility();
}

function getMap()
{
    //Aiikt9GA_UhBdPSglwhNhw7HDy4fhJWW914nHrQBtNeAL9vbIAPpSryhzVYqqGgT
    map = new Microsoft.Maps.Map(document.getElementById('mapView'),

                    { credentials: 'ArMqqR4wThOov2Sy7HgTOXBQk3Jn7iDF50GHVqvkYt_tN6xtJQl8hw_nqBm8v6Rz',
                        showCopyright: false, enableSearchLogo: false, showLogo: false, customizeOverlays: false, useInertia: false, tileBuffer: 4
                    });


    Microsoft.Maps.Events.addThrottledHandler(map, 'viewchangeend', onViewChangeEnd, 1000);

    _measureLayer.setOptions({ zIndex: 9999 });
    map.entities.push(_measureLayer);
}

function onViewChangeEnd()
{
    enableMeasuring(false);
    async(handleSelectiveVisibility);
}

function onMeasureBegin(e)
{
    if (!_isMeasuring) return;

    if (e.targetType == "map")
    {
        var point = new Microsoft.Maps.Point(e.getX(), e.getY());
        var loc = e.target.tryPixelToLocation(point);

        prepareMeasureTool(loc);
    }
    else if (e.targetType == "pushpin")
    {
        var loc = e.target.getLocation();

        prepareMeasureTool(loc);
    }
    else if (e.targetType == "polyline")
    {
        var locations = e.target.getLocations();
        var point = new Microsoft.Maps.Point(e.pageX, e.pageY);
        var polyVertices = map.tryLocationToPixel(locations, Microsoft.Maps.PixelReference.page);
        var loc = null;

        if (distanceXY(point.x, point.y, polyVertices[0].x, polyVertices[0].y) <
            distanceXY(point.x, point.y, polyVertices[1].x, polyVertices[1].y))
        {
            prepareMeasureTool(new Microsoft.Maps.Location(locations[0].latitude, locations[0].longitude));
        }
        else
        {
            prepareMeasureTool(new Microsoft.Maps.Location(locations[1].latitude, locations[1].longitude));
        }
    }

    e.handled = true;
}

function onMeasureMove(e)
{
    if (!_isMeasuring) return;
    if (_currMeasureTool == null) return;

    e.Handled = true;

    if (e.targetType == "map")
    {
        var point = new Microsoft.Maps.Point(e.getX(), e.getY());
        var loc = e.target.tryPixelToLocation(point);

        if (_isMeasuring)
        {
            map.getRootElement().style.cursor = "crosshair";
        }
        else
            map.getRootElement().style.cursor = "";

        if (loc != null && _currMeasureTool != null)
        {
            var location = _currMeasureTool.getLocations();
            _currMeasureTool.setLocations([location[0], new Microsoft.Maps.Location(loc.latitude, loc.longitude)]);
        }
    }
    else if (e.targetType == "pushpin")
    {
        var loc = e.target.getLocation();

        if (loc != null && _currMeasureTool != null)
        {
            var location = _currMeasureTool.getLocations();
            _currMeasureTool.setLocations([location[0], new Microsoft.Maps.Location(loc.latitude, loc.longitude)]);
        }
    }
    else if (e.targetType == "polyline")
    {
        var location = _currMeasureTool.getLocations();
        var locations = e.target.getLocations();
        var point = new Microsoft.Maps.Point(e.pageX, e.pageY);
        var polyVertices = map.tryLocationToPixel(locations, Microsoft.Maps.PixelReference.page);
        var loc = null;

        if (distanceXY(point.x, point.y, polyVertices[0].x, polyVertices[0].y) <
            distanceXY(point.x, point.y, polyVertices[1].x, polyVertices[1].y))
        {
            _currMeasureTool.setLocations([location[0], new Microsoft.Maps.Location(locations[0].latitude, locations[0].longitude)]);
        }
        else
        {
            _currMeasureTool.setLocations([location[0], new Microsoft.Maps.Location(locations[1].latitude, locations[1].longitude)]);
        }
    }
}

function onMeasureEnd(e)
{
    if (!_isMeasuring) return;

    _lastMeasureTool = _currMeasureTool;
    _currMeasureTool = null;

    e.handled = true;

    if (_lastMeasureTool != null)
    {
        var location = _lastMeasureTool.getLocations();
        var distanceFt = distanceLatLon(location[0].latitude, location[0].longitude, location[1].latitude, location[1].longitude).toFixed(2);
        var distanceMi = (distanceLatLon(location[0].latitude, location[0].longitude, location[1].latitude, location[1].longitude) / 5280).toFixed(2);

        var info = { title: 'Distance',
            description: distanceFt.toString() + " ft (" + distanceMi + ' mi)',
            width: 200,
            height: 75,
            showCloseButton: false,
            zIndex: 9999
        };

        var lastInfoBox = new Microsoft.Maps.Infobox(location[1], info);
        _measureLayer.push(lastInfoBox);
    }
}

function prepareMeasureTool(location)
{
    _measureLayer.clear();

    if (_currMeasureTool == null)
    {
        if (location != null)
        {
            var thickness = 3;
            var opts = { strokeColor: new Microsoft.Maps.Color(255, 0, 255, 0), strokeThickness: parseInt(thickness), strokeDashArray: "3 3" };
            _currMeasureTool = new Microsoft.Maps.Polyline([new Microsoft.Maps.Location(location.latitude, location.longitude),
                                                                    new Microsoft.Maps.Location(location.latitude, location.longitude)], opts);

            _measureLayer.push(_currMeasureTool);
        }
    }
}

function displayInfobox(e)
{
    // make sure we clear any infoBox timer that may still be active
    stopInfoboxTimer(e);

    // build or display the infoBox
    var pin = e.target;
    if (pin != null)
    {
        // Create the info box for the pushpin
        var location = pin.getLocation();
        var options = {
            id: 'infoBox1',
            title: pin.wellName,
            description: 'Enervest Operating',
            htmlContent: '',
            height: 75,
            width: 300,
            visible: true,
            showPointer: true,
            showCloseButton: false,
            // offset the infobox enough to keep it from overlapping the pin.
            offset: new Microsoft.Maps.Point(0, pin.getHeight()),
            zIndex: 999
        };
        // destroy the existing infobox, if any
        // In testing, I discovered not doing this results in the mouseleave
        // and mouseenter events not working after hiding and then reshowing the infobox.
        if (_pinInfobox != null)
        {
            map.entities.remove(_pinInfobox);
            if (Microsoft.Maps.Events.hasHandler(_pinInfobox, 'mouseleave'))
                Microsoft.Maps.Events.removeHandler(_pinInfobox.mouseLeaveHandler);
            if (Microsoft.Maps.Events.hasHandler(_pinInfobox, 'mouseenter'))
                Microsoft.Maps.Events.removeHandler(_pinInfobox.mouseEnterHandler);
            _pinInfobox = null;
        }
        // create the infobox
        _pinInfobox = new Microsoft.Maps.Infobox(location, options);
        // hide infobox on mouseleave
        _pinInfobox.mouseLeaveHandler
                    = Microsoft.Maps.Events.addHandler(_pinInfobox, 'mouseleave', _pinInfoboxMouseLeave);
        // stop the infobox hide timer on mouseenter
        _pinInfobox.mouseEnterHandler
                    = Microsoft.Maps.Events.addHandler(_pinInfobox, 'mouseenter', _pinInfoboxMouseEnter);
        // add it to the map.
        map.entities.push(_pinInfobox);
    }
}

function hideInfobox(e)
{
    if (_pinInfobox != null)
        _pinInfobox.setOptions({ visible: false });
}

// This function starts a count-down timer that will hide the infoBox when it fires.
// This gives the user time to move the mouse over the infoBox, which disables the timer
// before it can fire, thus allowing clickable content in the infobox.
function startInfoboxTimer(e)
{
    // start a count-down timer to hide the popup.
    // This gives the user time to mouse-over the popup to keep it open for clickable-content.
    if (_pinInfobox.pinTimer != null)
    {
        clearTimeout(_pinInfobox.pinTimer);
    }
    // give 300ms to get over the popup or it will disappear
    _pinInfobox.pinTimer = setTimeout(timerTriggered, 300);
}

// Clear the infoBox timer, if set, to keep it from firing.
function stopInfoboxTimer(e)
{
    if (_pinInfobox != null && _pinInfobox.pinTimer != null)
    {
        clearTimeout(_pinInfobox.pinTimer);
    }
}

function pinDoubleClick(e)
{
    $("#dialog-modal").html("").dialog({
        height: 500,
        width: 600,
        resizable: false,
        title: e.target.wellName,
        modal: true,
        buttons: [{ text: "Ok", click: function () { $(this).dialog("close"); } },
                        { text: "Cancel", click: function () { $(this).dialog("close"); } }]
    }).load(_baseUrl + "/Map/GetWellDetails/" + e.target.wellId, function () { $("#dialog-modal").dialog("open"); });
}

function pinMouseOver(e)
{
    if (_isMeasuring) return;

    displayInfobox(e);
}
function pinMouseOut(e)
{
    if (_isMeasuring) return;
    startInfoboxTimer(e);
}
function _pinInfoboxMouseLeave(e)
{
    if (_isMeasuring) return;

    hideInfobox(e);
}
function _pinInfoboxMouseEnter(e)
{
    if (_isMeasuring) return;

    stopInfoboxTimer(e);
}
function timerTriggered(e)
{
    if (_isMeasuring) return;

    hideInfobox(e);
}


function distanceLatLon(prevLat, prevLong, currLat, currLong)
{
    var degreesToRadians = Math.PI / 180;
    var earthRadius = 6371; // approximation in kilometers assuming earth to be spherical

    var previousRadianLat = prevLat * degreesToRadians;
    var previousRadianLong = prevLong * degreesToRadians;
    var currentRadianLat = currLat * degreesToRadians;
    var currentRadianLong = currLong * degreesToRadians;

    var latitudeRadianDelta = currentRadianLat - previousRadianLat;
    var longitudeRadianDelta = currentRadianLong - previousRadianLong;

    var expr1 = (Math.sin(latitudeRadianDelta / 2) * Math.sin(latitudeRadianDelta / 2)) +
                    (Math.cos(previousRadianLat) * Math.cos(currentRadianLat) * Math.sin(longitudeRadianDelta / 2) * Math.sin(longitudeRadianDelta / 2));
    var expr2 = 2 * Math.atan2(Math.sqrt(expr1), Math.sqrt(1 - expr1));
    var distanceValue = earthRadius * expr2;

    return (distanceValue / 1.61) * 5280;
}

function distanceXY(x0, y0, x1, y1)
{
    return Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
}

function async(fn)
{
    fn();
    //setTimeout(fn, 300);
}


