var single_notify_debug = false;

var EPSG4326 = new OpenLayers.Projection("EPSG:4326");
var EPSG900913 = new OpenLayers.Projection("EPSG:900913");
var map; //complex object of type OpenLayers.Map

//Data
var legendDataStore;
var colourDataStore;
var layerDataStore;

//Components whose state we need to track
var layerGrid;
var reverseColoursVal = false;
var colourSliderVal = 8;
var colourGridVal = "cb|Blues";

//Other components that can be updated.
var reverseColoursButton;
var confirmButton;
var colourButtonPanel;
var colourSliderPanel;
var colourGrid;
var legendInfoPanel;
var legendGrid;
var legendDescriptionPanel;
//var titleWin;

//Lookups
var colourKeyToI = new Array();
var colourRGBKeyToI = new Array();

//Defaults
var contextOpacityValue = 100;
var choroplethOpacityValue = 70;
var choroplethOpacitySetup = false;

var currentlySelectedTable;
var currentlySelectedNode;
var currentlySelectedBaseId = "";
var currentlySelectedColour = "";
var bin_type = "";

var startingGroupID ="Health";
var startingTableID = "UV020";
var startingNodeID = "UV0200002";

OpenLayers.Util.onImageLoadError = function() 
{
    	if (this.src != null && this.src.match(/tiles1/)) 
    	{
		this.src = this.src.replace("tiles1", "tiler1");
		//Replace tiler3 with tiler2 when tiler2 working.
    	} 
    	else 
    	{	
		this.src = "http://censusprofiler.org/images/404.png";
	}
};

function init() 
{
	//TODO - Show a splash here while loading (IE takes several seconds to load.)
	map = new OpenLayers.Map ("map", 
	{
	    	projection: EPSG900913,
	   	displayProject: EPSG4326,
		units: "m",
		numZoomLevels: 19,
		maxResolution: 156543.0339,
		maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
		restrictedExtent: new OpenLayers.Bounds(-35, 40, 30, 68).transform(EPSG4326, EPSG900913),
    	    	controls:[
	                new OpenLayers.Control.Navigation(),
	                new OpenLayers.Control.PanZoomBar(),
			new OpenLayers.Control.Attribution(),
			new OpenLayers.Control.MouseDefaults()
		]
   	});

	layerBlank = new OpenLayers.Layer.OSM("Blank", 
		"http://censusprofiler.org/images/darkgrey.png", 
		{numZoomLevels: 17, transitionEffect: "resize", isBaseLayer: true, attribution: ""});
	layerAerial = new OpenLayers.Layer.Google("Aerial Imagery", 
		{numZoomLevels: 17, type: G_SATELLITE_MAP, sphericalMercator: true, attribution: "Imagery copyright Google and partners"});
	layerChoro = new OpenLayers.Layer.OSM("Choropleth", 
		"http://censusprofiler.org/images/darkgrey.png", 
		{numZoomLevels: 19, transitionEffect: "resize", isBaseLayer: false, attribution: "Data copyright ONS"});
	layerContext = new OpenLayers.Layer.OSM("Context", 
                "http://tiles1.censusprofiler.org/context/${z}/${x}/${y}.png",
		{numZoomLevels: 19, transitionEffect: "none", isBaseLayer: false, attribution: "Map data CC-By-SA OpenStreetMap"});

 	layerChoro.setVisibility(true);
	layerContext.setVisibility(true);

        map.addLayers([layerBlank, layerAerial, layerChoro, layerContext ]);
	map.setCenter(new OpenLayers.LonLat(-2, 54.5).transform(EPSG4326, EPSG900913), 6);

	//Set up control defaults.
	//document.getElementById('arealunitS').disabled = true; //TODO Make this selectable once safeguards are built in.
	//document.getElementById('suggestcoloursgroupsC').disabled = true; //TODO Reimplement.
	//document.getElementById('arealunitS').value = "mlo";

	for (var i = 0; i < colours.length; i++)
	{
		var currKey = colours[i][1] + "|" + colours[i][2];
		colourKeyToI[currKey] = i;
	}

	for (var i = 0; i < colour_rgbs.length; i++)
	{
		var currKey = colour_rgbs[i][0] + "|" + colour_rgbs[i][1] + "|" + colour_rgbs[i][2] + "|" + colour_rgbs[i][3];
		colourRGBKeyToI[currKey] = i;
	}
	
	for (var i = 0; i < uv_tables.length; i++)
	{
		var id = uv_tables[i]['TabName'];
		var text = uv_tables[i]['Description']

		if (id.substring(0, 2) == "UV")
		{
			text = text.substring(11);
			text = text.toLowerCase();
			text = text.toTitleCase();
		}
		document.getElementById("uv_tables_fr").options[i] = new Option(text, id);
	}
	
	Ext.onReady(function() { 
		setupColourData();
		setupColourPanel();
		showColourPanel();

		setupLegendData();
		setupLegendPanel();
		showLegendPanel();

		//showTitlePanel();

		setupLayerData();
		setupLayerPanel();
		showLayerPanel();

		drawColours();

		contextopacitySE = new Ext.slider.SingleSlider({
			renderTo: 'contextopacityS',
			width: 100,
			value: contextOpacityValue,
			minValue: 0,
			maxValue: 100,
			plugins: new Ext.slider.Tip()
		});
		contextopacitySE.on('change', function() {
			contextOpacityValue = contextopacitySE.getValue();
			changeContextopacity()
		});
		/*
		Ext.MessageBox.show({
			title: 'CensusGIV',
			msg: 'Welcome to CensusGIV, the website which allows you to quickly and easily create maps of the 2001 census in England and Wales. ' + 
				'CensusGIV was built by University College London with a grant from the Economic and Social Research Council.<br /><br />' + 
				'Use the bar at the top to choose your data and colour scheme. The bar at the bottom helps you fine-tune the look of the map.',
			buttons: Ext.MessageBox.OK,
			animEl: 'dynamicLayerSelector',
			icon: Ext.MessageBox.INFO,
			fn: showTopPanel
		});
		*/	
		map.events.register("move", null, drawLegend);
		//TODO Hire the "loading" splash at this point.
	});
}           

