﻿var levelPointTileSpace = [];
var symbolClustering = true;
var lotsOfPoints = [];

function initFunctionality(map, points) {
    var lotsOfPoints = points;
    addClusteredPoints(lotsOfPoints, map, 2, map.extent);
    dojo.connect(map, "onZoomStart", function() {
        map.graphics.clear();
    });
    dojo.connect(map, "onExtentChange", function(extent, delta, leveChange, lod) {
        addClusteredPoints(lotsOfPoints, map, lod.level + 2, extent);
    });
}

function addClusteredPoints(points, map, level, extent) {
    var df = dojox.lang.functional;
    //Get current tiling scheme.  This restriction can be removed.  
    //The only thing required is origin, array of grid pixel resolution, & grid pixel size
    var tileInfo = map.getLayer(map.layerIds[0]).tileInfo;

    //lambda function to map points to tile space
    var toTileSpaceF = df.lambda("point, tileWidth, tileHeight, oPoint "
            + "-> [Math.floor((oPoint.y - point.y)/tileHeight),Math.floor((point.x-oPoint.x)/tileWidth), point]");

    var levelResolution = tileInfo.lods[level].resolution;
    var width = levelResolution * tileInfo.width;
    var height = levelResolution * tileInfo.height;

    //predefine width, height, origin point for toTileSpaceF function
    var toTileSpace = df.partial(toTileSpaceF, df.arg, width, height, tileInfo.origin);
    var extentTileCords = df.map([new esri.geometry.Point(extent.xmin, extent.ymin), new esri.geometry.Point(extent.xmax, extent.ymax)], toTileSpace);  //map extent corners to tile sapce
    var minRowIdx = extentTileCords[1][0];
    var maxRowIdx = extentTileCords[0][0];
    var minColIdx = extentTileCords[0][1];
    var maxColIdx = extentTileCords[1][1];

    var pointSymbol = {
        "single": new esri.symbol.PictureMarkerSymbol("images/CircleBlue24.png", 24, 24),
        "grouped": new esri.symbol.PictureMarkerSymbol("images/CircleRed32.png", 32, 32)
    };

    //points to tiles
    if (!levelPointTileSpace[level]) {
        var pointsTileSpace = df.map(points, toTileSpace);  //map all points to tilespace
        var tileSpaceArray = [];
        dojo.forEach(pointsTileSpace, function(tilePoint, ptIndex) {  //swizel points[row,col,point] to row[col[points[]]]
            if (tileSpaceArray[tilePoint[0]]) {
                var y = tileSpaceArray[tilePoint[0]];
                if (y[tilePoint[1]]) {
                    y[tilePoint[1]].push(tilePoint[2]);
                } else {
                    y[tilePoint[1]] = tilePoint[1];
                    y[tilePoint[1]] = [tilePoint[2]];
                }
            } else {
                var y = tileSpaceArray[tilePoint[0]] = [];
                y[tilePoint[1]] = tilePoint[1];
                y[tilePoint[1]] = [tilePoint[2]];
            }
        });
        //once this has been computed store this in a level array
        levelPointTileSpace[level] = tileSpaceArray;
    }
    var tileCenterPointF = df.lambda("cPt,nextPt->{x:(cPt.x+nextPt.x)/2,y:(cPt.y+nextPt.y)/2}");

    dojo.forEach(levelPointTileSpace[level], function(row, rowIndex) {
        if ((rowIndex >= minRowIdx) & (rowIndex <= maxRowIdx)) {
            dojo.forEach(row, function(col, colIndex) {
                if (col) {
                    if ((colIndex >= minColIdx) & (colIndex <= maxColIdx)) {
                        var pointsToBeAdded = [];
                        if (col.length > 2) {
                            var tileCenterPoint = df.reduce(col, tileCenterPointF);
                            map.graphics.add(new esri.Graphic(new esri.geometry.Point(tileCenterPoint.x, tileCenterPoint.y), pointSymbol.grouped));
                            (dojo.isIE) ? null : map.graphics.add(new esri.Graphic(new esri.geometry.Point(tileCenterPoint.x, tileCenterPoint.y), new esri.symbol.TextSymbol(col.length).setOffset(0, -3)));
                        }
                        else {
                            dojo.forEach(col, function(point) {
                                map.graphics.add(new esri.Graphic(new esri.geometry.Point(point), pointSymbol.single));
                            });
                        }
                    }
                }
            });
        }
    });
}