// objects.js

// This file defines all the objects used globaly throughout the IMS Viewer Application.
// This is to ensure that Objects are available to all files that need them.

// A generic Point location
function Point(inX, inY){
	this.x = inX || 0;
	this.y = inY || 0;
}

// Describes the location of a rectangular box.
// The Max values should be larger than the Min values but this is not enforced
function Envelope(inMinX, inMinY, inMaxX, inMaxY) {
	this.minx = inMinX || 0;
	this.miny = inMinY || 0;
	this.maxx = inMaxX || this.minx;
	this.maxy = inMaxY || this.miny;
}

// Describes a Dataset as returned from the AXL Files within a Layer Attribute
function Dataset(){
 	this.name;
	this.type;
	this.workspace;
}

/* A Layer Object.
 * Holds all the data that the Layer Arrays (above) can.
 * This is to make way for a rewrite of the Layer hadeling code. */
function Layer(){
	// Fields copied from aimsLayers.js
 	this.id;         // axl ID
	this.name;       // axl name
	this.index;      // The Layers position in the array layerList.
	this.isVisible;  // true or false
	this.type;       // can be "image", "acetate" or if a feature layer then point, line, polygon etc.
	this.isFeature;
	this.isDynamic = false;   // If the layer is dynamic it needs special treatment.
	this.extent;     // Envelope Object representing the full extent of this layer.
	this.lableOn;		// true or false
	this.minScale;
	this.maxScale;

	this.selectFilter = '#ALL#';    // List of field names to use for standard Querey Requests (see aimsParams.js)
	this.fieldAliasList;            // Field Aliases (fieldAliasList["sde.field.name1"] = "displayName1";)
	this.whereClause = '';          // If a Layer needs a where clause to differentiate itself from other data in the layer (e.g. Schools) this will link to the entry in the params file.
	// not using the datasets yet.
	this.datasetList = new Array();
	this.datasetCount = 0;
	// Used in the Observatory test viewer
	this.displayByRange = false;		// If the data should be displayed as a dynamic range.
	this.displayFieldName;				// The field to use when displaying data ranges.
	this.quartiles;						// Dynamic ranges are in quartiles. This is an array of Q0 -> Q4 values.


	this.init = function(){
		// search for select Filter list
		if (selFieldList[this.id]) {
			this.selectFilter = selFieldList[this.id];
		}
		if (fieldAliasList[this.id]) {
			this.fieldAliasList = this.getFieldAliasList(fieldAliasList[this.id]);
		}
		if (whereClauseList[this.id]) {
			this.whereClause = whereClauseList[this.id];
		}
	}
	
	this.addField = function(inField){
		if (!this.fieldList)
			this.fieldList = new Array();
		
		this.fieldList.push(inField);
	}
	
	/* Returns an Assocciative array of field aliases.
	 * aliasText format should be "SDE.QUALIFIED.NAME:DisplayName|SDE.QUALIFIED.NAME2:DisplayName2" */
	this.getFieldAliasList = function(aliasText){
		var aliasList = {};
		var fieldList = aliasText.split('|');
		var fieldNameAliasPair;
		
		for (var i=0; i<fieldList.length; i++){
			fieldNameAliasPair = fieldList[i].split(':');
			aliasList[fieldNameAliasPair[0]] = fieldNameAliasPair[1];
		}
		return aliasList;
	}

	this.getImageXML = function(){	// Returns the xml for this layer to be used in an image request.
		var axl = "";
		if (this.isVisible) {
			axl += '<LAYERDEF id="' + this.id + '" visible="true" />\n';
		}else {
			axl += '<LAYERDEF id="' + this.id + '" visible="false" />\n';
		}
		return axl;
	}
}

/* Represents a layers data field. 
 * e.g. Shool layer has NAME, ADDRESS, #ID# and #SHAPE# fields */
function Field(){
	this.name;
	this.type;
	this.size;
	this.precision;
	this.isID = false;
	this.isShape = false;
}