function showTopPanel()
{	
	var tabs = new Ext.TabPanel({
		region: 'center',
		activeTab: 0,
		border: false,
		defaults: {autoScroll:true},
		items: [{
			title: 'Topic List',
			html: 'Hello',
			border: false,
			margins:'3 3 3 3'
		},{
			title: 'Word Cloud',
			html: 'TBA',
			border: false,
			margins:'3 3 3 3'
		}]
	});

	var win = new Ext.Window({
		renderTo: 'topPanel',
		title: 'Map Chooser',
		closable: false,
		collapsible: true,
		width: 600,
		height: 100,
		layout: 'border',
		items: [tabs]
	});

	win.show(this);

}

function refreshDynamicLayer()
{
	var iC = colourKeyToI[colourGridVal];
	var bin_size = colourSliderVal;
	
	var sendLayerArealUnit = "mlo";
	//var overrideArealUnit = document.getElementById('arealunitS').value;
	var overrideArealUnit = "mlo";

	if (overrideArealUnit != "mlo")
	{
		sendLayerArealUnit = overrideArealUnit;
	}

	if (reverseColoursVal) { reversecolour_py = '1'; } else { reversecolour_py = '0'; }

	currentlySelectedBaseId = "";
	if (currentlySelectedNode.id.substring(0, 2) == "UV")
	{
		currentlySelectedBaseId = currentlySelectedNode.id.substring(0, 5) + "0001";
	}

	var currSelectedNodeValues = uv_dictionary[currentlySelectedTable.id][currentlySelectedNode.id]

	//TODO: Replace with a more sophisticated (and user-selectable) model here.
	if (currSelectedNodeValues[7] - (currSelectedNodeValues[8]*1) < 0)
	{
		bin_type = "geometric_small";
	}
	else if (currSelectedNodeValues[7] + (currSelectedNodeValues[8]*1) > 1)
	{
		bin_type = "geometric_large";
	}
	else
	{
		bin_type = "standard_dev";
	}
	
	var currentlySelectedTablePrefix = currentlySelectedTable.id;
	if (currentlySelectedTable.id.substring(0, 2) == "UV")
	{
		currentlySelectedTablePrefix = "uv_";
	}
	else if (currentlySelectedTable.id == "uk_oac")
	{
		bin_type = "classification_qual";
		sendLayerArealUnit = "oa";
	}
	else if (currentlySelectedTable.id == "eng_imd_2007")
	{
		bin_type = "classification_quan";		
		sendLayerArealUnit = "lsoa";
	}
	else if (currentlySelectedTable.id == "pop_den_uv02_")
	{
		bin_type = "rank"
	}

	//Chorogen requires msoa_avg,lsoa_avg,oa_avg,msoa_sd,lsoa_sd,oa_sd
	//CETL DB has oa_avg,oa_sd,lsoa_avg,lsoa_sd,msoa_avg,msoa_sd
	//We translate between the two here.

	urlStr = bin_type + "|"
		+ currentlySelectedTablePrefix + "|"
		+ sendLayerArealUnit + "|" 
		+ currentlySelectedNode.id + "|" 
		+ currentlySelectedBaseId + "|"
		+ currSelectedNodeValues[7] + "|" 
		+ currSelectedNodeValues[5] + "|"  
		+ currSelectedNodeValues[3] + "|"  
		+ currSelectedNodeValues[8] + "|"  
		+ currSelectedNodeValues[6] + "|"  
		+ currSelectedNodeValues[4] + "|"  
		+ colours[iC][1] + "|" 
		+ colours[iC][2] + "|" 
		+ "na_1" + "|"
		+ bin_size + "|"
		+ reversecolour_py;
	
	layerChoro.url = "http://tiler1.censusprofiler.org/," + urlStr + "/${z}/${x}/${y}.png";
	layerChoro.redraw();
	drawLegend();
}

function colourBlockRenderer(val, metaData)
{
	return "<div style='background-color: " + val + "'>&nbsp;</div>";
}

function colourBlockEdgeRenderer(val, metaData)
{
	metaData.css = 'extjs-cell-to-edge';
	metaData.attr = 'style="padding: 0; font-size: 10px;"';
	return "<div style='background-color: " + val + "'>&nbsp;</div>";
}


