﻿var DataPointHandler = function(mapReference, shapeLayer, shapeCallback) {
    /********************
    * Private Properties
    *********************/
    var _map = mapReference;
    var _layer = shapeLayer;
    var _shapeCallback = shapeCallback;
    var _data = null;
    var _mapWidth = 0;
    var _mapHeight = 0;

    /****************
    * Private Methods 
    *****************/

    function _init() {
        _reinitialize();
    }

    function _reinitialize() {
        //Calculate the size of the map in pixels
        //This should always be done so that the map can be resizable
        var mapView = _map.GetMapView();
        var bottomRight = _map.LatLongToPixel(mapView.BottomRightLatLong);
        _mapWidth = parseInt(Math.ceil(bottomRight.x));
        _mapHeight = parseInt(Math.ceil(bottomRight.y));
    }

    /****************
    * Public Methods 
    *****************/

    this.Redraw = function() {
        //remove all pins from the layer
        _layer.DeleteAllShapes();

        if (_data != null) {
            //create an array to store the shapes in.
            var shapes = [];
            var zoomLevel = _map.GetZoomLevel();

            //Itirate through the shapes in the base layer
            var i = _data.length - 1;
            do {
                var pixel = _map.LatLongToPixel(_data[i].LatLong);

                //check to see if the shape is within the bounds of the viewable map
                if (pixel != null && pixel.x <= _mapWidth && pixel.y <= _mapHeight && pixel.x >= 0 && pixel.y >= 0 &&
                //zoom level logic
                ((zoomLevel < 7 && _data[i].isMajorCity == 1) || (zoomLevel >= 7 && zoomLevel < 9 && _data[i].Type == 2) || zoomLevel >= 9)
                ) {
                    shape = _shapeCallback(_data[i]);
                    shapes.push(shape);
                }
            }
            while (i--);

            _layer.AddShape(shapes);

            var i = shapes.length - 1;

            if (i > 0) {
                do {
                    //can only set z-index after shape added to map
                    shapes[i].SetZIndex(shapes[i]._zIndex);
                }
                while (i--);
            }
        }
    };

    //Cluster Functionality Specific Methods

    /**
    * Reinitializes the constants being used to perform the calculates. These constants a depended on the map dimensions.
    * Example:  clusterAlgorithm.ResetConstants();
    */
    this.ResetConstants = function() {
        _reinitialize();
    };

    /**
    * Returns a reference to the shape layer being used.
    * Example:  clusterAlgorithm.GetShapeLayer();
    */
    this.GetShapeLayer = function() {
        return _layer;
    };

    /**
    * Sets the shape layer that is used to to display the clustered data.
    * @param {VEShapeLayer} - VEShapeLayer that is to be used
    * Example:  clusterAlgorithm.SetShapeLayer(layer);
    */
    this.SetShapeLayer = function(a) {
        _layer = a;
    };

    //Callback handlers

    /**
    * Sets callback function that gets called when creating a pushpin to represent a single data point.
    * @param {function} - Function to be called when creating single point pushpin.
    * Example:  clusterAlgorithm.SetSingleShapeCallback(callback);
    */
    this.SetShapeCallback = function(a) {
        _shapeCallback = a;
    };

    //Data Handlers

    /**
    * Sets the data that is to be clustered and displayed on the map.
    * @param {[object]} - An array of objects that are to be mapped. All objects 
    * must at minimium have a LAtLong property with valid latitude and longitude values. 
    * The algorithm will convert them to VELatLong when loading in data.
    * Example:  clusterAlgorithm.SetData(data);
    */
    this.SetData = function(a) {
        _data = a;

        this.Redraw();
    };

    /**
    * Gets the data array and returns it to the user. 
    * Example:  clusterAlgorithm.GetData();
    */
    this.GetData = function() {
        return _data;
    };

    this.GetDataByID = function(id) {
        var result;

        var i = _data.length - 1;
        if (i >= 0) {
            do {
                if (id == _data[i].ID) {
                    result = _data[i];
                    break;
                }
            }
            while (i--);
        }

        return result;
    };

    this.GetDataByLatLong = function(latlong) {
        var results = [];

        var i = _data.length - 1;
        if (i >= 0) {
            do {
                if (_data[i].LatLong.Latitude == latlong.Latitude && _data[i].LatLong.Longitude == latlong.Longitude) {
                    results.push(_data[i]);
                }
            }
            while (i--);
        }

        return results.reverse();
    };

    this.GetPointsByVisitDay = function(day) {
        var results = [];

        var previousDay = day - 1;

        var i = _data.length - 1;
        if (i >= 0) {
            do {
                if (day == _data[i].VisitDay) {
                    results.push(_data[i].LatLong);
                }
                if (previousDay == _data[i].VisitDay) {
                    results.push(_data[i].LatLong);
                    break;
                }
            }
            while (i--);
        }

        return results;
    };

    this.GetCommunitiesByProvince = function(province) {
        var results = [];

        var i = _data.length - 1;
        if (i >= 0) {
            do {
                if (province == _data[i].Province && jQuery.inArray(_data[i].City, results) < 0) {
                    results.push(_data[i].City);
                }
            }
            while (i--);
        }

        return results.sort();
    };

    this.GetDataByCityAndProvince = function(city, province) {
        var results;

        var i = _data.length - 1;
        if (i >= 0) {
            do {
                if (city == _data[i].City && province == _data[i].Province) {
                    results = _data[i];
                    break;
                }
            }
            while (i--);
        }

        return results;
    };

    this.GetDates = function() {
        var results = [];
        var day = 0;

        for (var i = 0; i < _data.length; i++) {
            if (day != _data[i].VisitDay) {
                results.push(_data[i].Date);
                day++;
            }
        }

        return results;
    };

    //Initialize class
    _init();
};
