// Some global variables used in this function are from external javascript files
/*global OpenLayers */
// Some global variables used within this function originate from product.conf (via index-template.html)
/*global map:true,routes:true,markers:true,radarLayer:true,tempsLayer:true,windsLayer:true */
/*global ,map_resolutions,max_extents,restrict_extents,tilecache_handler,state_data */

function loadMap()
{

// define map object
map = new OpenLayers.Map(
	"map",
	{
		units: 'm',
		sphericalMercator: true,
		maxExtent: new OpenLayers.Bounds(max_extents[0], max_extents[1], max_extents[2], max_extents[3]),
		restrictedExtent: new OpenLayers.Bounds(restrict_extents[0], restrict_extents[1], restrict_extents[2], restrict_extents[3]),
		resolutions: map_resolutions,
		// empty out the controls and add them manually below
		controls: []
	}
);

//map.addControl(new OpenLayers.Control.PanZoomBar());
map.addControl(new OpenLayers.Control.Navigation({}));

// XXX: development only
//map.addControl(new OpenLayers.Control.MousePosition());
//map.addControl(new OpenLayers.Control.ScaleLine());

// define base layer
var layer = new OpenLayers.Layer.WMS(
	"Base Map",
	tilecache_handler,
	{
		layers: state_data,
		format: 'image/png'
	},
	{
		buffer: 0
		//transitionEffect: 'resize',
	}
);

layer.events.register('loadstart', map, update_zoom_indicator);
map.events.register('zoomend', map, update_zoom_indicator);

map.addLayer(layer);
get_best_zoom();

// define a marker layer
routes = new OpenLayers.Layer.Vector("Roads", {
	styleMap: new OpenLayers.StyleMap({
		'default': new OpenLayers.Style({cursor: "pointer"},{
			rules: [
				new OpenLayers.Rule({minScaleDenominator: "${scale}", symbolizer: {display: 'none'}}),
				// turning this to 0 causes some browsers (IE, in praticular) to drop the route
				new OpenLayers.Rule({maxScaleDenominator: "${scale}", symbolizer: {strokeColor: "#0000FF", strokeWidth: 12, strokeOpacity: 0.01}})
			]
		}),
		'select': new OpenLayers.Style({cursor: "pointer"},{
			rules: [
				new OpenLayers.Rule({maxScaleDenominator: "${scale}", symbolizer: {strokeColor: "#0000FF", strokeWidth: 12, strokeOpacity: 0.5}}),
				new OpenLayers.Rule({minScaleDenominator: "${scale}", symbolizer: {display: 'none'}})
			]
		}),
		'temporary': new OpenLayers.Style({cursor: "pointer"},{
			rules: [
				new OpenLayers.Rule({maxScaleDenominator: "${scale}", symbolizer: {strokeColor: "#00FF00", strokeWidth: 12, strokeOpacity: 0.5}}),
				new OpenLayers.Rule({minScaleDenominator: "${scale}", symbolizer: {display: 'none'}})
			]
		})
	})
});




// some styles for displaying icons
markers = new OpenLayers.Layer.Vector("Information", {
	styleMap: new OpenLayers.StyleMap({
		'default': new OpenLayers.Style({cursor: "pointer"},{
			rules: [
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^road-work$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/road_work-sm.png", graphicHeight: 17, graphicWidth: 20, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^obstructions$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/obstructions-sm.png", graphicHeight: 17, graphicWidth: 20, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^scheduled-events$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/scheduled_events-sm.png", graphicHeight: 17, graphicWidth: 20, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^incidents-accidents$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/incidents_accidents-sm.png", graphicHeight: 17, graphicWidth: 20, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^disasters$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/disasters-sm.png", graphicHeight: 17, graphicWidth: 20, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^disturbances$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/disturbances-sm.png", graphicHeight: 17, graphicWidth: 20, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "camera"}), minScaleDenominator: "${minScale}", maxScaleDenominator: "${maxScale}", symbolizer: {externalGraphic: "images/legend_icons/camera-sm.png", graphicHeight: 17, graphicWidth: 20, labelAlign: "cm", cursor: "pointer", graphicXOffset: "${xOffset}", graphicYOffset: "${yOffset}"}})
			]
		}),
		'select': new OpenLayers.Style({cursor: "pointer"},{
			rules: [
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^road-work$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/road_work-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^obstructions$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/obstructions-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^scheduled-events$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/scheduled_events-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^incidents-accidents$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/incidents_accidents-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^disasters$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/disasters-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^disturbances$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/disturbances-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "camera"}), minScaleDenominator: "${minScale}", maxScaleDenominator: "${maxScale}", symbolizer: {externalGraphic: "images/legend_icons/camera-sm.png", graphicHeight: 21, graphicWidth: 24, labelAlign: "cm", cursor: "pointer", graphicXOffset: "${xOffset}", graphicYOffset: "${yOffset}"}})
			]
		}),
		'temporary': new OpenLayers.Style({cursor: "pointer"},{
			rules: [
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^road-work$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/road_work-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^obstructions$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/obstructions-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^scheduled-events$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/scheduled_events-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^incidents-accidents$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/incidents_accidents-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^disasters$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/disasters-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "^disturbances$"}), maxScaleDenominator: "${minScale}", symbolizer: {externalGraphic: "images/legend_icons/disturbances-sm.png", graphicHeight: 21, graphicWidth: 24, cursor: "pointer", fillOpacity: 1}}),
				new OpenLayers.Rule({filter: new OpenLayers.Filter.Comparison({type: OpenLayers.Filter.Comparison.LIKE, property: "icontype", value: "camera"}), minScaleDenominator: "${minScale}", maxScaleDenominator: "${maxScale}", symbolizer: {externalGraphic: "images/legend_icons/camera-sm.png", graphicHeight: 21, graphicWidth: 24, labelAlign: "cm", cursor: "pointer", graphicXOffset: "${xOffset}", graphicYOffset: "${yOffset}"}})
			]
		})
	})
});

