function MapController(){}
//current set of issues pulled from the server
MapController.currentIssues = [];
//issue totals
MapController.aggIssues = [];
//star sizes for each issue
MapController.starIssues = [];
//current issues to be displayed
MapController.currentIssuesIndex = 0;
//periodical executer, invokes show next issue periodically
MapController.pe;
//number of issues puffed since the last info window was popped up
MapController.infoWindowCnt = 0;
MapController.plotInterval = 0.2; //seconds
MapController.MIN_PLOT_INTERVAL = 0.2; //seconds
MapController.DATA_REFRESH_INTERVAL = 20; //seconds
//the number of markers that should be puffed until the next info window is popped up
MapController.infoWindowInterval = 0; //gets calculated later: max is 15 min is 0
MapController.MIN_IW = 4; //min number of info windows displayed 
MapController.AK_LOCODE = 2; //locode for Alaska
MapController.HI_LOCODE = 15; //locode for Hawaii 
MapController.INFO_WINDOW_PAUSE = 3; //number of seconds to pause after info window is displayed
MapController.PUFF_DELAY = 0.3; //seconds to delay before a puff
MapController.PUFF_LENGTH = 0.3; //seconds, duration of puff effect
MapController.PUFF_SCALE = 350; //controls size of puff
MapController.activeIssue = 7; //the current active issue, 7 is all
MapController.cluster = false; //whether cluster mode is on
MapController.widgetAllMode = false; //whether widget: show all mode is on
MapController.stadiumMode = false; //whether stadium mode is on
MapController.initMap = false; //whether the map has been initialized 
MapController.puffImage = null;
MapController.DEFAULT_MARKER_SIZE = 1;
MapController.lastTimeStamp = -1;

MapController.showIssueType = function(issue){
    if(issue == 7){
        MapController.setWidgetAllMode(true);
    }
    else {
        MapController.setWidgetAllMode(false);
    }
    MapController.activeIssue = issue;
    MapController.resetMap();
}
MapController.resetMap = function(){
    MapController.markerManager.clearMap();
    MapController.plotInitialTotals();
}

MapController.setActiveIssue = function(issue) {
    MapController.activeIssue = issue;
}
MapController.setClusterMode = function(on) {
    MapController.cluster = on;
}
MapController.setWidgetAllMode = function(on) {
    MapController.widgetAllMode = on;
    MapController.stadiumMode = !on;
}
MapController.setStadiumMode = function(on) {
    MapController.stadiumMode = on;
    MapController.widgetAllMode = !on;
}
MapController.setPlotInterval = function(numCurrentIssues){
    if(MapController.stadiumMode){
        var interval = MapController.DATA_REFRESH_INTERVAL/numCurrentIssues;
        if(interval >= MapController.MIN_PLOT_INTERVAL){
            MapController.plotInterval = interval;
        } 
        else {
            MapController.plotInterval = MapController.MIN_PLOT_INTERVAL;
        }
    }
    else {
        var interval = (MapController.DATA_REFRESH_INTERVAL-MapController.MIN_IW*MapController.INFO_WINDOW_PAUSE)/numCurrentIssues;
        if(interval >= MapController.MIN_PLOT_INTERVAL){
            MapController.plotInterval = interval;
        } 
        else {
            MapController.plotInterval = MapController.MIN_PLOT_INTERVAL;
        }
    }
}
MapController.setInfoWindowInterval = function(numCurrentIssues){
    MapController.infoWindowInterval = Math.floor(numCurrentIssues/MapController.MIN_IW);
}

MapController.initialize = function() {
	MapController.puffImage = document.createElement("img");
	MapController.puffImage.className = "transition";
	MapController.puffImage.style.width = "32px";
	MapController.puffImage.style.height = "31px";
	
    if (GBrowserIsCompatible()) {
        if(MapController.stadiumMode){
            //US map
            map_US = new GMap2($("USMapContainer"));
            map_US.setCenter(new GLatLng(37.0625,-95.677068), 5);
            map_US.disableDragging();
            map_US.disableDoubleClickZoom();
                
            //Anchorage map
            map_AK = new GMap2($("AKMapContainer"));
            map_AK.setCenter(new GLatLng(64.2446, -152.2266), 3);
            map_AK.disableDragging();
            map_AK.disableDoubleClickZoom();
                
            //Honolulu map
            map_HI = new GMap2($("HIMapContainer"));
            map_HI.setCenter(new GLatLng(20.6122, -157.4341), 5);
            map_HI.disableDragging();
            map_HI.disableDoubleClickZoom();
        }
        else {
            //US map
            map_US = new GMap2($("USMapContainer"));
            map_US.setCenter(new GLatLng(37.0625,-95.677068), 4);
            map_US.disableDragging();
            map_US.disableDoubleClickZoom();
                
            //Anchorage map
            map_AK = new GMap2($("widget_AKMapContainer"));
            map_AK.setCenter(new GLatLng(64.2446, -152.2266), 2);
            map_AK.disableDragging();
            map_AK.disableDoubleClickZoom();
                
            //Honolulu map
            map_HI = new GMap2($("widget_HIMapContainer"));
            map_HI.setCenter(new GLatLng(20.6122, -157.4341), 5);
            map_HI.disableDragging();
            map_HI.disableDoubleClickZoom();

        }
    
    MapController.markerManager = new MarkerManager();
    //update the array of MapController.currentIssues
    MapController.updateIssues();
  }
}

