﻿
Type.registerNamespace("GeomaticTechnologies.GMU");

GeomaticTechnologies.GMU.Map = function(service, mapArgs) {
    /// <summary>
    ///   The VE Map.
    ///   Supports load on demand
    /// </summary>
    /// <param name="service">The webservice to call for the map data.</param>
    /// <param name="mapArgs">The map initalisation data.</param>

    this._service = service;
    this._mapArgs = mapArgs;
    
    this._map = null;
    this._pinID = 0;
    this._zoomlevel = 0;
    this._boreLayer = null;
    
    //popup specific
    this._PopupPrefix = "POPUP";
    this._currentpin = null;
    this._currentindex = 0;   
    
    this.GetPinDataDelegate = null;
    this.PinHoverDelegate = null;
    this.EndZoomDelegate = null;
    
    this._isIni=false;
    this._init();
}

GeomaticTechnologies.GMU.Map.prototype = {

    _init: function() {

        this._map = new VEMap(this._mapArgs.DivID);
        this._map.LoadMap(this._mapArgs.Center, this._mapArgs.Zoomlevel, this._mapArgs.Style, this._mapArgs.Fixed, this._mapArgs.Mode, false, 1);
        this._map.SetScaleBarDistanceUnit(this._mapArgs.Scale);

        this._boreLayer = new VEShapeLayer();
        this._map.AddShapeLayer(this._boreLayer);
        this._boreLayer.Hide();
        //setup the function to get new data whenever the map changes
        this.GetPinDataDelegate = Function.createDelegate(this, this._GetPinData);
        //this._map.AttachEvent("onchangeview", this.GetPinDataDelegate);
        //turn off the standard popup and attach our custom handler
        this.PinHoverDelegate = Function.createDelegate(this, this._PinActivate);
        this._map.AttachEvent("onmouseover", this.PinHoverDelegate);
        //this._map.AttachEvent("onclick", this.PinHoverDelegate);
        this.EndZoomDelegate = Function.createDelegate(this, this._onEndZoom);
        this._map.AttachEvent("onendzoom", this.EndZoomDelegate);

        //Setup additional storage for shapes
        VEShape.prototype.Bounds = "";
        //get the data for the default view
        //this._GetPinData();
        this.GetMap = function() {
            return this._map;
        }

        this.GetBoreLayer = function() {
            return this._boreLayer;
        }

        // Commented out the default AttachEvent and _GetPinData calls.
        // Using this, the bore data is only retrieved when the layer is on.
        this.ToggleBoreLayer = function(show) {
            if (show) {
                this._map.AttachEvent("onchangeview", this.GetPinDataDelegate);
                this._GetPinData();
                this._boreLayer.Show();
            }
            else {
                this._map.DetachEvent("onchangeview", this.GetPinDataDelegate);
                this._boreLayer.Hide();
            }
        }
    },

    _onEndZoom: function(e) {
        this._showHideIconLabel(e.zoomLevel);
    },

    _showHideIconLabel: function(zoomLevel) {

        if (zoomLevel > 10) {
            this._changecss('.PinIconLabel', 'display', 'block');
        }
        else {
            this._changecss('.PinIconLabel', 'display', 'none');
        }
    },

    _changecss: function(selector, stylename, value) {
        var ss, rules;

        // make sure there is a stylesheet available
        if (document.styleSheets.length <= 0) {
            if (document.createStyleSheet) {
                document.createStyleSheet();
            } else {
                var styleTag = document.createElement("style");
                styleTag.type = "text/css";
                document.getElementsByTagName("head")[0].appendChild(styleTag);
            }
        }

        // convert the stylename to camel case if necessary
        ccstylename = (stylename || "").replace(/\-(\w)/g, function(m, c) { return (c.toUpperCase()); });
        ccstylename = (ccstylename == 'float') ? 'cssFloat' : ccstylename;
        selector = selector.toLowerCase();

        // loop to delete or change the css
        for (var i = 0; i < document.styleSheets.length; i++) {
            ss = document.styleSheets[i];
            try {
                rules = ss.cssRules || ss.rules;
            }
            catch (e) {
                rules = []
            }
            for (var j = 0, len = rules.length; j < len; j++) {
                if (rules[j].selectorText && rules[j].selectorText.toLowerCase() == selector) {
                    if (value != null) {
                        rules[j].style[ccstylename] = value;
                    } else {
                        if (ss.deleteRule) {
                            ss.deleteRule(j);
                        } else if (ss.removeRule) {
                            ss.removeRule(j);
                        } else {
                            // poor man's delete
                            rules[j].style.cssText = "";
                        }
                    }
                    return;
                }
            }
        }

        if (stylename && value) {
            // if the selector wasn't found and isn't supposed to be deleted then add it
            ss = document.styleSheets[0];
            rules = ss.cssRules || ss.rules;
            if (ss.insertRule) {
                ss.insertRule(selector + "{" + stylename + ":" + value + "; }", rules.length);
            } else if (ss.addRule) {
                ss.addRule(selector, stylename + ":" + value + ";", rules.length);
            } else {
                throw new Error("Selector not found and add/insert rule not supported.");
            }
        }
    },

    _GetPinData: function() {
        /// <summary>
        ///   Get the latest map data from the webservice.
        /// </summary>

        //encode the current map bounds
        var points = new Array();
        var zoom;

        if (this._map.GetMapStyle() == VEMapStyle.Birdseye) {
            //set zoomlevel      
            zoom = 19;
            var be = this._map.GetBirdseyeScene();
            var rect = be.GetBoundingRectangle();
            points.push(rect.TopLeftLatLong);
            points.push(rect.BottomRightLatLong);

        } else {
            var view = this._map.GetMapView();
            points.push(view.TopLeftLatLong);
            points.push(view.BottomRightLatLong);

            //get zoomlevel
            zoom = this._map.GetZoomLevel();

        }
        var bounds = Utility.createEncodings(points);
        if (this._zoomlevel != zoom) {
            //clear existing pins
            this._boreLayer.DeleteAllShapes();
            this._zoomlevel = zoom;
        }

        //call webservice
        this._service.GetClusteredMapData(bounds, zoom, Function.createDelegate(this, this._OnMapDataSucceeded), Utility.OnFailed);
    },

    _getCustomIcon: function(hasHydrograph, isClustered, pinID) {
        var boreIcon = '';

        if (isClustered == 'True') {
            boreIcon = '<img src="Images/cluster_bore.gif"/>'
        }
        else {
            //alert(hasHydrograph);
            if (hasHydrograph == 'True') {
                boreIcon += '<img src="Images/blue_bore.gif"/>';
            }
            else {
                boreIcon += '<img src="Images/yellow_bore.gif"/>';
            }
            boreIcon += '<div class="PinIconLabel">';
            boreIcon += pinID;
            boreIcon += '</div>';
        }
        return boreIcon;
    },

    _OnMapDataSucceeded: function(results) {
        /// <summary>
        ///   Receive data for map.
        /// </summary>  
        /// <param name="result">The webservice result object - Optomised CSV string</param>

        //decode pins

        var result = results.split(",")
        var locs = Utility.decodeLine(result[0]);
        var newShapes = new Array();

        //clear existing pins
        this._boreLayer.DeleteAllShapes();
        //add new pins
        for (x = 0; x < locs.length; x++) {
            var loc = locs[x];
            var bounds = result[x * 4 + 1];
            var pinID = result[x * 4 + 2];
            var hasHydrograph = result[x * 4 + 3];
            var isClustered = result[x * 4 + 4];

            var newShape = new VEShape(VEShapeType.Pushpin, loc);
            newShape.Bounds = bounds;

            newShape.SetCustomIcon(this._getCustomIcon(hasHydrograph, isClustered, pinID));
            newShapes.push(newShape);
        }

        this._boreLayer.AddShape(newShapes);

        if (this._boreLayer.IsVisible() && !this._isIni) {
            this._boreLayer.Show();
        }
        else {
            this._boreLayer.Hide();
        }
        this._isIni = false;
    },

    _PinActivate: function(e) {
        /// <summary>
        ///   Receives any mouse of event from VE
        /// </summary>  
        /// <param name="e">The MapEvent object</param>         
        if (e.elementID) {
            var popupShape = this._map.GetShapeByID(e.elementID)
            if (popupShape) {
                //set current pin
                this._currentpin = popupShape;
                this._currentindex = 0;

                //get the content for the pin.
                this._getAJAXContent();
                this._currentpin.SetDescription("<div id='" + this._PopupPrefix + this._currentpin.GetID() + "'>Loading...</div>");
                this._currentpin.SetTitle("");
            }
        }
    },

    _getAJAXContent: function() {
        /// <summary>
        ///   Request content for popup.
        /// </summary>  

        //call the web service
        this._service.GetPushPin(this._currentpin.Bounds, this._currentindex, Function.createDelegate(this, this._OnContentSucceeded), Utility.OnFailed, this._currentpin.GetID());
    },

    _OnContentSucceeded: function(result, ID) {
        /// <summary>
        ///   Receive content for popup.
        /// </summary>  
        /// <param name="result">The webservice result object - JSON PinData</param>  
        /// <param name="ID">The popup ID associated with this call</param>  

        //verify this is the data for the current popup.
        if (ID == this._currentpin.GetID()) {
            if (this._map.GetMapMode() == VEMapMode.Mode3D) {
                //3D mode fails to be able to retrieve the div we placed earlier so resort to setting the title and description only
                //this._currentpin.SetTitle(result.Title + this._currentpin.Pins);
                //this._currentpin.SetDescription(result.Details);
                if (this._currentpin.Pins > 1 && this._currentpin.GetShapeLayer().IsExpansion) {
                    this._currentpin.SetDescription("<div id='" + this._PopupPrefix + this._currentpin.GetID() + "'>Too many (" + this._currentpin.Pins + "). Zoom in further.</div>");
                    this._currentpin.SetTitle("");
                }
                else if (this._currentpin.Pins > 1 && this._currentpin.GetShapeLayer().IsExpansion == false) {
                    this._currentpin.SetDescription("<div id='" + this._PopupPrefix + this._currentpin.GetID() + "'>Click to Expand</div>");
                    this._currentpin.SetTitle("");
                }
                else {
                    this._currentpin.SetTitle(result.Title);
                    this._currentpin.SetDescription(result.Details);
                }

            } else {
                //create the content element
                var el = document.createElement("div");
                Sys.UI.DomElement.addCssClass(el, "PopupArea");

                var TitleDiv = document.createElement("div");
                //var ZoomDiv = document.createElement("div");
                var DetailsDiv = document.createElement("div");
                //ZoomDiv.setAttribute("title", "Zoom to current address");

                TitleDiv.innerHTML = "<b>Bore ID: " + result.Title + "</b>";
                Sys.UI.DomElement.addCssClass(TitleDiv, "TitleArea");
                //                ZoomDiv.innerHTML = "";
                //                Sys.UI.DomElement.addCssClass(ZoomDiv, "ZoomArea");
                DetailsDiv.innerHTML = "<br /><br />" + result.Details + "<br />";

                el.appendChild(TitleDiv);
                //                el.appendChild(ZoomDiv);
                el.appendChild(DetailsDiv);

                //Store current lat lon in case user wants to zoom to it
                this._currentLatLon = new VELatLong(result.Lat, result.Lon);

                //                $addHandler(ZoomDiv,"click",Function.createDelegate(this, this._ZoomRecord));      

                //clear loading and attach the content
                $get(this._PopupPrefix + ID).innerHTML = "";
                $get(this._PopupPrefix + ID).appendChild(el);

                //abc2.innerHTML = "";
                //abc2.appendChild(el);

                if (result.TotalRecords > 1) {
                    //prev / next functionlaity
                    var prevButton = document.createElement("div");
                    prevButton.innerHTML = "Previous";
                    el.appendChild(prevButton);
                    Sys.UI.DomElement.addCssClass(prevButton, "ActionButton");
                    if (this._currentindex > 0) {
                        $addHandler(prevButton, "click", Function.createDelegate(this, this._PreviousRecord));
                        prevButton.setAttribute("title", "Show previous bore");
                    } else {
                        Sys.UI.DomElement.addCssClass(prevButton, "ButtonDisabled");
                    }
                    var nextButton = document.createElement("div");
                    nextButton.innerHTML = "Next";
                    el.appendChild(nextButton);
                    Sys.UI.DomElement.addCssClass(nextButton, "ActionButton");
                    if (this._currentindex < (result.TotalRecords - 1)) {
                        $addHandler(nextButton, "click", Function.createDelegate(this, this._NextRecord));
                        nextButton.setAttribute("title", "Show next bore");
                    } else {
                        Sys.UI.DomElement.addCssClass(nextButton, "ButtonDisabled");
                    }

                    var pageCount = document.createElement("div");
                    pageCount.innerHTML = "&nbsp; &nbsp;" + (this._currentindex + 1) + " of " + result.TotalRecords + " Records";
                    el.appendChild(pageCount);
                    Sys.UI.DomElement.addCssClass(pageCount, "PageCount");
                }

                //If this is the first time the popup is shown see if it is off screen
                if (this._currentindex == 0) {
                    //Parent Popup DIV
                    //This is making a big assumption on the layout of VE Popup
                    var parentDiv = el.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;

                    if (!isNaN(parseInt(parentDiv.style.left))) {
                        var beak = parentDiv.childNodes[1];

                        if (parseInt(parentDiv.style.left) < -1)  //The amount of overlap before we change the class
                        {
                            var origLeft = parseInt(parentDiv.style.left);
                            origLeft = origLeft * -1; // Change to a positive number (absolute)
                            origLeft = origLeft - 20; // How far the beak is offset from the side
                            parentDiv.style.left = "10px"; //How far we want it to sit inside the left hand side

                            if (origLeft < 24) {
                                //Make sure the beak sits on the div
                                parentDiv.style.left = (origLeft * -1) + 34 + "px";
                                origLeft = 24;
                            }

                            if (parseInt(parentDiv.style.top) < parentDiv.clientHeight) {
                                //Too close to the top, put the div below the pin
                                if (parseInt(beak.style.top) == parseInt(parentDiv.style.top))
                                    parentDiv.style.top = parseInt(parentDiv.style.top) + 44 + "px"; //lower the div
                                else
                                    parentDiv.style.top = parseInt(parentDiv.style.top) + 80 + "px"; //lower the div

                                if (parentDiv.className.indexOf('noBeak') == -1) {
                                    beak.style.top = "-20px"; //put the beak above the top of the div

                                    if (parentDiv.className.indexOf('ero-leftBeak') > -1) {
                                        //beak will appear to the top left
                                        beak.style.left = origLeft + "px";
                                        beak.style.right = "";
                                    }
                                    else {
                                        //beak will appear to the top right
                                        beak.style.right = origLeft + "px";
                                        beak.style.left = "";
                                    }

                                    //Change the image to the top one
                                    beak.className = "erotopBeak";
                                }
                            }
                            else {
                                //put the div above the pin
                                parentDiv.style.top = parseInt(parentDiv.style.top) - (parentDiv.clientHeight - 20) + "px"; //raise the div

                                if (parentDiv.className.indexOf('noBeak') == -1) {
                                    beak.style.top = parentDiv.clientHeight - 4;  //put the beak below the bottom of the div

                                    if (parentDiv.className.indexOf('ero-leftBeak') > -1) {
                                        //beak will appear to the bottom left
                                        beak.style.left = origLeft + "px";
                                        beak.style.right = "";
                                    }
                                    else {
                                        //beak will appear to the bottom right
                                        beak.style.right = origLeft + "px";
                                        beak.style.left = "";
                                    }

                                    //Change the image to the bottom one
                                    beak.className = "erobottomBeak";
                                }

                            }
                        }
                        else {
                            if (parentDiv.className.indexOf('noBeak') == -1) {

                                //Reset Class back to normal left or right
                                if (parentDiv.className.indexOf('ero-leftBeak') > -1) {
                                    //beak.style.top = ""; //This is set by VE
                                    beak.style.right = "";
                                    beak.style.left = "";
                                    beak.className = "eroleftBeak";
                                }
                                else {
                                    //beak.style.top = ""; //This is set by VE
                                    beak.style.right = "";
                                    beak.style.left = "";
                                    beak.className = "erorightBeak";
                                }
                            }
                        }
                    }
                }
            }
        }
    },

    _PreviousRecord: function() {
        /// <summary>
        ///   Request the previous record.
        /// </summary>  
        this._currentindex--;
        $get(this._PopupPrefix + this._currentpin.GetID()).innerHTML = "Loading...";
        this._getAJAXContent();
    },

    _NextRecord: function() {
        /// <summary>
        ///   Request the next record.
        /// </summary>      
        this._currentindex++;
        $get(this._PopupPrefix + this._currentpin.GetID()).innerHTML = "Loading...";
        this._getAJAXContent();
    },

    Dispose: function() {
        /// <summary>
        ///   cleans up all objects. Detaches all events.
        /// </summary>
        if (this._map != null) {
            this._map.DetachEvent("onchangeview", this.GetPinDataDelegate);
            this._map.DetachEvent("onmouseover", this.PinHoverDelegate);
            this._map.DetachEvent("onclick", this.PinHoverDelegate);
            this._map.Dispose();
        }
        this._service = null;
        this._mapArgs = null;

        this._map = null;
        this._pinID = null;
        this._zoomlevel = null;
        this._boreLayer = null;

        //popup specific
        this._PopupPrefix = null;
        this._currentpin = null;
        this._currentindex = null;

        this.GetPinDataDelegate = null;
        this.PinHoverDelegate = null;
    }
}

GeomaticTechnologies.GMU.Map.registerClass('GeomaticTechnologies.GMU.Map', null, Sys.IDisposable);

if (typeof(Sys) !== "undefined") Sys.Application.notifyScriptLoaded();