/* load highlight module */


YUI({
	modules: {
		"gallery-effects": {
			fullpath: "yui/gallery/gallery-effects-min.js",
			requires: ["node", "anim", "async-queue", "base"]
		}
	}
}).use("gallery-effects", function(Y) 
{    
	flash_report = 
		new Y.Effects.Highlight
		({
			node: "#information",
			startColor: "#3863c8", 
			endColor: "#ffffff", 
			restoreColor: "#ffffff"
		});
});




/* end highlight module */

var handleReportSuccess = function(obj) {
	layout.addUnit(layout.get('units')[1]);

	var root = obj.responseXML.documentElement;
	var report = document.getElementById('information');
	report.innerHTML = '<div id="report">' + root.getElementsByTagName('contents')[0].firstChild.nodeValue + '</div>';

	layout.getUnitByPosition('bottom').set
	(
		'header', 
		'<div>' +
			'<div id="right" style="float: right; width: 40px; text-align: right;">' + 
				'<div onclick="refresh_report(lastReport)" title="Refresh Report" class="refresh_control"></div>' +
				'<div style="margin-left: 4px;" title="Close Report Window" onclick="layout.getUnitByPosition(\'bottom\').close();" class="close_control"></div>' +
			'</div>' + 
			'<div id="left" style="margin-right: 40px;">' + 
				root.getElementsByTagName('title')[0].firstChild.nodeValue + 
			'</div>' +
		'</div>'
	);

	flash_report.run();

	layout.getUnitByPosition("bottom").addListener(
		"close", function(e) {
			selectControl.unselectAll();
			map.updateSize();

		}
	);
};

var handleReportFailure = function(obj) {
	lastEvent  =  lastReport;

	processFailure(obj);
};

var handleCameraFailure = function(obj) {
	lastEvent  =  cameraEvent;

	processFailure(obj);
};

var processFailure = function(obj) {
	layout.addUnit(layout.get('units')[1]);

	var report = document.getElementById('information');
	report.innerHTML = '<div id="report">Report failed to load.  Please <a href="#" onclick="refresh_report(lastEvent); return false">try again</a>.</div>';

	if(typeof obj.statusText === "undefined")
	{
		errorMessage = "Unexpected Error";
	}
	else
	{
		errorMessage = obj.statusText;
	}

	layout.getUnitByPosition('bottom').set
	(
		'header',
		'<div>' +
			'<div id="right" style="float: right; width: 40px; text-align: right;">' +
				'<div onclick="refresh_report(lastEvent);" title="Refresh Report" class="refresh_control"></div>' +
				'<div style="margin-left: 4px;" title="Close Report Window" onclick="layout.getUnitByPosition(\'bottom\').close();" class="close_control"></div>' +
			'</div>' +
			'<div id="left" style="margin-right: 40px;">' +
				errorMessage +
			'</div>' +
		'</div>'
	);

	layout.getUnitByPosition("bottom").addListener(
		"close", function(e) {
			selectControl.unselectAll();
			map.updateSize();
		}
	);
};