//pull issues from the server asynchronously
MapController.updateIssues = function() {
    var d = new Date();
    var t = d.getTime();
    var r = Math.random();
    var id = t+r;
    new Ajax.Request("issueall/",
      {
        method:'get',
        parameters:{id:id},
        onSuccess: function(transport){
            var response = transport.responseText;
            MapController.handleResponse(response);
        },
        onException: function(transport, exception) {
            throw exception;
        }
      });
}


MapController.handleResponse = function(response) {
    //parse the response and fill up MapController.currentIssues
    var chunks = response.split("END\n"); 
    var cur = chunks[0];
    var timestamp = chunks[1];
    //if the data hasn't been refreshed yet
    if(timestamp == MapController.lastTimeStamp){
        //else check for new issues later 
        MapController.updateIssues.delay(MapController.DATA_REFRESH_INTERVAL/2);
        return;
    }
    MapController.lastTimeStamp = timestamp;
    var agg = chunks[2];
    var star = chunks[3];
    //parse the ISSUE_CUR part 
    var curLines = cur.split("\n"); 
    MapController.currentIssues = [];
    for(var i = 0; i < curLines.length-1; i++){
        var a = curLines[i].split(" ");
        var issue = {};
        var locode = a[0];
        if(!OSMS_Utils.stateData[locode]) continue;
        issue.locode = locode;
        issue.id = a[2];
        issue.areaCode = a[1];
        MapController.currentIssues.push(issue);
    }
    //set the plotInterval based on the amount of current issues
    MapController.setPlotInterval(MapController.currentIssues.length);
    MapController.setInfoWindowInterval(MapController.currentIssues.length);
    //parse the ISSUE_AGG part
    var aggLines = agg.split("\n"); 
    for(var i = 0; i < aggLines.length-1; i++){
        var a = aggLines[i].split(" ");
        var locode = a[0];
        if(!OSMS_Utils.stateData[locode]) continue;
        MapController.aggIssues[locode]=a.slice(1);

    }
    //parse the ISSUE_STAR part
    var starLines = star.split("\n"); 
    for(var i = 0; i < starLines.length-1; i++){
        var a = starLines[i].split(" ");
        var locode = a[0];
        if(!OSMS_Utils.stateData[locode]) continue;
        MapController.starIssues[locode]=a.slice(1);
    }
    if(!MapController.initMap){
        MapController.initMap = true;
        MapController.plotInitialTotals();
    }
    MapController.currentIssuesIndex = 0;
    //stop the periodic updating of markers
    if(MapController.pe)MapController.pe.stop();
    //if there are issues to plot
    if(MapController.currentIssues.length > 0){
        //restart the periodic update of issues 
        //MapController.pe = new PeriodicalExecuter(MapController.showNextIssue,MapController.plotInterval);
        MapController.showFirstIssue();
    } 
    else {
        //else check for new issues later 
        MapController.updateIssues.delay(MapController.DATA_REFRESH_INTERVAL/2);
    }
}

//resume showing issues after a pause for an infowindow
MapController.resumeShowingIssues = function(infoWindowMarker){
        if(!MapController.stadiumMode){
            infoWindowMarker.closeInfoWindow();
            map_US.panTo(new GLatLng(37.0625,-95.677068));
        }
        MapController.pe = new PeriodicalExecuter(MapController.showNextIssue,MapController.plotInterval);
}