/* UserLocation Holds a number of URL Paramiters */
var userLocation = new function() {
	this.env;	// Starting Extent
	this.loc;	// User Point
	this.id = "userLocation";	
	this.layer = 'os4km';	// The old Webmaps Param. Defaults to 1:50K scale maps.
	this.layers = new Array();	// Array of axlid's for Layers that should be activated when the viewer open.
	this.scale;
	this.label = '';
	this.url = '';
	this.isActive = false;
	this.LayerObj;		// Holds a copy of the Layer Object. To be used with aimsLayers.js once compatible.
	
	this.init = function() {
		this.scale = TileConversion.getTileSize(this.layer);
		var tmpLayer = TileConversion.getEquivLayer(this.layer);
		this.addStartLayer(tmpLayer);
		this.setLayer();
		addLayerObj(this.LayerObj);
	}
	
	/* adds a Layer id to the list of layers that should be visible at the start. */
	this.addStartLayer = function(axlId) {
		this.layers.push(axlId);
	}
	
	this.layerStartsOn = function(axlId){
		for (var i=0; i<this.layers.length; i++){
			if (this.layers[i] == axlId) {
				return true;
			}
		}
		return false;
	}
	
	this.setLayer = function(){
		this.LayerObj = new Layer();
		this.LayerObj.name = this.label || "User Point";
		this.LayerObj.id = this.id;
		this.LayerObj.type = "acetate";
		this.LayerObj.isVisible = true;		// By default assume that users want to see their data
		this.LayerObj.lableOn = true;
		this.LayerObj.minScale = 0;
		this.LayerObj.maxScale = 1.79E308; // This number is from ESRI's code. Not sure why this value
		this.LayerObj.getImageXML = userLocation.getXML;	// Replace the xml generation code.
		this.LayerObj.isDynamic = true;
		return this.LayerObj;
	}
	
	this.setLocation = function(inPoint){
		this.loc = inPoint;
		this.e = inPoint.x;
		this.n = inPoint.y;
		this.isActive = true;
	}
	
	this.setEnvelope = function(inEnv){
		this.env = inEnv;
		this.isActive = true;
	}
		
	this.hasArea = function(){
		if (this.env){
			return true;
		}
		return false;
	}
	
	this.getEnvelope = function(){
		var env =  new Envelope();
		
		if (this.e2 === undefined || this.n2 === undefined) 
		{
			env.minx = env.maxx = this.e;
			env.miny = env.maxy = this.n;
		} 
		else 
		{
			if (this.e <= this.e2) {
				env.minx = this.e;
				env.maxx = this.e2;
			} else {
				env.minx = this.e2;
				env.maxx = this.e;
			}
			
			if (this.n <= this.n2) {
				env.miny = this.n;
				env.maxy = this.n2;
			} else {
				env.miny = this.n2;
				env.maxy = this.n;
			}
		}
		return env;
	}
	
	/* This is only used from the Layer Object, hence 'this' refers to a Layer Object not a userLocation. */
	this.getXML = function() {
		var axl = '';
		var labelPoint;
		var symbolPoint;
		
		if (userLocation.isActive) {
			if (userLocation.label != '') {
				if (userLocation.hasArea()) {
					symbolPoint = getCenterPoint(userLocation.env);
					labelPoint = new Point(symbolPoint.x + 10, symbolPoint.y + 10);
				}
				else {
					symbolPoint = userLocation.loc;
					labelPoint = new Point(userLocation.loc.x + 10, userLocation.loc.y + 10);
				}
				
				axl =  '<LAYER type="acetate" name="' + userLocation.label + '" id="' + userLocation.id + '">\n';
				axl += '    <OBJECT units="database">\n';
				axl += '        <POINT coords="' + symbolPoint.x + ' ' + symbolPoint.y + '">\n';
				axl += '            <SIMPLEMARKERSYMBOL color="255,0,0" outline="255,0,0" overlap="false" type="star" width="15" />\n';
				axl += '        </POINT>\n';
				axl += '    </OBJECT>\n';
				axl += '    <OBJECT units="database">\n';
				axl += '        <TEXT coords="' + labelPoint.x + ' ' + labelPoint.y + '" label="' + userLocation.label + '">\n';
				axl += '            <TEXTMARKERSYMBOL outline="255,255,0" font="Arial" fontsize="12" fontstyle="bold" /></TEXT>\n';
				axl += '    </OBJECT>\n';
				axl += '</LAYER>\n';
			}
		}
		return axl;
	}

}


