var map, panel, days, layerTable, treeData, g_timeLayers, animate_pause, animate_play;
var aoos_cache = "http://ak.aoos.org/python/tilecache/tilecache.py?";
var hr = 0, I=1, tz='UTC';
var loading_counter = 0;
var aoos_cgi = '/cgi-bin/pws_fe3.py';

String.prototype.repeat = function(l){ return new Array(l+1).join(this); }; 

var printDate = function(d) {
    return d.getUTCFullYear()+'-'+pad(d.getUTCMonth()+1,2,'0')+'-'+pad(d.getUTCDate(),2,'0')+' '+pad(d.getUTCHours(),2,'0')+':'+pad(d.getUTCMinutes(),2,'0')+':'+pad(d.getUTCSeconds(),2,'0');
};

var getDate = function(tz, i, theHr) {
    if (theHr == null) {
	theHr = hr;
    }
    var d = new Date();
    var msecs = days[i]+theHr*3600*1000;
    d.setTime(msecs);
    if (tz != 'UTC') {
	msecs = msecs-d.getTimezoneOffset()*60*1000;
	d.setTime(msecs);
    }
    return printDate(d);
};

var switchDay = function(tz, i) {
    var I0 = I;
    I = i;
    if (tz == 'UTC') {
	I = i;
	hr = 0;
    } else {
	var d = new Date();
	var msecs = days[i]+d.getTimezoneOffset()*60*1000;
	d.setTime(msecs);
	hr = d.getUTCHours();
	if (msecs >= days[i] && msecs < days[i-1]) {
	    I = i;
	} else if (msecs >= days[i-1]) {
	    I = i-1;
	} else {
	    I = i+1;
	}
    }
};

var pad = function(s, width, fillchar) {
    return fillchar.repeat(width-(s+'').length)+s;
};

var init = function() {
    lbonacina();
    map_init();
    setupLayers();
};

var setupLayers = function() {
    var requestInfo = 'request=setup';
    new Ajax.Request(aoos_cgi, {
	method: 'get',
        parameters: requestInfo,
        onComplete: function (theResponse) {
	    var rObj = theResponse.responseText.strip().evalJSON();
	    days = rObj.days;
	    layerTable = rObj.layerTable;
	    treeData = rObj.treeData;
	    if ($('leftContent').hasClassName('errorMsg')) {
		$('leftContent').removeClassName('errorMsg');
	    }
	    buildTreeUI('leftContent');
	    preload();
	    mkTimeDropDown();
	    
	    // group byTime layers
	    var cnt = -1;
	    g_timeLayers = {idObj: {}};
	    g_timeLayers.names = new Array();
	    for (var id in layerTable) {
		if (layerTable[id].byTime) {
		    cnt = cnt+1;
		    g_timeLayers.idObj[id] = {};
		    g_timeLayers.names[cnt] = layerTable[id].name;
		}
	    }
	    g_timeLayers.names = g_timeLayers.names.join(',');
        },
	
	onFailure: function (theResponse) {
	    $('leftContent').addClassName('errorMsg').innerHTML = 'Load error <br /> Reload page (F5) <br />';
	}
    });
};

var mkTimeDropDown = function() {
    var dropdownparams = {id: "timeDropDown", label: getDate(tz, I).substring(0,10)};
    var mydropdown = dijit.byId(dropdownparams.id);
    if (mydropdown) {
	mydropdown.destroyDescendants();
    } else {
	mydropdown = new dijit.form.DropDownButton(dropdownparams, dojo.byId('timeDropDown'));
    }
    
    //Menu
    var params = {id: "timeMenu"};
    var menu = new dijit.Menu(params, document.createElement("div"));
    
    //MenuItem for the menu
    for (var i=1; i<days.length-1; i++) {
	params = {label: getDate(tz, i).substring(0,10)};
	var menuItem = new dijit.MenuItem(params, document.createElement("div"));
	menu.addChild(menuItem);
	var callback = function(i) {
	    return function() {
		if (animate_play) {
		    animate_play = false;
		}
		switchDay(tz, i);
		$('timeDropDown_label').innerHTML = getDate(tz, I).substring(0,10);
		check_day();
	    }
	}(i);
	dojo.connect(menuItem, "onClick", callback);
    }
    
    //add the Menu to the dropDown
    mydropdown.dropDown = menu;
};