MapController.getMapByLocode = function(locode) {
    if(locode == MapController.AK_LOCODE){
        return map_AK;
    } 
    else if(locode == MapController.HI_LOCODE) {
        return map_HI;
    }
    else {
        return map_US;
    }
}
MapController.getMapNameByLocode = function(locode) {
    if(locode == MapController.AK_LOCODE){
        return "AK";
    } 
    else if(locode == MapController.HI_LOCODE) {
        return "HI";
    }
    else {
        return "US";
    }
}
MapController.plotInitialTotals = function() {
    for(var locode in OSMS_Utils.stateData){
        var lat = OSMS_Utils.stateData[locode].lat 
        var lon = OSMS_Utils.stateData[locode].lon 
        var map = MapController.getMapByLocode(locode);
        var iconArraySize = OSMS_Utils.getIconArraySize();
        var size;
        if(MapController.starIssues[locode]) {
            size = Math.max(3,Math.ceil(MapController.starIssues[locode][MapController.activeIssue-1]/100*iconArraySize)-1);
        }
        else {
            size = MapController.DEFAULT_MARKER_SIZE;
        }
        MapController.markerManager.plotMarker(map, locode, size, MapController.activeIssue, MapController.widgetAllMode);
    }
}
//plot and puff the next issue
MapController.showFirstIssue = function() {
    if(MapController.currentIssuesIndex < MapController.currentIssues.length) {
        var issue = MapController.currentIssues[MapController.currentIssuesIndex];
        var locode = issue.locode;
        var map = MapController.getMapByLocode(locode);
        var iconArraySize = OSMS_Utils.getIconArraySize();
        var size = Math.max(1,Math.ceil(MapController.starIssues[locode][MapController.activeIssue-1]/100*iconArraySize)-1);
        var marker = MapController.markerManager.plotMarker(map, locode, size, MapController.activeIssue, MapController.widgetAllMode);
        //every MapController.infoWindowInterval markers that are plotted, open an info window 
        if(!MapController.stadiumMode && MapController.getMapNameByLocode(locode) == "US") {
            //get the html for the info window
            var html = MapController.getInfoWindowHTML(locode, issue.id, issue.areaCode);
            //open the info window
            marker.openInfoWindowHtml(html);
            //resume plotting issues
            MapController.resumeShowingIssues.delay(MapController.INFO_WINDOW_PAUSE, marker);
            //do the puff effect on the marker
            MapController.transitionEffect(locode, OSMS_Utils.stateData[locode].lat, OSMS_Utils.stateData[locode].lon, issue.id, size);
        }else {
            MapController.infoWindowCnt++;
            //do the puff effect on the marker
            MapController.transitionEffect(locode, OSMS_Utils.stateData[locode].lat, OSMS_Utils.stateData[locode].lon, issue.id, size);
            MapController.resumeShowingIssues();
        }
        //inc the counter so that next time this function is invoked, the next issue will be shown
        MapController.currentIssuesIndex++;
    }
    else {
        //if there are no issues left to plot, get some more from the server
        MapController.updateIssues.delay(MapController.DATA_REFRESH_INTERVAL/2);
    }
}

//plot and puff the next issue
MapController.showNextIssue = function() {
    if(MapController.currentIssuesIndex < MapController.currentIssues.length){
        var issue = MapController.currentIssues[MapController.currentIssuesIndex];
        var locode = issue.locode;
        //var lat = OSMS_Utils.stateData[locode].lat 
        //var lon = OSMS_Utils.stateData[locode].lon 
        var map = MapController.getMapByLocode(locode);
        var iconArraySize = OSMS_Utils.getIconArraySize();
        var size = Math.max(1,Math.ceil(MapController.starIssues[locode][MapController.activeIssue-1]/100*iconArraySize)-1);
        var marker = MapController.markerManager.plotMarker(map, locode, size, MapController.activeIssue, MapController.widgetAllMode);
        //every MapController.infoWindowInterval markers that are plotted, open an info window 
        if(MapController.infoWindowCnt >= MapController.infoWindowInterval && !MapController.stadiumMode && MapController.getMapNameByLocode(locode) == "US") {
            //stop plotting
            if(MapController.pe)MapController.pe.stop();
            //get the html for the info window
            var html = MapController.getInfoWindowHTML(locode, issue.id, issue.areaCode);
            //open the info window
            marker.openInfoWindowHtml(html);
            //resume plotting issues
            MapController.resumeShowingIssues.delay(MapController.INFO_WINDOW_PAUSE, marker);
            //reset the count
            MapController.infoWindowCnt = 0;
            //do the puff effect on the marker
            MapController.transitionEffect(locode, OSMS_Utils.stateData[locode].lat, OSMS_Utils.stateData[locode].lon, issue.id, size);
        }else {
            MapController.infoWindowCnt++;
            //do the puff effect on the marker
            MapController.transitionEffect(locode, OSMS_Utils.stateData[locode].lat, OSMS_Utils.stateData[locode].lon, issue.id, size);
        }
        //inc the counter so that next time this function is invoked, the next issue will be shown
        MapController.currentIssuesIndex++;
    }
    else {
        //if there are no issues left to plot, get some more from the server
        if(MapController.pe)MapController.pe.stop();
        MapController.updateIssues();
    }
}