function Attribute(){
	this.name;
	this.value;
}

function Feature(){
	this.pos = new Envelope;
	this.attributeList = new Array();	// array of attributes 
	this.attCount = 0;
}

function WebMapLayer(inName, inSize, inFactor, inDesiredscale, inAxlId){
	this.name = inName;
	this.tileSize = inSize;
	this.factor = inFactor;
	this.scale = inDesiredscale || 50000;
	this.layerId = inAxlId || "";	// The axl id of the replacment layer.
}

/* This object is a reference of the map layers used in the old WebMaps program and their conversion factors
 * By taking the name of the old WebMaps layer you can use this to find the scale it should be sown at.
 */
var TileConversion = new function (){
	// conversionRatio is the ratio of scale/pixels in order to make the map look nice
	// eg 10000 * conversionRatio = 2.4 which is the nuber of co-ordiantes per pixel for the desired scale.
	// if the viewer window is 800 pixel wide the xMin & xMax would have a difference of 2.4 * 800 = 1920
	var conversionRatio = 0.00024;
	var maxLimit = 250000;	// Any scale at or above this will be set to the full extent of the map
	var theLayer = new Array(
		// Headings		'layerName'	  tileSize  factor   scale axlId
		// Aerial Photography
		new WebMapLayer('aerial20km', 10000, 10000, 150000, "aerial"),
		new WebMapLayer('aerial10km', 5000,  1000,  100000, "aerial"),
		new WebMapLayer('aerial4km',  2000,  1000,  50000,  "aerial"),
		new WebMapLayer('aerial2km',  1000,  100,   10000,  "aerial"),
		new WebMapLayer('aerial800m', 475,   100,   5000,   "aerial"),
		// Eu & UK wide maps
		new WebMapLayer('eu2000km', 1000000, 1000000, 250000),
		new WebMapLayer('uk1000km', 500000,  100000,  250000),
		new WebMapLayer('uk400km',  200000,  100000,  250000),
		new WebMapLayer('uk100km',  50000,   10000,   250000),
		// Hertfordshire districts, towns, Main Roads etc
		new WebMapLayer('districts', 25000, 10000, 250000),
		new WebMapLayer('herts50km', 25000, 10000, 200000),
		new WebMapLayer('herts20km', 10000, 10000, 150000),
		new WebMapLayer('herts10km', 5000,  1000, 100000),
		// Herts Electorial Division 
		new WebMapLayer('el50km', 25000, 10000, 200000, "council"),
		new WebMapLayer('el20km', 10000, 10000, 150000, "council"),
		new WebMapLayer('el10km', 5000,  1000,  100000, "council"),
		new WebMapLayer('el4km',  2000,  1000,  50000,  "council"),
		// OS maps
		new WebMapLayer('os4km',  2000, 1000, 50000),	// 1:50k scale maps
		new WebMapLayer('os1km',  500,  100,  10000),	// 1:10k
		new WebMapLayer('os500m', 250,  100,  5000),	// 1:10k
		// Salt Routs
		new WebMapLayer('salting4km', 2000, 1000, 50000, "salting"),
		new WebMapLayer('salting1km', 500, 100,   10000, "salting"),
		// Street Level Map 
		new WebMapLayer('sm2km',  1000, 100, 25000),
		new WebMapLayer('sm1km',  500,  100, 10000),
		new WebMapLayer('sm500m', 250,  100, 5000)
	);
	
	/* Returns the WebMapLayer object with the name equale to the given name */
	this.findByName = function(inName){
		var i;	
		for(i in theLayer){
			if(theLayer[i].name == inName) 
				return theLayer[i];
		}
		return null;
	}
	
	this.getScaleRatio = function(inName){
		var theWmLyr = this.findByName(inName);
		var theScale = theWmLyr.scale;
		if (theScale > 250000) theScale = 250000;
		var theRatio = theScale * this.conversionRatio;
		return theRatio;
	}
	
	this.getTileSize = function(inName){
		var theWmLyr = this.findByName(inName);
		return theWmLyr.tileSize;
	}
	
	this.getEquivLayer = function(inName){
		var theWmLyr = this.findByName(inName);
		if (theWmLyr.layerId != ""){
			return theWmLyr.layerId;
		} else return null;
	}
	
	
}