var addLayer = function(item, visible) {
    var layer, params, options;
    var byTime = (item.byTime ? item.byTime : false);
    var add = true;
    var name = item.name;
    
    if (item.layerClass == 'WMSTILE') {
	params = {layers: name,
		  transparent:true,
		  format:'image/png'};
	options = {visibility: visible,
		   isBaseLayer: false,
		   drawOrder: item.drawOrder};
	
	if (byTime && item.maxTime && item.time) {
	    // layer being added after day switching
	    params.time = item.time.replace(' ', 'T')+'Z';
	} else if (byTime && item.maxTime) {
	    // layer being added before any day switching
	    params.time = getDate('UTC', I).replace(' ', 'T')+'Z';
	    if (getDate('UTC', I) > item.maxTime) {
	    	add = false;
	    }
	} else if (byTime) {
	    add = false;
	}
	if (add) {
	    if (debug && byTime) {
		console.log(name+' ... adding -- time="'+params.time+'"');
	    } else if (debug) {
		console.log(name+' ... adding');
	    }
	    layer = new OpenLayers.Layer.WMS(item.id, aoos_cache, params, options);
	    layer.events.register('loadstart', layer, function() {
		loading_counter = loading_counter+1;
	    });
	    layer.events.register('loadend', layer, function() {
		loading_counter = loading_counter-1;
	    });
	    map.addLayer(layer);
	    orderLayer(layer, item.drawOrder);
	}
    } else if (item.layerClass == 'KMLPOINT') {
	if (debug) { console.log(name+' ... adding'); }
        layer = new OpenLayers.Layer.Vector(item.id, {
            projection: map.displayProjection,
            strategies: [new OpenLayers.Strategy.Fixed()],
            protocol: new OpenLayers.Protocol.HTTP({
                url: aoos_cgi+'?request=GETLATESTKML%26NAME='+name+'%26icon='+item.icon+'%26TESTPROPERTY='+item.testProperty,
                format: new OpenLayers.Format.KML({
                    extractStyles: true,
                    extractAttributes: true
                })
            }),
	    visibility: visible,
	    isBaseLayer: false,
	    drawOrder: item.drawOrder
        });
	map.addLayer(layer);
	orderLayer(layer, item.drawOrder);
	
        var select = new OpenLayers.Control.SelectFeature(layer, {id: item.id+'_select'});
        layer.events.on({
            "featureselected": onFeatureSelect.bindAsEventListener(select.unselectAll),
            "featureunselected": onFeatureUnselect
        });
        map.addControl(select);
        select.activate();   
    }
};

var onFeatureSelect = function(event,onPopupClose) {
    var feature, popup;
    feature = event.feature;
    popup = new OpenLayers.Popup.FramedCloud("thePopup", 
					     feature.geometry.getBounds().getCenterLonLat(),
					     new OpenLayers.Size(100,100),
					     "<h2>"+feature.attributes.name + "</h2>" + feature.attributes.description,
					     null, true, onPopupClose
					    );
    feature.popup = popup;
    map.addPopup(popup);
};

var onFeatureUnselect = function(event) {
    var feature = event.feature;
    if(feature.popup) {
        map.removePopup(feature.popup);
        feature.popup.destroy();
        delete feature.popup;
    }
};

var toggleLayer = function(item, visible) {
    var layers = map.getLayersByName(item.id);
    if (layers.length == 1) {
	layers[0].setVisibility(visible);
    } else if (layers.length == 0) {
	addLayer(item, visible);
    }
    item.visibility = visible;
};

var orderLayer = function(layer, drawOrder) {
    if (drawOrder != null) {
	var matchLT = {test: function(value) { return value != null && parseFloat(value)<=drawOrder; }};
	var nLT = map.getLayersBy('drawOrder', matchLT).length-1;
	var delta = nLT-map.getLayerIndex(layer);
	map.raiseLayer(layer, delta);
    }
};

var switchBase = function() {
    if(map) {
	map.setBaseLayer(map.getLayersByName(this.value)[0]);
    }
};

var map_init = function() {
    var options = {
	maxExtent: new OpenLayers.Bounds(-17532819.80,7514065.63,-15028131.26,10018754.17),
	maxResolution: 1222.992452,
	controls: [],
	units: 'm',
	projection: new OpenLayers.Projection("EPSG:900913"),
        displayProjection: new OpenLayers.Projection("EPSG:4326")
    };
    options.maxExtent = new OpenLayers.Bounds(-17532819.80,7514065.63,-15654303.4,10018754.17);
    options.maxExtent = new OpenLayers.Bounds(-16906647.66,8140237.76,-15654303.4,8766409.90);
    
    map = new OpenLayers.Map('map', options);
    
    // basemap layers
    var gphy = new OpenLayers.Layer.Google(
        "Google Physical",
        {type: G_PHYSICAL_MAP,'sphericalMercator': true, minZoomLevel: 6, drawOrder: 0}
    );
    var gmap = new OpenLayers.Layer.Google(
        "Google Streets",
        {'sphericalMercator': true, minZoomLevel: 6, drawOrder: 0}
    );
    var ghyb = new OpenLayers.Layer.Google(
        "Google Hybrid",
        {type: G_HYBRID_MAP,'sphericalMercator': true, minZoomLevel: 6, drawOrder: 0}
    );
    var gsat = new OpenLayers.Layer.Google(
        "Google Satellite",
        {type: G_SATELLITE_MAP,'sphericalMercator': true, minZoomLevel: 6, drawOrder: 0}
    );
    map.addLayers([gphy, gsat, gmap, ghyb]);
    
    // basic map controls
    map.addControl(new OpenLayers.Control.PanZoomBar());
    map.addControl(new OpenLayers.Control.MousePosition());
    map.addControl(new OpenLayers.Control.ScaleLine({topOutUnits: 'mi', topInUnits: 'ft', bottomOutUnits: 'km', bottomInUnits: 'm'}));
    // known OpenLayers bug regarding OverviewMap control and Spherical Mercator
    //options.layers = [gphy.clone()];
    //map.addControl(new OpenLayers.Control.OverviewMap(options));
    
    // extended map controls
    var control = new OpenLayers.Control.ZoomBox({title:"Zoom box"}),
    panel = new OpenLayers.Control.Panel({defaultControl: control, div: document.getElementById("panel")});
    panel.addControls([new OpenLayers.Control.MouseDefaults({title:"Pan"}),
		       control]);
    
    map.addControl(panel);
    map.zoomToMaxExtent();
};