MapController.transitionEffect = function(locode, lat, lon, issueId, size) {
	var point = new GLatLng(lat, lon);
	var map = MapController.getMapByLocode(locode);
	var divPoint = map.fromLatLngToContainerPixel(point);
	//var icon = OSMS_Utils.getPuffIconByIndex(issueId);
	//var img = document.createElement("img");
	var img = MapController.puffImage.cloneNode(false);
	
	img.src = OSMS_Utils.getPuffIconByIndex(issueId);
	
	try {
			map.getContainer().appendChild(img);
	}
	catch (e) {
			alert("transitionEffect failed while getting div 'USMapContainer': " + e);
			return;
	}
	
	img.style.display = "block";
	img.style.left = (divPoint.x - img.offsetWidth/2) + "px";
	img.style.top = (divPoint.y - img.offsetHeight) + "px";
	try {
			Effect.VariablePuff(img, MapController.PUFF_SCALE, { duration: MapController.PUFF_LENGTH });
	}
	catch (e) {
			alert("transitionEffect failed while executing Puff effect: " + e);
			return;
	}
}

MapController.getInfoWindowHTML = function(locode, issueId, areaCode) {
    var bodyText;
    if(issueId == OSMS_Utils.OTHER_ID){
        var startText = "<div id='infoWindowDiv'><p>";
        var endText = "</p></div>";
        var issueText = "<span class='bold black'>Someone<br/></span>";
        var stateText = "<span class='bold black'>" + OSMS_Utils.stateData[locode].name + "</span>"
        var acText = "<span class='bold black'>" + areaCode + "</span>"
        bodyText = "<span class='midGray'>in area code "+ acText +" of " + stateText + " supports Obama-Biden</span>"
    } 
    else {
        var startText = "<div id='infoWindowDiv'><p>";
        var endText = "</p></div>";
        var issueText = "<span class='bold black'>" + OSMS_Utils.issueNames[issueId] + "<br/></span>";
        var stateText = "<span class='bold black'>" + OSMS_Utils.stateData[locode].name + "</span>"
        var acText = "<span class='bold black'>" + areaCode + "</span>"
        bodyText = "<span class='midGray'>is an important issue for "+ stateText +" in the " + acText + " area code.</span>"
    }
	return startText + issueText + bodyText + endText;
}
//A simple marker manager
function MarkerManager(){
    this.markers = [];
}

MarkerManager.prototype.getGMarker = function(locode){
    return this.markers[locode]; 
}

MarkerManager.prototype.getSize = function(locode){
    return this.markers[locode].size; 
}

MapController.getAllWidgetActiveIssue = function(locode){
    var maxIndex = 0;
    var max = 0;
    var aggIssues = MapController.aggIssues[locode]
    if(!aggIssues) return 1;
    for(var i = 0; i < aggIssues.length;i++){
        if(max < aggIssues[i] && i !=OSMS_Utils.OTHER_ID-1){
            max = aggIssues[i];
            maxIndex = i;
        }
    }
    return maxIndex+1;
}
MarkerManager.prototype.plotMarker = function(map, locode, size, activeIssue, widgetAllMode ){
    var lat = OSMS_Utils.stateData[locode].lat 
    var lon = OSMS_Utils.stateData[locode].lon 
    var latlng = new GLatLng(lat, lon);
    var map = MapController.getMapByLocode(locode);
    var oldMarker = this.markers[locode];
    //if in 'widget all issues' mode, calculate the leading issue
    //else use the activeIssue
    var issue;
    if(widgetAllMode) {
        issue = MapController.getAllWidgetActiveIssue(locode);
    }
    else {
        issue = activeIssue;
    }
    //if in cluster mode get a cluster icon
    //else get a regular icon
    var icon;
    if(MapController.cluster){
        icon = OSMS_Utils.getClusterIconByIndex(issue, size);
    }
    else {
        icon = OSMS_Utils.getIconByIndex(issue, size);
    }
    //if there is an oldMarker at the locode and the new size is greater than the old markers size
    if(oldMarker != null && size > oldMarker.size){
        //replace the oldMarker with a new one
        var marker = new GMarker(latlng, icon, true); 
        marker.size = size;
        map.addOverlay(marker);
        map.removeOverlay(oldMarker);
        this.markers[locode] = marker;
    }
    //else if there is no oldMarker at the locode
    else if(!oldMarker){
        //add a new one
        var marker = new GMarker(latlng, icon, true); 
        marker.size = size;
        this.markers[locode] = marker;
        map.addOverlay(marker);
    }
    return this.markers[locode];
}

MarkerManager.prototype.clearMap = function(){
    for(var locode in this.markers){
        delete this.markers[locode];
        delete this.markers[locode];
    }
    map_US.clearOverlays();
    map_AK.clearOverlays();
    map_HI.clearOverlays();
}

