﻿Type.registerNamespace('PComCs.Hubs.Components');
PComCs.Hubs.Components.MarkerRequestManager = function() {
    PComCs.Hubs.Components.MarkerRequestManager.initializeBase(this);

    this._loadMarkersFromServiceFunction;

    this._root = {
        isLoaded: false,
        isEmpty: false,
        zoomLevel: 0,
        bounds: new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)),
        children: new Array()
    };

    this._alertTree = function(node) {
        if (node == null)
            node = this._root;

        var msg = '';
        for (var i = 0; i < node.zoomLevel; i++)
            msg += '-';

        msg += "isLoaded: " + node.isLoaded + ', ';
        msg += "isEmpty: " + node.isEmpty + ', ';
        msg += "zoomLevel: " + node.zoomLevel + ', ';
        msg += "bounds: " + node.bounds.toString() + '\r\n';

        for (var j = 0; j < node.children.length; j++) {
            msg += this._alertTree(node.children[j]);
        }

        if (node == this._root)
            alert(msg);

        return msg;
    }

    this.REGIONS_PER_TILE_SIDE = 0.5;

    this._getRegionBounds = function(latLngBounds, zoomLevel) {

        var regionLatSize = Math.min(180, 180 / (Math.pow(2, zoomLevel) * this.REGIONS_PER_TILE_SIDE));
        var regionLngSize = Math.min(360, 360 / (Math.pow(2, zoomLevel) * this.REGIONS_PER_TILE_SIDE));

        var regionSwLat = (Math.floor((latLngBounds.getSouthWest().lat() + 90) / regionLatSize) * regionLatSize) - 90;
        var regionSwLng = (Math.floor((latLngBounds.getSouthWest().lng() + 180) / regionLngSize) * regionLngSize) - 180;

        var regionNeLat = (Math.ceil((latLngBounds.getNorthEast().lat() + 90) / regionLatSize) * regionLatSize) - 90;
        var regionNeLng = (Math.ceil((latLngBounds.getNorthEast().lng() + 180) / regionLngSize) * regionLngSize) - 180;

        // If the latitude or longitude "wrapped" around the globe, then encompass the entire range.
        if (latLngBounds.getSouthWest().lat() > latLngBounds.getNorthEast().lat() && regionSwLat <= regionNeLat) {
            regionSwLat = -90;
            regionNeLat = 90;
        }

        if (latLngBounds.getSouthWest().lng() > latLngBounds.getNorthEast().lng() && regionSwLng <= regionNeLng) {
            regionSwLng = -180;
            regionNeLng = 180;
        }

        return new GLatLngBounds(new GLatLng(regionSwLat, regionSwLng), new GLatLng(regionNeLat, regionNeLng));
    };

    this._getRegions = function(latLngBounds, zoomLevel) {

        var regionLatSize = Math.min(180, 180 / (Math.pow(2, zoomLevel) * this.REGIONS_PER_TILE_SIDE));
        var regionLngSize = Math.min(360, 360 / (Math.pow(2, zoomLevel) * this.REGIONS_PER_TILE_SIDE));

        var regions = new Array();

        // Compute the number of clumps needed (along each side) to cover the bounds.
        var latRegionCount;
        var lngRegionCount;

        if (latLngBounds.getNorthEast().lat() > latLngBounds.getSouthWest().lat())
            latRegionCount = Math.max(1, (latLngBounds.getNorthEast().lat() - latLngBounds.getSouthWest().lat()) / regionLatSize);
        else
            latRegionCount = Math.max(1, (180 + latLngBounds.getNorthEast().lat() - latLngBounds.getSouthWest().lat()) / regionLatSize);

        if (latLngBounds.getNorthEast().lng() > latLngBounds.getSouthWest().lng())
            lngRegionCount = Math.max(1, (latLngBounds.getNorthEast().lng() - latLngBounds.getSouthWest().lng()) / regionLngSize);
        else
            lngRegionCount = Math.max(1, (360 + latLngBounds.getNorthEast().lng() - latLngBounds.getSouthWest().lng()) / regionLngSize);

        for (var latIdx = 0; latIdx < latRegionCount; latIdx++) {
            for (var lngIdx = 0; lngIdx < lngRegionCount; lngIdx++) {
                regions.push(new GLatLngBounds(
                    new GLatLng(latLngBounds.getSouthWest().lat() + (regionLatSize * latIdx), latLngBounds.getSouthWest().lng() + (regionLngSize * lngIdx)),
                    new GLatLng(latLngBounds.getSouthWest().lat() + (regionLatSize * (latIdx + 1)), latLngBounds.getSouthWest().lng() + (regionLngSize * (lngIdx + 1)))));
            }
        }

        return regions;
    };

    this._loadMarkersForRegion = function(regionToLoad, zoomLevel, successCallback, currentNode) {

        //        var msg = '';
        //        msg += "regionToLoad: " + regionToLoad.toString() + "\r\n";
        //        msg += "zoomLevel: " + zoomLevel + "\r\n";
        //        msg += "\r\n";
        //        msg += "currentNode.bounds: " + currentNode.bounds.toString() + "\r\n";
        //        msg += "currentNode.isLoaded: " + currentNode.isLoaded + "\r\n";
        //        msg += "currentNode.isEmpty: " + currentNode.isEmpty + "\r\n";
        //        msg += "currentNode.zoomLevel: " + currentNode.zoomLevel + "\r\n";
        //        msg += "\r\n";
        //        msg += "currentNode.bounds.containsBounds(regionToLoad): " + currentNode.bounds.containsBounds(regionToLoad) + "\r\n";
        //        msg += "currentNode.bounds.equals(regionToLoad): " + currentNode.bounds.equals(regionToLoad) + "\r\n";

        //        if (currentNode.bounds.containsBounds(regionToLoad))
        //            alert(msg);

        // If the region to load doesn't isn't contained by the node's region, 
        // then return false (No markers loaded)
        if (currentNode.bounds.containsBounds(regionToLoad) == false)
            return false;

        // If the region to load is contained by the node's region,
        // and the node is loaded,
        // and the node's region is empty,
        // then return true (Markers already loaded)
        if (currentNode.isLoaded == true &&
            currentNode.isEmpty == true)
            return true;

        // If the region to load equals the node's region,
        // and the zoom level equals the node's zoom level,
        // and the current node has been loaded,
        // then return true (Markers already loaded)
        if (currentNode.bounds.equals(regionToLoad) &&
            currentNode.zoomLevel == zoomLevel &&
            currentNode.isLoaded == true)
            return true;

        // If the specified zoom level is greater than the current node's zoom level,
        // then recursively search the node's children.
        // Otherwise, load the markers from the service.
        if (currentNode.zoomLevel < zoomLevel) {
            // If the current node has no children, then add them.
            if (currentNode.children.length == 0) {
                var childRegions = this._getRegions(currentNode.bounds, currentNode.zoomLevel + 1);

                for (var i = 0; i < childRegions.length; i++) {
                    var newNode = {
                        isLoaded: false,
                        isEmpty: false,
                        zoomLevel: currentNode.zoomLevel + 1,
                        bounds: childRegions[i],
                        children: new Array()
                    };

                    currentNode.children.push(newNode);
                }
            }

            for (var j = 0; j < currentNode.children.length; j++) {
                var markersLoaded = this._loadMarkersForRegion(regionToLoad, zoomLevel, successCallback, currentNode.children[j]);

                if (markersLoaded) return true;
            }
        }
        else {
            // Create a new success callback function to creates a 
            // new node,indicating that the markers have been loaded.
            var successCallbackWrapper = function(results) {

                // Call the base success callback function.
                successCallback(results);

                if (results) {

//                    var msg = '';
//                    msg += 'currentNode.bounds: ' + currentNode.bounds.toString() + '\r\n';
//                    msg += '\r\n';
//                    msg += 'results.clumpMarkers.length: ' + results.clumpMarkers.length + '\r\n';
//                    msg += 'results.instanceMarkers.length: ' + results.instanceMarkers.length + '\r\n';
//                    alert(msg);

                    // Flag the current node as loaded.
                    currentNode.isEmpty = results.clumpMarkers.length + results.instanceMarkers.length == 0;
                    currentNode.isLoaded = true;
                }
            };

            // Load the markers from the service.
            this._loadMarkersFromServiceFunction(regionToLoad, zoomLevel, successCallbackWrapper);

            return true;
        }
    }
}

