var MODEL_MISSING_DATA_TEXT = "n/a";

/**
 * A model object contains all the data for the widget. Initially the load
 * method is called to retrieve the data from the server. Then the array of get
 * functions can be used to retrieve individual data items (without going to the
 * server). These functions are getRegionById, getMeasurementById, getMap,
 * getRegionsByType. These functions return complete de-normalised rows
 * containing Region, Map, and Label data.
 * 
 * @param serverURL -
 *            The base url of ther server to retrieve data from
 * @param widgetStyle -
 *            The widgetStyle is used to hit a different services for different
 *            sets of maps.
 */
function BOM_AWRIS_Model(baseURL, serverURL, widgetStyle, customerKey) {

  this.baseURL = baseURL;
	this.serverURL = serverURL;
	this.widgetStyle = widgetStyle;

	this.callback = "&callback=?";
	this.customerKeyParam = "?key=";
	this.customerKey = customerKey;

	this.MAP_BASE = 'mapBaseURL';

	this.measurements = MISSING;
	this.staticData = MISSING;
	this.charts = MISSING;
	this.error = MISSING;

	this.mappings = new Array();
	this.mappings['urn:bom.gov.au:awris:common:codelist:regiontype:country'] = 'country';
	this.mappings['urn:bom.gov.au:awris:common:codelist:regiontype:state'] = 'state';
	this.mappings['urn:bom.gov.au:awris:common:codelist:regiontype:city'] = 'city';
	this.mappings['urn:bom.gov.au:awris:common:codelist:regiontype:capitalcity'] = 'city';
	this.mappings['urn:bom.gov.au:awris:common:codelist:regiontype:drainagedivision'] = 'drainage';

	/**
	 * Loads the data from the server. Calls the given 'finalCallback' if the
	 * data is retrieved successfully. Passes the second parameter 'presenter'
	 * to this callback. The finalCallback is never called if the data is not
	 * retrieved.
	 */
	this.load = function(finalCallback, presenter) {
		// clear the error status
		this.errorStatus = MISSING;
		this.error = MISSING;
		var url = this.addSlash(this.baseURL) + this.addSlash(this.serverURL) + this.widgetStyle;

		function dataRecieved(data, model, finalCallback, presenter) {
			model.staticData = new Array();
			model.staticData.regions = new Array();
			model.staticData.maps = new Array();
			model.measurements = new Array();
			model.charts = new Array();
			var index = 0;
			
			//first try and parse an error
			if (typeof data.error != 'undefined') {
				var item = data.error;
				model.processErrorItem(item, model);
			}
			// now try and process the second format of JSON
			else if (typeof data.data != 'undefined') {
				if (typeof data.data.widgetRegion != 'undefined') {
					var item = data.data.widgetRegion;
					if (typeof data.data.widgetRegion['region_id'] != 'undefined') {
						model.processDataItem(item, model);
					} else if (typeof item.length != 'undefined') {
						for ( var i = 0; i < item.length; ++i) {
							model.processDataItem(item[i], model);
						}
					}
				}
			}
			// if that didn't work, try and process the first format
			// This is only included as some of the tests depend on the first
			// format in the future the test data should be converted to the 
			// new format and this code can be removed
			else {
				for (regionType in data) {
					if (regionType != model.MAP_BASE) {
						for (regionMapMeasurementIndex in data[regionType]) {
							var regionMapMeasurement = data[regionType][regionMapMeasurementIndex];
							regionMapMeasurement.type = regionType;
							if (typeof model.measurements[regionMapMeasurement.region_id] == 'undefined') {
								model.measurements.push(regionMapMeasurement);
							}
							if (typeof model.staticData.regions[regionMapMeasurement.region_id] == 'undefined') {
								model.staticData.regions
										.push(regionMapMeasurement);
							}
							if (typeof model.staticData.maps[index] == 'undefined') {
								// model.staticData.maps.push(regionMapMeasurement);
								regionMapMeasurement.map_id = index;
								model.staticData.maps[index] = regionMapMeasurement;
							}
							++index;
						}
					}
				}
			}

			model.staticData[model.MAP_BASE] = data[model.MAP_BASE];
			finalCallback(presenter);
		}
		this.getJSON(url, dataRecieved, finalCallback, presenter);
	};

	/**
	 * Retrieves a JSONP (or JSON if this.localDomain = true) file from the
	 * given url, calls the 'finalCallback' passing the 'presenter' on success.
	 */
	this.getJSON = function(url, callback, finalCallback, presenter) {
		var model = this;

		// add the customer key to the url
			url += this.customerKeyParam + this.customerKey;
					
    // add callback if this is NOT a relative url to canned data
		if (url.indexOf('http')!=-1) {
			url += this.callback;
		}	

		// setup an error handler
		$.ajaxSetup( {
			"error" : function(XMLHttpRequest, textStatus, errorThrown) {

				var error_code;
				var error_text;
				
				if (XMLHttpRequest.status != 200) {
					// Use the values from the XMLHttpRequest
					error_code = XMLHttpRequest.status;
					error_text = XMLHttpRequest.statusText;
				} else {
					// Use the values from the Parameters					
					error_code = errorThrown;
					error_text = textStatus;
				}

				model.error = new Object();
				model.error.error_code = error_code;
				model.error.error_text = error_text;
				
				// execute the finalCallback to display the error
				finalCallback(presenter);
			}
		});

		// execute the JSON service
		$.getJSON(url, function(data) {
			callback(data, model, finalCallback, presenter);
		});
	};

	/**
	 * Creates the regions, maps, measurements and charts from the WidgetRegion
	 * object (item) and populates the model.
	 */
	this.processDataItem = function(item, model) {
		model.staticData.regions.push(model.createRegion(item));
		var measurement = model.createMeasurement(item);
		if (measurement != null) {
			model.measurements.push(measurement);
		}
		if (typeof item.maps["map_type"] != 'undefined') {
			model.staticData.maps.push(model.createMap(item.maps,
					item['region_id'], model));
		} else if (typeof item.maps.length != 'undefined') {
			// is an array
			for ( var i = 0; i < item.maps.length; ++i) {
				model.staticData.maps.push(model.createMap(item.maps[i],
						item['region_id'], model));
			}
		}

		// NOTE: Since we are using a ResourceController to load charts
		// the database will have nothing in it and the following code will
		// be doing nothing.
		var chart = model.createChart(item['region_chart'], item['region_id']);
		if (chart != null) {
			model.charts.push(chart);
		}
	};

	/**
	 * Creates the regions, maps, measurements and charts from the WidgetRegion
	 * object (item) and populates the model.
	 */
	this.processErrorItem = function(item, model) {
		if (typeof item["error_code"] != 'undefined'){
			model.error = item;
		}
	};
	
	/**
	 * Creates a model chart item. Expects an object with values 'chart',
	 * 'image_height', 'image_width'
	 */
	this.createChart = function(chartInput, region_id) {
		if (typeof chartInput == 'undefined') {
			return null;
		}

		var modelChart = {
			'chart' : chartInput['chart'],
			'image_height' : chartInput['image_height'],
			'image_width' : chartInput['image_width'],
			'region_id' : region_id
		};
		return modelChart;
	};

	this.createRegion = function(item) {
		var type = item['region_type'];
		if (typeof this.mappings[type] != 'undefined') {
			type = this.mappings[type];
		}
		var region = {
			'region_id' : item['region_id'],
			'long_name' : item['long_name'],
			'short_name' : item['short_name'],
			'type' : type
		};
		return region;
	};

	this.createMeasurement = function(item) {
		if (typeof item['Volume'] == 'undefined'
				&& typeof item['Percentage'] == 'undefined') {
			return null;
		}
		var id = item['region_id'];
		var volume = item['Volume'];
		var percentage = item['Percentage'];
		var observation_date = item['ObservationDate'];
		if (typeof item['Volume'] == 'undefined')
			volume = MODEL_MISSING_DATA_TEXT;
		if (typeof item['Percentage'] == 'undefined')
			percentage = MODEL_MISSING_DATA_TEXT;
		if (typeof item['ObservationDate'] == 'undefined')
			observation_date = null;
		var measurement = {
			'region_id' : id,
			'volume' : volume,
			'percentage' : percentage,
			'observation_date' : observation_date
		};
		return measurement;
	};

	this.createLabel = function(label) {
		return { 'region_id':label['region_id'], 'x':label['x'], 'y':label['y'] };
	};

	this.createMap = function(mapInput, region_id, model) {
		var type = mapInput['map_type'];
		if (typeof this.mappings[type] != 'undefined') {
			type = this.mappings[type];
		}
		var map = {
			'region_id' : region_id,
			'map_type' : type,
			'map' : mapInput['map'],
			'image_height' : mapInput['image_height'],
			'image_width' : mapInput['image_width']
		};
		map.labels = new Array();
		if (typeof mapInput.labels != 'undefined') {
			if (typeof mapInput.labels['region_id'] != 'undefined') {
				map.labels.push(model.createLabel(mapInput.labels));
			} else if (typeof mapInput.labels.length != 'undefined') {
				// is an array
				for ( var i = 0; i < mapInput.labels.length; ++i) {
					map.labels.push(model.createLabel(mapInput.labels[i]));
				}
			}
		}
		return map;
	};

	/**
	 * Retrieved a Region(region_id,type,long_name,short_name) by region_id,
	 * returns null if it does not exist.
	 */
	this.getRegionById = function(region_id) {
		for ( var i = 0; i < this.staticData.regions.length; ++i) {
			var region = this.staticData.regions[i];
			if (region.region_id == region_id) {
				return region;
			}
		}
	};

	/**
	 * Retrieved a Measurement(region_id,volumeGL,percentageVolume) by
	 * region_id, returns null if it does not exist.
	 */
	this.getMeasurementById = function(region_id) {
		for ( var i = 0; i < this.measurements.length; ++i) {
			var measurement = this.measurements[i];
			if (measurement.region_id == region_id)
				return measurement;
		}
	};

	/**
	 * Retrieve a Chart(chart, image_height, image_width, region_id) by
	 * region_id, returns null if it does not exist.
	 */
	this.getChartByRegionId = function(region_id) {
		var length = this.charts.length;
		for ( var i = 0; i < length; ++i) {
			var chart = this.charts[i];
			if (chart.region_id == region_id)
				return chart;
		}
	};

	/**
	 * Retrieves a Map(map_id,region_id,map_type,map,[Label(region_id,x,y)]) by
	 * region_id and map_type. If no map_type is specified the first map with
	 * the given region_id is returned
	 */
	this.getMap = function(region_id, map_type) {
		for ( var i = 0; i < this.staticData.maps.length; ++i) {
			var map = this.staticData.maps[i];
			if (typeof map_type != 'undefined') {
				if (map.map_type == map_type && map.region_id == region_id) {
					return map;
				}
			} else {
				if (map.region_id == region_id) {
					return map;
				}
			}
		}
	};

	/**
	 * Returns all the regions of a given type as an array of
	 * Region(region_id,type,long_name,short_name)
	 */
	this.getRegionsByType = function(region_type) {
		var results = new Array();

		for ( var i = 0; i < this.staticData.regions.length; ++i) {
			var region = this.staticData.regions[i];
			if (region.type == region_type)
				results.push(region);
		}
		return results;
	};

	this.getErrorCode = function() {
		if (this.error != MISSING) {
			if (typeof this.error['error_code'] != 'undefined') {
				return this.error['error_code'];
			}
		}
	};

	this.getErrorText = function() {
		if (this.error != MISSING) {
			if (typeof this.error['error_text'] != 'undefined') {
				return this.error['error_text'];
			}
		}
	};

	/**
	 * Add a trailing slash to supplied url.
	 */
	this.addSlash = function(url) {
	    if(url && url.length > 1 && url.charAt(url.length-1) != "/" ) {
	        url += "/";	
	    }
	    return url;
	};
}