var reportCallback = {
	success: handleReportSuccess,
	failure: handleReportFailure
};

YAHOO.namespace("example.CameraFetch");

YAHOO.example.CameraFetch = function()
{
	// shorcuts
	var Event = YAHOO.util.Event,
		tIds = {},
		current = null;

	// We use the Get Utility's success handler in conjunction with
	// the web service callback in order to detect bad responses
	// from the web service.
	var onCameraFetchSuccess = function(o)
	{ /* does nothing */ };

	return {
		callback: function(results)
		{
			// Mark the transaction as complete.  This will be checked by the onSuccess
			// handler to determine if the transaction really succeeded.
			tIds[current] = true;

			//  show the bottom report pane; note that we will NOT show this if the 
			//    user already has the camera pop-out window open.
			if(  !(YUI2.cameraWindow.cfg.getProperty("visible"))  )      // window NOT visible? Proceed.
			{
				layout.addUnit(layout.get('units')[1]);           // add the bottom pane

				// add a listener, so that all features deselect if the pane is closed.
				layout.getUnitByPosition("bottom").addListener(
					"close", function(e) {
						selectControl.unselectAll();
					}
				);
			}

			processCameraImage(results, feature.attributes.ident);
		},

		getCameraFetchData: function()
		{
			// we want to send our callback script this feature's ident, as well as the current map scale
			var sURL = 
				camera_handler +
				"?generate=" + feature.attributes.ident +
				"," + map.getScale(); 

			// now make the call, using our URL from above
			var transactionObj = YAHOO.util.Get.script
			(
				sURL, // this is our target desintation
				{
					onSuccess: onCameraFetchSuccess,
					onFailure: handleCameraFailure,
					onTimeout: handleCameraFailure,
					timeout  : 1000,
					scope    : this
				}
			);
		
			// keep track of the current transaction id.  The transaction will be
			// considered complete only if the web service callback is executed.
			current = transactionObj.tId;
		}
    };
}();


/* define radar layers */
radarLayer = new OpenLayers.Layer.WMS(
	"Radar Layer",
	tilecache_handler,
	{
		layers: 'radar'
	},
	{
		isBaseLayer: false,
		visibility: false,
		opacity: 0.5
	}
);
map.addLayer(radarLayer);

tempsLayer = new OpenLayers.Layer.WMS(
	"Temperature Layer",
	tilecache_handler,
	{
		layers: 'temps'
	},
	{
		isBaseLayer: false,
		visibility: false,
		opacity: 0.5
	}
);
map.addLayer(tempsLayer);

windsLayer = new OpenLayers.Layer.WMS(
	"Winds Layer",
	tilecache_handler,
	{
		layers: 'winds'
	},
	{
		isBaseLayer: false,
		visibility: false,
		opacity: 0.5
	}
);
map.addLayer(windsLayer);

/* markers should be on top to keep it interactive */
map.addLayer(routes);
map.addLayer(markers);

var hoverControl = new OpenLayers.Control.SelectFeature(
	[routes, markers],
	{ hover: true, highlightOnly: true, renderIntent: 'temporary'});
map.addControl(hoverControl);
hoverControl.activate();

var selectControl = new OpenLayers.Control.SelectFeature(
	[routes, markers]);
map.addControl(selectControl);
selectControl.activate();

markers.events.on({
	"featureselected": function(e) {
	
		feature  =  e.feature; // extends our scope (we'll need this in reportSuccess)

		// check to see if it is a camera marker
		if( feature.attributes.icontype.match(/^camera/)  ) // if this is a camera, generate the tables
		{
			cameraEvent  =  e;   // save this camera in case we pop up in the window
			cameraEvent.feature.attributes.feature_type = 'marker';

			YAHOO.example.CameraFetch.getCameraFetchData();
		}
		else
		{
			lastReport  =  e;
			lastReport.feature.attributes.feature_type = 'marker';

			YAHOO.util.Connect.asyncRequest('GET', report_handler + "?ident=" + feature.attributes.ident, reportCallback);
		}
	}//,
//	"featureunselected": function(e) {
//		layout.getUnitByPosition("bottom").close();
//	}
});

routes.events.on({
	"featureselected": function(e) {
		lastReport  =  e;
		lastReport.feature.attributes.feature_type = 'route';

		feature  =  e.feature; // extends our scope (we'll need this in reportSuccess)
		YAHOO.util.Connect.asyncRequest('GET', report_handler + "?ident=" + feature.attributes.segment, reportCallback);
	}//,
	/* "featurehighlighted": function(e) {
	alert("hit me");
		curPopup = new OpenLayers.Popup.FramedCloud("featurePopup",
			e.feature.geometry.getBounds().getCenterLonLat(),
			null, e.feature.attributes.name, null, false, null);
	},
	"featureunhighlighted": function(e) {
	// remove framed cloud
	} */
//	"featureunselected": function(e) {
//		layout.getUnitByPosition("bottom").close();
//	}
});

}