PComCs.Hubs.Components.MarkerRequestManager.prototype =
{
    get_loadMarkersFromServiceFunction: function() {
        return this._loadMarkersFromServiceFunction;
    },
    set_loadMarkersFromServiceFunction: function(value) {
        this._loadMarkersFromServiceFunction = value;
    },
    getMarkers: function(latLngBounds, zoomLevel, successCallback) {

        // Normalize the specified bounds.
        var regionBounds = this._getRegionBounds(latLngBounds, zoomLevel);

//        var msg = '';
//        msg += "zoomLevel: " + zoomLevel + "\r\n";
//        msg += "latLngBounds: " + latLngBounds.toString() + "\r\n";
//        msg += "regionBounds: " + regionBounds.toString() + "\r\n";
//        alert(msg);
//        msg += "\r\n";

        // Split the specified bounds into a list of loadable regions,
        var regionsToLoad = this._getRegions(regionBounds, zoomLevel);

        // Load markers (if necessary) for each region.
        while (regionsToLoad.length > 0) {
            var regionToLoad = regionsToLoad.pop();
//            msg += "regionToLoad: " + regionToLoad.toString() + "\r\n";
//            alert(msg);

            // Load markers beginning with the root node.           
            this._loadMarkersForRegion(regionToLoad, zoomLevel, successCallback, this._root);
        }
    }
}

PComCs.Hubs.Components.MarkerRequestManager.registerClass('PComCs.Hubs.Components.MarkerRequestManager',Sys.Component);
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();