var update_date = function() {
    var layers, cached, name, thisDate, thisHr;
    var theDate = getDate('UTC', I);
    
    $('hourLabel').innerHTML = getDate(tz, I).substring(11);
    for (var id in g_timeLayers.idObj) {
	layers = map.getLayersByName(id);
	name = layerTable[id].name;
	thisDate = theDate;
	if (layerTable[id].timeStep) {
	    thisHr = hr-hr%layerTable[id].timeStep;
	    thisDate = getDate('UTC', I, thisHr);
	    if (debug && thisDate != theDate) { console.log(layerTable[id].name+' ... adjusting time for timeStep='+layerTable[id].timeStep+' hr ("'+theDate+'=>'+thisDate+')'); }
	}
	if (layerTable[id].maxTime) {
	    cached = layerTable[id].maxTime >= thisDate;
	    if (cached && layers.length == 1) {
		if (debug) { console.log(name+' ... updating -- time="'+thisDate.replace(' ', 'T')+'Z"'); }
		layers[0].mergeNewParams({time: thisDate.replace(' ', 'T')+'Z'});
	    } else if (cached) {
		addLayer(layerTable[id], layerTable[id].visibility);
	    } else if (!cached && layers.length == 1) {
		if (debug) { console.log(name+' ... destroying (time not cached)'); }
		layers[0].destroy();
	    }
	} else if (layers.length == 1) {
	    if (debug) { console.log(name+' ... destroying (day not cached)'); }
	    layers[0].destroy();
	}
    }
};

var check_day = function() {
    var theDate = getDate('UTC', I);
    var thisHr, thisDate;
    
    // update time attribute for all byTime layers
    for (var id in g_timeLayers.idObj) {
	thisDate = theDate;
	if (layerTable[id].timeStep) {
	    thisHr = hr-hr%layerTable[id].timeStep;
	    thisDate = getDate('UTC', I, thisHr);
	    if (debug && thisDate != theDate) { console.log(layerTable[id].name+' ... adjusting time for timeStep='+layerTable[id].timeStep+' hr ("'+theDate+'=>'+thisDate+')'); }
	}
	layerTable[id].time = thisDate;
	layerTable[id].maxTime = null;
    }
    
    // check if theDate is cached
    var requestInfo = 'request=checkDay&layers='+g_timeLayers.names+'&time='+theDate;
    new Ajax.Request(aoos_cgi, {
	method: 'get',
        parameters: requestInfo,
        onComplete: function (theResponse) {
	    var rObj = theResponse.responseText.strip().evalJSON();
	    // update maxTime attribute
	    for (var id in g_timeLayers.idObj) {
		layerTable[id].maxTime = rObj.layers[layerTable[id].name];
	    }
	    update_date();
        }
    });
};

var animate = function(action, direction) {
    if (loading_counter>0) {
	// keep playing only after all layers finish loading
	var my_timeout = setTimeout("animate('"+action+"','"+direction+"');", 2000);
	return;
    }
    if (animate_pause) {
	animate_pause = false;
	animate_play = false;
	return
    } else if (action == 'play') {
	animate_play = true;
    }
    
    var origDay = parseInt(getDate(tz, I).substring(8,10));
    
    var newHr = hr+[-1,1][(direction=='forward')+0];
    var newDate = getDate(tz, I, newHr);
    var newDay = parseInt(newDate.substring(8,10));
    if (debug) { console.log('animate -- newHr_utc='+newHr+', newDay_tz='+newDay+' (origDay_tz='+origDay+')'); }
    if (origDay == newDay) {
	hr = newHr;
	update_date();
	if (action=='play') {
	    var my_timeout = setTimeout("animate('"+action+"','"+direction+"');", 2000);
	}
    }
};

var switchTimezone = function() {
    tz = this.value;
    var theDate = getDate(tz, I);    
    $('hourLabel').innerHTML =  theDate.substring(11);
    $('timezoneLabel').innerHTML = tz;
    $('timeDropDown_label').innerHTML = theDate.substring(0,10);
};