//  FUNCTION: 
//     refresh_report
//
//  DESCRIPTION:
//     This function simpy acts as a bridge between the refresh button on the report bar
//     and the associated Ajax calls that fetch-and-display the updated report. 
//
//  PARAMETERS: 
//     passedEvent (object)
//     --------------------
//        - An event object that will be passed along to the appropriate callback function. 
//        - In some cases, this is derived from lastReport -- a global variable.
//	

function refresh_report(passedEvent)
{
	//  pretty basic logic here: if we're dealing with a route, simulate a route click
	//  and pass along the event we received. Similarly, if it's a marker (an icon),  
	//  simulate an icon click and pass along the event we received.

	if( passedEvent.feature.attributes.feature_type === 'marker')
	{
		markers.events.triggerEvent('featureselected', passedEvent);
	}
	else if( passedEvent.feature.attributes.feature_type === 'route'  )
	{
		routes.events.triggerEvent('featureselected', passedEvent);
	}
	
}


//  FUNCTION: 
//     toggle_icons
//
//  DESCRIPTION:
//     This function is used to toggle icons on and off. When called, it will simply iterate
//     through all icons on the map, check to see if any of them match the supplied icon type,
//     and then perform the following on matches: (see parameters below) and either:
//         1. use the OpenLayers "addFeatures" method if the icons need to be turned on,
//             -or-
//         2. use the Openlayers "removeFeatures" method if icons need to be turned off
//
//     The state of a particular icon (on or off) is determined by an HTML checkbox. We'll
//     simply check the current state of this to figure out which OpenLayers method we'll
//     need to call. (If we pass "cameras" as our iconType, we'll search for the HTML 
//     checkbox element with an ID "cameras_control" and find its state.)
//
//
//  PARAMETERS: 
//     iconType (string)
//     -----------------
//        - The type of icon we want to toggle on or off. Each icon on the map is an object,
//             with one of its included attributes being "iconType."   
//        - iconType is SET in "icons-template.js"
//        - iconType is DERIVED from the "data/statewide.iconlist" file 
//

function toggle_icons(iconType)
{
	var newStatus; 
	var iconControl = iconType + "_control";

	if(document.getElementById(iconControl).checked)       //  IF 
	{                                                      //    the icon box is checked
		for(i = 0; i < iconList[iconType].length; i++)     //  THEN 
		{                                                  //    run "addFeatures" on all appropriate
			markers.addFeatures(iconList[iconType][i]);    //    icons
		}
	}
	else                                                   //  OTHERWISE
	{                                                      //    the icon box is unchecked,
		for(i = 0; i < iconList[iconType].length; i++)     //  SO 
		{                                                  //    run "removeFeatures" on all appropriate
			markers.removeFeatures(iconList[iconType][i]); //    icons
		}
	}
}


//  FUNCTION: 
//     update_zoom_indicator
//
//  DESCRIPTION:
//     Automatically finds the current scale of the map, and then iterates through the list
//     of scales defined in the product.conf file to see which zoom level it corresponds to.
//     After this is the determined, the visual zoom indicator updated accordingly.

function update_zoom_indicator()
{
	var current_resolution = map.getResolution();
	var thisResolution;

	for(thisResolution in map_resolutions)  // see which resolution we fall at, and update the visual indicator accordingly
	{
		if( (current_resolution > (map_resolutions[thisResolution] - 1)) && (current_resolution < (map_resolutions[thisResolution] + 1)) )
		{
			document.getElementById('zoomToolImg').src = 'images/scaling2/' + thisResolution + '.png';
		}
	}
}


//  FUNCTION: 
//     get_best_zoom
//
//  DESCRIPTION:
//     Uses the restrictedExtent property (derived from the product.conf file) to find which zoom
//     level is most-appropriate for the given map viewport.

function get_best_zoom()
{
	var bounds = map.restrictedExtent;
	var bestZoom = map.getZoomForExtent(bounds, false);
		// setting the second parameter to 'true' will allow for clipping of parts of the state
		// to take place in an effort to display a closer zoom level

	update_zoom_indicator();
	map.zoomTo(bestZoom);
}