function percentRenderer(val)
{
	if (val < 0.001 && val > 0)
	{
		return Math.round(val*100000)/1000 + "%"
	}
	if (val < 0.01 && val > 0)
	{
		return Math.round(val*10000)/100 + "%"
	}
	if (Math.round(val*1000)/10 == Math.round(val*100))
	{
		return Math.round(val*100) + "%"
	}
	return Math.round(val*1000)/10 + "%"
}

function tooltipRenderer(val)
{
	if (val.indexOf('|') < 0)
	{
		return val;
	}
	var parts = val.split("|");
	return "<span title='[" + parts[0].toUpperCase() + "] " + parts[1] + "'>" + parts[0] + "</span>";
}

function setupLayerData()
{
}


function setupColourData()
{
	colourDataStore = new Ext.data.ArrayStore({
		fields: [
			{name: 'seq_id', id: 'seq_id', type: 'integer'},
			{name: 'seq_name', id: 'seq_name'},
			{name: 'nice_name', id: 'nice_name'},
			{name: 'c1', id: 'c1'},
			{name: 'c2', id: 'c2'},
			{name: 'c3', id: 'c3'},
			{name: 'c4', id: 'c4'},
			{name: 'c5', id: 'c5'},
			{name: 'c6', id: 'c6'},
			{name: 'c7', id: 'c7'},
			{name: 'c8', id: 'c8'},
			{name: 'c9', id: 'c9'}
		],
		sortInfo: { field: 'seq_id', direction: 'ASC'}
	});
}

function setupLegendData()
{
	legendDataStore = new Ext.data.ArrayStore({
		fields: [
			{name: 'seq_id', id: 'seq_id', type: 'integer'},
			{name: 'bin_num', id: 'bin_num', type: 'integer', align: 'middle'},
			{name: 'colourBlock', id: 'colourBlock'},
			{name: 'min', id: 'min', type: 'float'},
			{name: 'max', id: 'max', type: 'float'},
			{name: 'classificationName', id: 'classificationName'}
		],
		sortInfo: { field: 'seq_id', direction: 'DESC'}
	});
}

