# Source: modern-store-locator-circle.js

    /**
     * @namespace WPGMZA
     * @module ModernStoreLocatorCircle
     * @requires WPGMZA
     */
    (function($) {

    	/**
    	 * This module is the modern store locator circle
    	 * @constructor
    	 */
    	WPGMZA.ModernStoreLocatorCircle = function(map_id, settings) {
    		var self = this;
    		var map;

    		if(WPGMZA.isProVersion())
    			map = this.map = MYMAP[map_id].map;
    		else
    			map = this.map = MYMAP.map;

    		this.map_id = map_id;
    		this.mapElement = map.element;
    		this.mapSize = {
    			width:  $(this.mapElement).width(),
    			height: $(this.mapElement).height()
    		};

    		this.initCanvasLayer();

    		this.settings = {
    			center: new WPGMZA.LatLng(0, 0),
    			radius: 1,
    			color: "#63AFF2",

    			shadowColor: "white",
    			shadowBlur: 4,

    			centerRingRadius: 10,
    			centerRingLineWidth: 3,

    			numInnerRings: 9,
    			innerRingLineWidth: 1,
    			innerRingFade: true,

    			numOuterRings: 7,

    			ringLineWidth: 1,

    			mainRingLineWidth: 2,

    			numSpokes: 6,
    			spokesStartAngle: Math.PI / 2,

    			numRadiusLabels: 6,
    			radiusLabelsStartAngle: Math.PI / 2,
    			radiusLabelFont: "13px sans-serif",

    			visible: false
    		};

    		if(settings)
    			this.setOptions(settings);
    	};

    	WPGMZA.ModernStoreLocatorCircle.createInstance = function(map, settings) {

    		if(WPGMZA.settings.engine == "google-maps")
    			return new WPGMZA.GoogleModernStoreLocatorCircle(map, settings);
    		else
    			return new WPGMZA.OLModernStoreLocatorCircle(map, settings);

    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.initCanvasLayer = function() {

    	}

    	WPGMZA.ModernStoreLocatorCircle.prototype.onResize = function(event) {
    		this.draw();
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.onUpdate = function(event) {
    		this.draw();
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.setOptions = function(options) {
    		for(var name in options)
    		{
    			var functionName = "set" + name.substr(0, 1).toUpperCase() + name.substr(1);

    			if(typeof this[functionName] == "function")
    				this[functionName](options[name]);
    			else
    				this.settings[name] = options[name];
    		}
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.getResolutionScale = function() {
    		return window.devicePixelRatio || 1;
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.getCenter = function() {
    		return this.getPosition();
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.setCenter = function(value) {
    		this.setPosition(value);
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.getPosition = function() {
    		return this.settings.center;
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.setPosition = function(position) {
    		this.settings.center = position;
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.getRadius = function() {
    		return this.settings.radius;
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.setRadius = function(radius) {

    		if(isNaN(radius))
    			throw new Error("Invalid radius");

    		this.settings.radius = radius;
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.getVisible = function(visible) {
    		return this.settings.visible;
    	};

    	WPGMZA.ModernStoreLocatorCircle.prototype.setVisible = function(visible) {
    		this.settings.visible = visible;
    	};

    	/**
    	 * This function transforms a km radius into canvas space
    	 * @return number
    	 */
    	WPGMZA.ModernStoreLocatorCircle.prototype.getTransformedRadius = function(km)
    	{
    		throw new Error("Abstract function called");
    	}

    	WPGMZA.ModernStoreLocatorCircle.prototype.getContext = function(type)
    	{
    		throw new Error("Abstract function called");
    	}

    	WPGMZA.ModernStoreLocatorCircle.prototype.getCanvasDimensions = function()
    	{
    		throw new Error("Abstract function called");
    	}

    	WPGMZA.ModernStoreLocatorCircle.prototype.draw = function() {

    		var settings = this.settings;
    		var canvasDimensions = this.getCanvasDimensions();

            var canvasWidth = canvasDimensions.width;
            var canvasHeight = canvasDimensions.height;

    		var map = this.map;
    		var resolutionScale = this.getResolutionScale();

    		context = this.getContext("2d");
            context.clearRect(0, 0, canvasWidth, canvasHeight);

    		if(!settings.visible)
    			return;

    		context.shadowColor = settings.shadowColor;
    		context.shadowBlur = settings.shadowBlur;

    		// NB: 2018/02/13 - Left this here in case it needs to be calibrated more accurately
    		/*if(!this.testCircle)
    		{
    			this.testCircle = new google.maps.Circle({
    				strokeColor: "#ff0000",
    				strokeOpacity: 0.5,
    				strokeWeight: 3,
    				map: this.map,
    				center: this.settings.center
    			});
    		}

    		this.testCircle.setCenter(settings.center);
    		this.testCircle.setRadius(settings.radius * 1000);*/

            // Reset transform
            context.setTransform(1, 0, 0, 1, 0, 0);

            var scale = Math.pow(2, map.getZoom()) * resolutionScale;
            context.scale(scale, scale);

    		// Translate by world origin
    		var offset = this.getWorldOriginOffset();
    		context.translate(offset.x, offset.y);

            // Get center and project to pixel space
    		var center = new WPGMZA.LatLng(this.settings.center);
    		var worldPoint = this.getCenterPixels();

    		var rgba = WPGMZA.hexToRgba(settings.color);
    		var ringSpacing = this.getTransformedRadius(settings.radius) / (settings.numInnerRings + 1);

    		// TODO: Implement gradients for color and opacity

    		// Inside circle (fixed?)
            context.strokeStyle = settings.color;
    		context.lineWidth = (1 / scale) * settings.centerRingLineWidth;

    		context.beginPath();
    		context.arc(
    			worldPoint.x,
    			worldPoint.y,
    			this.getTransformedRadius(settings.centerRingRadius) / scale, 0, 2 * Math.PI
    		);
    		context.stroke();
    		context.closePath();

    		// Spokes
    		var radius = this.getTransformedRadius(settings.radius) + (ringSpacing * settings.numOuterRings) + 1;
    		var grad = context.createRadialGradient(0, 0, 0, 0, 0, radius);
    		var rgba = WPGMZA.hexToRgba(settings.color);
    		var start = WPGMZA.rgbaToString(rgba), end;
    		var spokeAngle;

    		rgba.a = 0;
    		end = WPGMZA.rgbaToString(rgba);

    		grad.addColorStop(0, start);
    		grad.addColorStop(1, end);

    		context.save();

    		context.translate(worldPoint.x, worldPoint.y);
    		context.strokeStyle = grad;
    		context.lineWidth = 2 / scale;

    		for(var i = 0; i < settings.numSpokes; i++)
    		{
    			spokeAngle = settings.spokesStartAngle + (Math.PI * 2) * (i / settings.numSpokes);

    			x = Math.cos(spokeAngle) * radius;
    			y = Math.sin(spokeAngle) * radius;

    			context.setLineDash([2 / scale, 15 / scale]);

    			context.beginPath();
    			context.moveTo(0, 0);
    			context.lineTo(x, y);
    			context.stroke();
    		}

    		context.setLineDash([]);

    		context.restore();

    		// Inner ringlets
    		context.lineWidth = (1 / scale) * settings.innerRingLineWidth;

    		for(var i = 1; i <= settings.numInnerRings; i++)
    		{
    			var radius = i * ringSpacing;

    			if(settings.innerRingFade)
    				rgba.a = 1 - (i - 1) / settings.numInnerRings;

    			context.strokeStyle = WPGMZA.rgbaToString(rgba);

    			context.beginPath();
    			context.arc(worldPoint.x, worldPoint.y, radius, 0, 2 * Math.PI);
    			context.stroke();
    			context.closePath();
    		}

    		// Main circle
    		context.strokeStyle = settings.color;
    		context.lineWidth = (1 / scale) * settings.centerRingLineWidth;

    		context.beginPath();
    		context.arc(worldPoint.x, worldPoint.y, this.getTransformedRadius(settings.radius), 0, 2 * Math.PI);
    		context.stroke();
    		context.closePath();

    		// Outer ringlets
    		var radius = radius + ringSpacing;
    		for(var i = 0; i < settings.numOuterRings; i++)
    		{
    			if(settings.innerRingFade)
    				rgba.a = 1 - i / settings.numOuterRings;

    			context.strokeStyle = WPGMZA.rgbaToString(rgba);

    			context.beginPath();
    			context.arc(worldPoint.x, worldPoint.y, radius, 0, 2 * Math.PI);
    			context.stroke();
    			context.closePath();

    			radius += ringSpacing;
    		}

    		// Text
    		if(settings.numRadiusLabels > 0)
    		{
    			var m;
    			var radius = this.getTransformedRadius(settings.radius);
    			var clipRadius = (12 * 1.1) / scale;
    			var x, y;

    			if(m = settings.radiusLabelFont.match(/(\d+)px/))
    				clipRadius = (parseInt(m[1]) / 2 * 1.1) / scale;

    			context.font = settings.radiusLabelFont;
    			context.textAlign = "center";
    			context.textBaseline = "middle";
    			context.fillStyle = settings.color;

    			context.save();

    			context.translate(worldPoint.x, worldPoint.y)

    			for(var i = 0; i < settings.numRadiusLabels; i++)
    			{
    				var spokeAngle = settings.radiusLabelsStartAngle + (Math.PI * 2) * (i / settings.numRadiusLabels);
    				var textAngle = spokeAngle + Math.PI / 2;
    				var text = settings.radiusString;
    				var width;

    				if(Math.sin(spokeAngle) > 0)
    					textAngle -= Math.PI;

    				x = Math.cos(spokeAngle) * radius;
    				y = Math.sin(spokeAngle) * radius;

    				context.save();

    				context.translate(x, y);

    				context.rotate(textAngle);
    				context.scale(1 / scale, 1 / scale);

    				width = context.measureText(text).width;
    				height = width / 2;
    				context.clearRect(-width, -height, 2 * width, 2 * height);

    				context.fillText(settings.radiusString, 0, 0);

    				context.restore();
    			}

    			context.restore();
    		}
    	}

    })(jQuery);