/**

 * @namespace WPGMZA

 * @module LatLng

 * @requires WPGMZA

 */

(function($) {



	/**

	 * Constructor

	 * @param mixed A latLng literal, or latitude

	 * @param mixed The latitude, where arg is a longitude

	 */

	WPGMZA.LatLng = function(arg, lng)

	{

		this._lat = 0;

		this._lng = 0;

		

		if(arguments.length == 0)

			return;

		

		if(arguments.length == 1)

		{

			// TODO: Support latlng string

			

			if(typeof arg == "string")

			{

				var m;

				

				if(!(m = arg.match(WPGMZA.LatLng.REGEXP)))

					throw new Error("Invalid LatLng string");

				

				arg = {

					lat: m[1],

					lng: m[3]

				};

			}

			

			if(typeof arg != "object" || !("lat" in arg && "lng" in arg))

				throw new Error("Argument must be a LatLng literal");

			

			this.lat = arg.lat;

			this.lng = arg.lng;

		}

		else

		{

			this.lat = arg;

			this.lng = lng;

		}

	}

	

	WPGMZA.LatLng.REGEXP = /^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/;

	

	WPGMZA.LatLng.isValid = function(obj)

	{

		if(typeof obj != "object")

			return false;

		

		if(!("lat" in obj && "lng" in obj))

			return false;

		

		return true;

	}

	

	Object.defineProperty(WPGMZA.LatLng.prototype, "lat", {

		get: function() {

			return this._lat;

		},

		set: function(val) {

			if(!$.isNumeric(val))

				throw new Error("Latitude must be numeric");

			this._lat = parseFloat( val );

		}

	});

	

	Object.defineProperty(WPGMZA.LatLng.prototype, "lng", {

		get: function() {

			return this._lng;

		},

		set: function(val) {

			if(!$.isNumeric(val))

				throw new Error("Longitude must be numeric");

			this._lng = parseFloat( val );

		}

	});

	

	WPGMZA.LatLng.prototype.toString = function()

	{

		return this._lat + ", " + this._lng;

	}

	

	WPGMZA.LatLng.prototype.toGoogleLatLng = function()

	{

		return new google.maps.LatLng({

			lat: this.lat,

			lng: this.lng

		});

	}

	

	/**

	 * @function moveByDistance

	 * @summary Moves this latLng by the specified kilometers along the given heading

	 * @return void

	 * With many thanks to Hu Kenneth - https://gis.stackexchange.com/questions/234473/get-a-lonlat-point-by-distance-or-between-2-lonlat-points

	 */

	WPGMZA.LatLng.prototype.moveByDistance = function(kilometers, heading)

	{

		var radius 		= 6371;

		

		var delta 		= parseFloat(kilometers) / radius;

		var theta 		= parseFloat(heading) / 180 * Math.PI;

		

		var phi1 		= this.lat / 180 * Math.PI;

		var lambda1 	= this.lng / 180 * Math.PI;

		

		var sinPhi1 	= Math.sin(phi1), cosPhi1 = Math.cos(phi1);

		var sinDelta	= Math.sin(delta), cosDelta = Math.cos(delta);

		var sinTheta	= Math.sin(theta), cosTheta = Math.cos(theta);

		

		var sinPhi2		= sinPhi1 * cosDelta + cosPhi1 * sinDelta * cosTheta;

		var phi2		= Math.asin(sinPhi2);

		var y			= sinTheta * sinDelta * cosPhi1;

		var x			= cosDelta - sinPhi1 * sinPhi2;

		var lambda2		= lambda1 + Math.atan2(y, x);

		

		this.lat		= phi2 * 180 / Math.PI;

		this.lng		= lambda2 * 180 / Math.PI;

	}

	

})(jQuery);