function setupLayerPanel()
{
	var allNodes = new Array();
	for(var groupkey in uv_tables)
	{
		var group = new Object();
		group.id = groupkey;
		group.name = groupkey;
		group.htmlname = "<div style='float: right; width: 170px;'>" + groupkey + "</div>";

		group.singleClickExpand = true;
		if (groupkey == startingGroupID)
		{
			group.expanded = true;
		}
		var groupchildren = new Array();
		for (var tablekey in uv_tables[groupkey])
		{
			var table = new Object();
			var name = uv_tables[groupkey][tablekey][1];
		
			if (tablekey.substring(0, 2) == "UV")
			{
				name = name.substring(11);
				name = name.toLowerCase();
				name = name.toTitleCase();
				name = name.replace("Ns-Sec", "NS-SEC");
				name = name.replace(" (Scotland)", "");
				name = name.replace(" (England and Wales)", "");
			}

			table.id = tablekey;
			table.name = name;
			table.htmlname = "<div style='float: right; width: 155px;'><a href='#' title='" ;

			var metadata = "Table ID: " + tablekey + ". ";

			var caveats = uv_tables[groupkey][tablekey][6];
			if (caveats != null && caveats != "")
			{

				caveats = caveats.replace(/'/g, "*");
				metadata += caveats;
			}
			table.htmlname += metadata + "'>" + name;

			//QQQ Check Country of Birth quotes and Multiple Ethnic Groups quotes.

			var coverage = new Array();
			var england = uv_tables[groupkey][tablekey][3] == 1;
			var scotland = uv_tables[groupkey][tablekey][4] == 1;
			var wales = uv_tables[groupkey][tablekey][5] == 1;
			if (england && scotland && wales) { coverage = "Britain"; }
			else if (england && wales) { coverage = "E&W";	}
			else if (england) { coverage = "Eng"; }
			else if (scotland) { coverage = "Sco"; }
			else if (wales)	{ coverage = "Wal"; }
			if (tablekey == "uk_oac") { coverage = "U.K."; }
			
			if (coverage != "Britain" && coverage != "U.K.")
			{			
				table.htmlname += " (" + coverage + ")";
			}
			
			table.htmlname += "</a> <a href='#' onclick='alert(\"" + metadata + "\")'><img src='images/q.png' width='9' height='9' alt= '?' /></a></div>";
				
			table.singleClickExpand = true;
			if (tablekey == startingTableID)
			{
				table.expanded = true;
			}
			var tablechildren = new Array();
			for (var metrickey in uv_dictionary[tablekey])
			{
				if (metrickey.substring(metrickey.length - 4) != "0001")
				{
					var metric = new Object();
					var name = uv_dictionary[tablekey][metrickey][1];
					name = name.replace("_", " ");
					name = name.toLowerCase();
					name = name.toTitleCase();
					name = name.replace("Eu ", "EU ");
					name = name.replace(" Ussr", " USSR");
					name = name.replace(" Uk", " UK");
					name = name.replace("Uk ", "UK ");
					name = name.replace(" and ", " & ");
					name = name.replace("Oac", "OAC");
					metric.name = name;
					metric.htmlname = "<div style='float: right; width: 140px;'>" + name + "</div>";
					metric.id = metrickey;
					if (tablekey.substring(0, 2) == "UV")
					{
						var base = uv_dictionary[tablekey][tablekey + "0001"][1];
						base = base.replace("_", " ");
						base = base.toLowerCase();
						metric.base = base; 
					}
					metric.leaf = true;
					tablechildren.push(metric);
				}
			}
			table.children = tablechildren;
			groupchildren.push(table);						
		}
		group.children = groupchildren;
		allNodes.push(group);
	}

	var rootNode = new Ext.tree.AsyncTreeNode({
			id: 'rootnode',
			children: allNodes,
			singleClickExpand: true
		});

	var treeLoader = new Ext.tree.TreeLoader();
	
	layerGrid = new Ext.ux.tree.TreeGrid({
		width: 225,
		height: 400,
		enableDD: false,
		tbar: false,
		header: false,
		columns:[{ 
			header: 'Table',
			dataIndex: 'htmlname',
			width: 210,
			resizable: false,
			fixed: true,
			id: 'layername'

		}],
		animate: true,
		loader: treeLoader,
		lines: true,
		rootVisible: false,
		singleExpand: true,
		listeners: { 
			collapsenode: function(nd) {
				nd.collapseChildNodes(true); },
			beforeexpandnode: function(nd) {
				//nd.getUI().getEl().scrollIntoView();
			} 
		}
	});

	new Ext.tree.TreeSorter(layerGrid, {
		folderSort: true,
		dir: "asc",
		sortType: function(node) { return node.id; }});

	layerGrid.setRootNode(rootNode);
	layerGrid.getSelectionModel().on('selectionchange', 
		function(sel, nd) 
		{ 
			if (nd.isLeaf()) 
			{ 
				currentlySelectedNode = nd; 
				currentlySelectedTable = nd.parentNode;
				if (currentlySelectedTable.id == "uk_oac")
				{
					colourSliderPanel.setMinValue(-1);
					colourSliderPanel.setMaxValue(11);
					colourSliderPanel.setValue(7);
					colourSliderPanel.disable();
					colourSliderVal = 7;
					redrawColours(); 
					confirmColours();
				}
				else
				{
					if (colourSliderPanel.minValue == -1)
					{
						colourSliderPanel.setMinValue(3);
						colourSliderPanel.setMaxValue(9);
						colourSliderPanel.setValue(8);
						colourSliderPanel.enable();
						colourSliderVal = 8;
						redrawColours();
						confirmColours();
					}
					
				}
				//titleWin.update(nd.parentNode.attributes.name + ": " + nd.attributes.name);
				//titleWin.doLayout();
				document.getElementById("titlePanel").innerHTML = nd.parentNode.attributes.name + ": " + nd.attributes.name;
				refreshDynamicLayer(); 
			} 
			else
			{
				//titleWin.update("Please choose a measure...");
				document.getElementById("titlePanel").innerHTML = "Please choose a measure...";
				currentlySelectedTable = nd;
			}
		} );

	//Through very laborious debugging, I have determined that it is necessary to:
	//(a) have the nodes specified as expanded below, and manually expand them in sequence here,
	// and (b) select through the grid and the path, rather than selecting the node directly.
	//Only then can the (async loaded) node be selected.
	//Simplify this function at your peril!
	rootNode.on('load', function() { 
		var startingCategory = layerGrid.getNodeById(startingGroupID); 
		startingCategory.expand(); 
		var startingTable = layerGrid.getNodeById(startingTableID); 
		startingTable.expand(); 
		var startingNode = layerGrid.getNodeById(startingNodeID); 
		layerGrid.selectPath(startingNode.getPath());
	});
}

function setupColourPanel()
{
	colourSliderTitle = new Ext.BoxComponent({
		autoHeight: true,
                html: "<div style='padding: 3px 0 0 3px;'>No. of intervals:</div>"
            });


	//Do not specify height here - breaks IE8.
	colourSliderPanel = new Ext.slider.SingleSlider({
		minValue: 3,
		value: 8,
	        maxValue: 9,
		increment: 1,
	        plugins: new Ext.slider.Tip(),
		listeners: {
			'change': function() { redrawColours(); }
		}
	    });

	colourSliderKey = new Ext.BoxComponent({
		autoHeight: true,
                html: "<div style='letter-spacing:20px; margin: 0px 0px 5px 5px; color: #666; '>3456789</div>"	
            });

	reverseColoursButton = new Ext.Button({
		columnWidth: .6,
		enableToggle: true,
		text: "Flip colour order",
		handler: function() { redrawColours(); }
	});

	confirmButton = new Ext.Button({
		columnWidth: .4,
		disabled: true,
		text: "<span style='font-weight: bold; color: red'>Apply</span>",
		handler: function() { confirmColours(); }
	});

	colourButtonPanel = new Ext.Panel({
		items: [reverseColoursButton, confirmButton],
		layout: 'column'
	});

	colourGrid = new Ext.grid.GridPanel({
		store: colourDataStore,
		colModel: new Ext.grid.ColumnModel({
			defaults: {
				width: 13,
				renderer: colourBlockEdgeRenderer
			},
			columns: [
				{dataIndex: 'seq_id', hidden: true},
				{dataIndex: 'seq_name', hidden: true},
				{dataIndex: 'nice_name', hidden: true},
				{dataIndex: 'c1', id: 'c1'},
				{dataIndex: 'c2', id: 'c2'},
				{dataIndex: 'c3', id: 'c3'},
				{dataIndex: 'c4', id: 'c4'},
				{dataIndex: 'c5', id: 'c5'},
				{dataIndex: 'c6', id: 'c6'},
				{dataIndex: 'c7', id: 'c7'},
				{dataIndex: 'c8', id: 'c8'},
				{dataIndex: 'c9', id: 'c9'}
			]
		}),


		selectionModel: new Ext.grid.RowSelectionModel({ singleSelect: true }),
		viewConfig: {
			autoFill: true,
			forceFit: true
		},
		tbar: false,
		disableSelection: false,	
		enableHdMenu: false,
		enableColumnMove: false,
		enableColumnResize: false,  
		draggable: false,
		height: 200,
		width: 165,
		hideHeaders: true,
		minColumnWidth: 13
	});
	colourGrid.getSelectionModel().addListener("rowselect", function() { confirmButton.enable(); }); 

}	

function confirmColours()
{	
	confirmButton.disable();
	colourSliderVal = colourSliderPanel.getValue();
	reverseColoursVal = reverseColoursButton.pressed;
	if (!colourGrid.getSelectionModel().hasSelection())
	{
		colourGrid.getSelectionModel().selectFirstRow();
	}
	colourGridVal = colourGrid.getSelectionModel().getSelected().get('seq_name');
	refreshDynamicLayer();
 
}

function setupLegendPanel()
{
	legendInfoPanel = new Ext.BoxComponent({
		autoHeight: true,
		cls: 'panelCentered',
                html: ""
            });

	legendDescriptionPanel = new Ext.BoxComponent({
		autoHeight: true,
		cls: 'panelCentered',
                html: ""
            });

	legendGrid = new Ext.grid.GridPanel({
		store: legendDataStore,
		columns: [
			{dataIndex: 'seq_id', hidden: true},
			{width: 50, renderer: colourBlockRenderer, header: '&nbsp;', dataIndex: 'colourBlock'},
			{width: 20, header: '#', dataIndex: 'bin_num', id: 'bin_num', hidden: true},
			{width: 50, renderer: percentRenderer, header: 'From', dataIndex: 'min', id: 'min'},
			{width: 50, renderer: percentRenderer, header: 'To', dataIndex: 'max', id: 'max'},
			{width: 80, renderer: tooltipRenderer, header: 'Classification', dataIndex: 'classificationName', id: 'classificationName', hidden: true}
		],
		tbar: false,
		disableSelection: true,	
		enableHdMenu: false,
		enableColumnMove: false,
		draggable: false,
		autoHeight: true
	});
}

//Don't call doAnchor on any of these panels, after showing them - causes problems in IE8.
function showTitlePanel()
{
	titleWin = new Ext.Window({
		renderTo: Ext.getBody(),
		hideBorders: true,
		resizable: true,
		closable: false,
		shadow: false,
		layout: 'fit',
		padding: 5,
		border: false,
		width: 490,
		autoHeight: true,
		bodyCssClass: 'title',
		html: "<br />"
	}); 

	titleWin.anchorTo(document.body, 'tl-tl?', [50, 100]);
	titleWin.show(this);
}

//Don't call doAnchor on any of these panels, after showing them - causes problems in IE8.
function showLayerPanel()
{
	var win = new Ext.Window({
		title: 'Themes',
		renderTo: Ext.getBody(),
		hideBorders: true,
		closable: false,
		collapsible: true,
		collapsed: true,
		expandOnShow: true,
		width: 245,
		resizable: true,
		shadow: false,
		layout: 'fit',
		items: [layerGrid]
	});

	win.anchorTo(document.body, 'tl-tl?', [50, 126]);
	win.show(this);
}

function showColourPanel()
{	
	var win = new Ext.Window({
		title: 'Classification & Colours',
		renderTo: Ext.getBody(),
		hideBorders: true,
		closable: false,
		collapsible: true,
		collapsed: true,
		expandOnShow: true,
		resizable: false,
		shadow: false,
		layout: 'anchor',
		width: 185,
		items: [colourSliderTitle, colourSliderPanel, colourSliderKey, colourButtonPanel, colourGrid]
	});

	win.anchorTo(document.body, 'tr-tr?', [-225, 10]);
	win.show(this);
}

function showLegendPanel()
{
	var win = new Ext.Window({
		title: 'Legend',
		renderTo: Ext.getBody(),
		closable: false,
		collapsible: true,
		layout: 'fit',
		hideBorders: true,
		resizable: false,
		width: 185,
		shadow: false,
		items: [legendInfoPanel, legendGrid, legendDescriptionPanel]
	});


	win.anchorTo(document.body, 'tr-tr?', [-30, 10]);
	win.show(this);
}

//This function must capture any current selection and restore it at the end.
function redrawColours()
{
	var selectionHasBeenMade = false;
	if (colourGrid.getSelectionModel().hasSelection())
	{
		selectionHasBeenMade = true;
		currentlySelectedColour = colourGrid.getSelectionModel().getSelected().data['seq_name'];
	}

	//TODO use pre-created colourData arrays rather than rebuilding from scratch each time - should work much faster.
	//Only need to build from scratch for the initial call.
	drawColours();

	if (selectionHasBeenMade)
	{
		for (var i = 0; i < colourDataStore.data.items.length; i++)
		{
			if (colourDataStore.data.items[i]['json'][1] == currentlySelectedColour)
			{
				colourGrid.getSelectionModel().selectRecords([colourDataStore.data.items[i]]);
			}
		}
	}
	else
	{
		colourGrid.getSelectionModel().selectFirstRow();
	}
	confirmButton.enable();
}

//Called to first create the colour data.
function drawColours()
{
	var qualitative = false;
	if (currentlySelectedTable.id == "uk_oac")
	{
		qualitative = true;
	}

	var colourData = new Array();
	var selected_bin_size = colourSliderPanel.getValue();
	for (var i = 0; i < colours.length; i++)
	{
		if ((qualitative && colours[i][5] == "qual") || (!qualitative && colours[i][5] != "qual"))
		{
			var newColourV = colours[i][1] + "|" + colours[i][2];
			var iC = colourKeyToI[newColourV];
			var bin_size_max = parseInt(colours[iC][4]);
			if (bin_size_max >= selected_bin_size)
			{
				var colourDataCurr = new Array();
				if (!reverseColoursButton.pressed)
				{
					colourDataCurr.push(i);
					colourDataCurr.push(newColourV);
					colourDataCurr.push(colours[i][0]);
				}
				for (var curr_bin = 1; curr_bin <= selected_bin_size; curr_bin++)
				{
					iCV = colourRGBKeyToI[colours[iC][1] + "|" + colours[iC][2] + "|" + selected_bin_size + "|" + curr_bin];
					colourDataCurr.push("rgb(" + parseInt(colour_rgbs[iCV][4]) + ", " + parseInt(colour_rgbs[iCV][5]) + "," + parseInt(colour_rgbs[iCV][6]) + ")");				
				}
				if (reverseColoursButton.pressed)
				{
					colourDataCurr.push(colours[i][0]);
					colourDataCurr.push(newColourV);
					colourDataCurr.push(i);
					colourDataCurr.reverse();
				}
				colourData.push(colourDataCurr);
			}
		}
	}

	colourDataStore.loadData(colourData);
}

function drawLegend()
{
	var iC = colourKeyToI[colourGridVal];
	var bin_size = colourSliderVal;

	var layerArealUnit = "mlo";
	//var overrideArealUnit = document.getElementById('arealunitS').value;
	var overrideArealUnit = "mlo";

	if (overrideArealUnit != "mlo")
	{
		layerArealUnit = overrideArealUnit;
	} 
	else if (map.getZoom() < 11)
	{
		layerArealUnit = "msoa";
	}
	else if (map.getZoom() < 14)
	{
		layerArealUnit = "lsoa";
	}
	else
	{
		layerArealUnit = "oa";
	}

	//TODO If OAC or IMD, use oa or lsoa (only, and always - disabled selector).

	var areaSize = (layerArealUnit == "msoa" ? "large" : (layerArealUnit == "lsoa" ? "medium" : (layerArealUnit == "oa" ? "small" : "")));
	if (bin_type == "classification_qual" || bin_type == "classification_quan")
	{
		areaSize = "classification";
		//document.getElementById('arealunitS').disabled = true;
	}
	else
	{
		//document.getElementById('arealunitS').disabled = false;

	}
	
	var val1 = 0;
	var val2 = 0;
	if (layerArealUnit == "oa")
	{
		val1 = uv_dictionary[currentlySelectedTable.id][currentlySelectedNode.id][3];
		val2 = uv_dictionary[currentlySelectedTable.id][currentlySelectedNode.id][4];
	}
	else if (layerArealUnit == "lsoa")
	{
		val1 = uv_dictionary[currentlySelectedTable.id][currentlySelectedNode.id][5];
		val2 = uv_dictionary[currentlySelectedTable.id][currentlySelectedNode.id][6];
	}
	else if (layerArealUnit == "msoa")
	{
		val1 = uv_dictionary[currentlySelectedTable.id][currentlySelectedNode.id][7];
		val2 = uv_dictionary[currentlySelectedTable.id][currentlySelectedNode.id][8];
	}

	var intervals = new Array();

	intervals["classification_quan"] = [
		[1/3, 2/3],
		[1/4, 2/4, 3/4],
		[1/5, 2/5, 3/5, 4/5],
		[1/6, 2/6, 3/6, 4/6, 5/6],
		[1/7, 2/7, 3/7, 4/7, 5/7, 6/7],
		[1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8],
		[1/9, 2/9, 3/9, 4/9, 5/9, 6/9, 7/9, 8/9],
		[1/10, 2/10, 3/10, 4/10, 5/10, 6/10, 7/10, 8/10, 9/10],
		[1/11, 2/11, 3/11, 4/11, 5/11, 6/11, 7/11, 8/11, 9/11, 10/11]]

	intervals["rank"] = [
		[1/3, 2/3],
		[1/4, 2/4, 3/4],
		[1/5, 2/5, 3/5, 4/5],
		[1/6, 2/6, 3/6, 4/6, 5/6],
		[1/7, 2/7, 3/7, 4/7, 5/7, 6/7],
		[1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8],
		[1/9, 2/9, 3/9, 4/9, 5/9, 6/9, 7/9, 8/9],
		[1/10, 2/10, 3/10, 4/10, 5/10, 6/10, 7/10, 8/10, 9/10],
		[1/11, 2/11, 3/11, 4/11, 5/11, 6/11, 7/11, 8/11, 9/11, 10/11]]

	intervals["standard_dev"] = [
		[val1-0.5*val2, val1+0.5*val2], 
		[val1-val2, val1, val1+val2], 
		[val1-1.5*val2, val1-0.5*val2, val1+0.5*val2, val1+1.5*val2], 
		[val1-2*val2, val1-val2, val1, val1+val2, val1+2*val2],
		[val1-1.25*val2, val1-0.75*val2, val1-0.25*val2, val1+0.25*val2, val1+0.75*val2, val1+1.25*val2],
		[val1-1.5*val2, val1-val2, val1-0.5*val2, val1, val1+0.5*val2, val1+val2, val1+1.5*val2],
		[val1-1.75*val2, val1-1.25*val2, val1-0.75*val2, val1-0.25*val2, val1+0.25*val2, val1+0.75*val2, val1+1.25*val2, val1+1.75*val2],
		[val1-2*val2, val1-1.5*val2, val1-val2, val1-0.5*val2, val1, val1+0.5*val2, val1+val2, val1+1.5*val2, val1+2*val2],
		[val1-2.25*val2, val1-1.75*val2, val1-1.25*val2, val1-0.75*val2, val1-0.25*val2, val1+0.25*val2, val1+0.75*val2, val1+1.25*val2, val1+1.75*val2, val1+2.25*val2]]


	intervals["geometric_small"] = [
		[val1/2, val1*2],
		[val1/2, val1, val1*2],
		[val1/4, val1/2, val1*2, val1*4],
		[val1/4, val1/2, val1, val1*2, val1*4],
		[val1/3, val1/2, val1/1.5, val1*1.5, val1*2, val1*3],
		[val1/3, val1/2, val1/1.5, val1, val1*1.5, val1*2, val1*3],
		[val1/4, val1/3, val1/2, val1/1.5, val1*1.5, val1*2, val1*3, val1*4],
		[val1/4, val1/3, val1/2, val1/1.5, val1, val1*1.5, val1*2, val1*3, val1*4],
		[val1/5, val1/4, val1/3, val1/2, val1/1.5, val1*1.5, val1*2, val1*3, val1*4, val1*5]]

	var val1d = 1-val1;
	intervals["geometric_large"] = [
		[1-val1d*2, 1-val1d/2],
		[1-val1d*2, 1-val1d, 1-val1d/2],
		[1-val1d*4, 1-val1d*2, 1-val1d/2, 1-val1d/4],
		[1-val1d*4, 1-val1d*2, 1-val1d, 1-val1d/2, 1-val1d/4],
		[1-val1d*3, 1-val1d*2, 1-val1d*1.5, 1-val1d/1.5, 1-val1d/2, 1-val1d/3],
		[1-val1d*3, 1-val1d*2, 1-val1d*1.5, 1-val1d, 1-val1d/1.5, 1-val1d/2, 1-val1d/3],
		[1-val1d*4, 1-val1d*3, 1-val1d*2, 1-val1d*1.5, 1-val1d/1.5, 1-val1d/2, 1-val1d/3, 1-val1d/4],
		[1-val1d*4, 1-val1d*3, 1-val1d*2, 1-val1d*1.5, 1-val1d, 1-val1d/1.5, 1-val1d/2, 1-val1d/3, 1-val1d/4],
		[1-val1d*5, 1-val1d*4, 1-val1d*3, 1-val1d*2, 1-val1d*1.5, 1-val1d/1.5, 1-val1d/2, 1-val1d/3, 1-val1d/4, 1-val1d/5]]

	intervals["classification_oac"] = [
		"Blue Collar Communities|Housing in these areas is more likely to be terraced rather than flats and residents mainly rent from the public sector. There is a high proportion of 5-14 year-olds. Residents tend to have fewer higher educational qualifications than the national average. A high proportion work in manufacturing, retail or construction.", 
		"City Living|Residents in these urban areas are more likely to live alone. They are more likely to hold higher educational qualifications and are often first generation immigrants to the UK. Housing is often made up of flats and detached homes are rare and residents typically rent their homes from the private sector.",
		"Countryside|Residents in these rural areas are likely to work from home and to be employed in agriculture or fishing. They often live in detached houses; in households with more than one car. Areas are less densely populated than other parts of the country.",
		"Prospering Suburbs|Residents in these prosperous areas often live in detached houses and less frequently in flats or terraced housing. Fewer residents rent their homes and homes are more likely to have central heating. Households often have access to more than one car.",
		"Constrained by Circumstances|Residents in these less well off areas typically live in flats and rent from the public sector. They are less likely to have higher qualifications. They rarely live in detached houses or in households with more than one car.",
		"Typical Traits|These are areas of terraced housing, where residents are unlikely to rent from the public sector. There are a range of ethnic backgrounds and types of households. Residents work in a range of industries.",
		"Multicultural|Residents in these areas are often non-white, mainly from Asian or Black British backgrounds. Many are first generation immigrants. Housing is mostly rented from the public or private sectors and is often split into flats. The main means of travelling for residents is by public transport.",
	]
	
	var legendData = new Array();

	var bin_size_pos = bin_size - parseInt(colours[iC][3]);

	for (var i = 0; i<bin_size; i++)
	{
		var iCV;
		var curr_bin;
		if (reverseColoursVal)
		{
			curr_bin = bin_size - i;
		}
		else
		{
			curr_bin = i + 1;
		}
		iCV = colourRGBKeyToI[colours[iC][1] + "|" + colours[iC][2] + "|" + bin_size + "|" + curr_bin];

		var min_val = -1;
		var max_val = -1;
		var seq_id;
		var classificationInfo;
		if (bin_type == "classification_qual")
		{
			intervalName = "classification_oac";
			classificationInfo = intervals[intervalName][i];
			seq_id = -i;	
			bin_id = i+1;
		}
		else if (bin_type == "classification_quan")
		{	
			seq_id = -i;
			bin_id = bin_size - i;
		}
		else
		{
			var min_bin_pos = parseInt(i-1)
			var max_bin_pos = i
			min_val = 0;
			max_val = 1;
			if (i > 0)
			{
				min_val = intervals[bin_type][bin_size_pos][min_bin_pos];
			}
			if (i < parseInt(bin_size-1))
			{
				max_val = intervals[bin_type][bin_size_pos][max_bin_pos];
			}	
			if (min_val < 0)
			{
				min_val = 0;
			}		
			if (max_val < 0)
			{
				max_val = 0;
			}		
			if (min_val > 1)
			{
				min_val = 1;
			}
			if (max_val > 1)
			{
				max_val = 1;
			}
			classificationInfo = "";
			seq_id = i;
			bin_id = i+1;
		}		
		legendData.push([seq_id, bin_id, "rgb(" + parseInt(colour_rgbs[iCV][4]) + ", " + parseInt(colour_rgbs[iCV][5]) + "," + parseInt(colour_rgbs[iCV][6]) + ")", min_val, max_val, classificationInfo]);
	}

	var legendBinText;
	if (bin_type == "classification_qual")
	{
		legendGrid.getColumnModel().setHidden(2, false);
		legendGrid.getColumnModel().setHidden(3, true);
		legendGrid.getColumnModel().setHidden(4, true);
		legendGrid.getColumnModel().setHidden(5, false);
		legendBinText = "Hover for classification description.";
	}
	else if (bin_type == "classification_quan" || bin_type == "rank")
	{
		legendGrid.getColumnModel().setHidden(2, false);
		legendGrid.getColumnModel().setHidden(3, true);
		legendGrid.getColumnModel().setHidden(4, true);
		legendGrid.getColumnModel().setHidden(5, true);
		legendBinText = "Equal numbers of areas in each group.";
	}
	else if (bin_type == "standard_dev" || bin_type == "geometric_small" || bin_type == "geometric_large") 
	{
		legendGrid.getColumnModel().setHidden(2, true);
		legendGrid.getColumnModel().setHidden(3, false);
		legendGrid.getColumnModel().setHidden(4, false);
		legendGrid.getColumnModel().setHidden(5, true);
		legendBinText = "Figures are proportion of " + currentlySelectedNode.attributes.base + " in each area.<br /><br />";
		if (bin_type == "standard_dev")
		{
			legendBinText += "Intervals are a number of standard deviations (" + parseInt(val2*1000)/10 + "%) away from an average value of " + parseInt(val1*1000)/10 + "%"; 
		}
		else if (bin_type == "geometric_small")
		{
			legendBinText += "Intervals are geometric multiples (e.g. half, double) either side of an average value of " + parseInt(val1*10000)/100 + "%"; 
		}
		else if (bin_type == "geometric_large")
		{
			legendBinText += "Intervals are geometric multiples of the difference, either side of an average value of " + parseInt(val1*10000)/100 + "%"; 
		}
	}
	legendInfoPanel.update("Showing " + areaSize + " areas.");
	legendDataStore.loadData(legendData);
	legendDescriptionPanel.update(legendBinText);
}

function toggleShowhideaerialimagery()
{
	if (document.getElementById('showhideaerialimageryC').checked) 
	{ 
		document.getElementById('choroplethopacityP').style.display = 'inline'; 
		map.setBaseLayer(layerAerial);
		changeChoroplethopacity();
	} 
	else 
	{ 
		document.getElementById('choroplethopacityP').style.display = 'none'; 
		map.setBaseLayer(layerBlank);
		layerChoro.setOpacity(1.0);
	}
	if (!choroplethOpacitySetup)
	{
		choroplethopacitySE = new Ext.slider.SingleSlider({
			renderTo: 'choroplethopacityS',
			width: 100,
			value: choroplethOpacityValue,
			minValue: 0,
			maxValue: 100,
			plugins: new Ext.slider.Tip()
		});
		choroplethopacitySE.on('change', function() {
			choroplethOpacityValue = choroplethopacitySE.getValue();
			changeChoroplethopacity()
		});
		choroplethOpacitySetup = true;
	}
}

function changeContextopacity()
{
	layerContext.setOpacity(contextOpacityValue/100.0);
}

function changeChoroplethopacity()
{
	layerChoro.setOpacity(choroplethOpacityValue/100.0);
}

function changeArealunit()
{
	refreshDynamicLayer();
}

function populateAttributeDropdowns()
{
}

