if (typeof jQuery != "function") {
	throw new Error("GLocation.js: You must load the jQuery library!");
}

function GLocation(config) {
	this.config = $.extend({}, GLocation.defaultConfig, config || {});
	this.distance = NaN; // stores the distance in meters to destination chosen by user
	this.marker = null; // stores the marker on the map associated with this gLocation
	this.gLatLng = (this.config.gLatLng && this.config.gLatLng.constructor == Array) ? new GLatLng(this.config.gLatLng[0], this.config.gLatLng[1]) : null;
	this.index = GLocation.instances.length;
	this.warning = "";
	this.tries = -1;
	this.duplicateIndex = NaN;
	this.initialized = false;
}

GLocation.defaultConfig = {
	name: "",
	street: "",
	zipcode: "",
	city: "",
	baseCountryCode: "",
	gLatLng: null, // array containing the coordinates used to instantiate the GLatLng class
	warning: "", // is set if Google is unable to locate the given address - is displayed to the user
	onNotFound: null,
	onFound: null,
	infoWindowOptions: null, // object literal with options for the marker info window
	mapIcon: null // must be GIcon object
}

GLocation.instances = [];
GLocation.invalidAddress = [];
GLocation.duplicates = [];

GLocation.geocoder = new GClientGeocoder();

GLocation.newInstance = function(config) {
	var instance = new GLocation(config);
	GLocation.instances.push(instance);
	return instance;
}

GLocation.initGLocations = function() {
	for (var i = 0, l = GLocation.instances.length; i < l; i++) {
		GLocation.instances[i].init();
	}
}

GLocation.compare = function(gLocation1, gLocation2) {
	return gLocation1.compareTo(gLocation2);
}

GLocation.prototype.toString = function() {
	return "[object GLocation] " + this.getDisplayName() + ", " + this.getAddress();
}

GLocation.prototype.checkDuplicate = function() {
	// necessary with a try - catch block - stopReading may have been called (the instance array is reset/cleared)
	try {
		for (var i = 0; i < this.index; i++) { // this looping takes a long time
			var instance = GLocation.instances[i];
			if (this.getAddress() == instance.getAddress() || (this.gLatLng && this.gLatLng.equals(instance.gLatLng))) {
				this.duplicateIndex = i;
				GLocation.duplicates.push(this);
				break;
			}
		}
	}
	catch (ex) {
	}
}

GLocation.prototype.init = function() {
	if (!this.initialized) {
		if (!(this.gLatLng instanceof GLatLng)) {
			var q = this.getAddress();
			if (this.config.baseCountryCode) {
				q += ", " + this.config.baseCountryCode;
			}
			else if (GLocation.geocoder.getBaseCountryCode()) {
				q += ", " + GLocation.geocoder.getBaseCountryCode();
			}
			GLocation.geocoder.getLocations(q, GEvent.callback(this, this.callback));
		}
		this.initialized = true;
	}
}

GLocation.prototype.getAddress = function() {
	return this.config.street + ", " + this.config.zipcode + " " + this.config.city;
}

GLocation.prototype.getDisplayName = function() {
	return this.config.name;
}

GLocation.prototype.callback = function(response) {
	this.tries++;
	if (response.Status.code != 200) {
		this.warning = this.config.warning;
		if (!GLocation.invalidAddress.includes(this, true)) {
			GLocation.invalidAddress.push(this);
		}
		if (typeof this.config.onNotFound == "function") {
			this.config.onNotFound.apply(this, [response]);
		}
		return;
	}
	var place = response.Placemark[0];
	this.gLatLng = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
	this.checkDuplicate();
	if (typeof this.config.onFound == "function") {
		this.config.onFound.apply(this, [response]);
	}
}

GLocation.prototype.compareTo = function(gLocation) {
	if (gLocation) {
		if (this.distance < gLocation.distance) {
			return -1;
		}
		if (this.distance > gLocation.distance) {
			return 1;
		}
	}
	return 0;
}
