
var MapController = Class.create({

    display: null,
    queue: null,
    updateInterval: null,
    updateId: null,
    fetchInterval: null,
    fetchId: null,
    updateData: null,
    cycleRepeat: null,

    initialize: function (mapElement, centerLat, centerLng, zoom, updateInterval, 
                         fetchInterval, url, userImgUrl, groupImgUrl, weekdays, updateData, cycleRepeat) {
        this.display = new MapDisplay(mapElement, centerLat, centerLng, zoom, weekdays);
        this.queue = new MapItemQueue(url, userImgUrl, groupImgUrl);
        this.updateInterval = updateInterval;
        this.fetchInterval = fetchInterval;
        this.updateData = updateData;
        this.cycleRepeat = cycleRepeat;
    },

    start: function () {
        if(this.isRunning())
            this.stop();
        this.fetch();
        this.updateId = setTimeout(this.update.bind(this, null), 1000);
    },

    update: function (item) {
        if(!item)
            var item = this.queue.next();

        if(item && this.queue.imageComplete()){
            this.display.displayItem(item);
            if(this.cycleRepeat || this.queue.hasMore()){
                this.queue.preloadNext();
                this.updateId = setTimeout(this.update.bind(this, null), this.updateInterval);
            }
            else {
                this.display.clear();
            }
        }
        else{
            this.updateId = setTimeout(this.update.bind(this, item), 500);
        }
    },

    fetch: function () {
        if(this.queue.runningOut() && !this.queue.isFetching())
            this.queue.fetch();
        if(this.updateData && !this.queue.isTooManyFetches())
            this.fetchId = setTimeout(this.fetch.bind(this), this.fetchInterval);
    },

    stop: function () {
        clearTimeout(this.runInterval);
        clearTimeout(this.fetchInterval);
    },

    isRunning: function () {
        return this.updateId || this.fetchId;
    }

});


var MapDisplay = Class.create({

    mapElement: null,
    map: null,
    weekdays: null,

    blueStartIcon: Object.extend(new GIcon(G_DEFAULT_ICON), {
        image: "http://www.pickuppal.com/pup/images/startblue.png"
    }),
    orangeStartIcon: Object.extend(new GIcon(G_DEFAULT_ICON), {
        image: "http://www.pickuppal.com/pup/images/startorange.png"
    }),
    blueEndIcon: Object.extend(new GIcon(G_DEFAULT_ICON), {
        image: "http://www.pickuppal.com/pup/images/endblue.png"
    }),
    orangeEndIcon: Object.extend(new GIcon(G_DEFAULT_ICON), {
        image: "http://www.pickuppal.com/pup/images/endorange.png"
    }),

    initialize: function (mapElement, centerLat, centerLng, zoom, weekdays) {
        this.mapElement = $(mapElement);
        this.map = new GMap2(this.mapElement);
        this.map.disableDragging();
        this.map.setCenter(new GLatLng(centerLat, centerLng), zoom);
        this.weekdays = weekdays;
    },

    displayItem: function (item) {
        this.clear();
        var latLng = new GLatLng(item.lat, item.lng);

        var originIcon = this.blueStartIcon;
        if(item.type == "NEW_GROUP" || item.type == "JOIN_GROUP" || item.type == "USER")
            originIcon = this.orangeStartIcon;

        this.map.addOverlay(new GMarker(latLng, {clickable: false, icon: originIcon}));

        var southernMostPoint = latLng;

        if(item.displayDest){
            var destLatLng = new GLatLng(item.destLat, item.destLng);
	        var destIcon = this.blueEndIcon;
            this.map.addOverlay(new GMarker(destLatLng, {clickable: false, icon: destIcon}));
            var points = [ latLng, destLatLng ];
            this.map.addOverlay(new GPolyline(points, "#0000FF", 4, 0.5));
            if(destLatLng.lat() < southernMostPoint.lat())
                southernMostPoint = destLatLng;
        }

        this.map.openInfoWindowHtml(southernMostPoint, this.toHtml(item), {noCloseOnClick:true, buttons:{close:{show:16}}} );
    },
    
    clear: function(){
        this.map.clearOverlays();
    },

    toHtml: function(item){
        var template = unescape(document.getElementById(item.type).innerHTML);
        if(typeof item.date == "number")
            item.date = this.weekdays[item.date];
        return template.interpolate(item);
    }

});

var MapItemQueue = Class.create({
    itemList: null,
    itemsLeft: null,
    index: null,
    url: null,
    userImgUrl: null,
    groupImgUrl: null,
    memento: null,
    nextImg: null,
    fetchAttempts: null,
    fetching: null,

    initialize: function (url, userImgUrl, groupImgUrl) {
        this.index = 0;
        this.url = url;
        this.itemList = new Array();
        this.itemsLeft = 0;
        this.nextImg = new Image();
        this.userImgUrl = userImgUrl;
        this.groupImgUrl = groupImgUrl;
        this.fetchAttempts = 0;
        this.fetching = false;
    },

    next: function(){
        var item = this.itemList[this.index];
        if(item){
            this.itemsLeft--;
            this.index++;
        }
        if(this.index == this.itemList.length)
            this.index = 0;
        return item;
    },

    preloadNext: function(){
        try{
            if(this.itemList[this.index].type == 'NEW_GROUP' || this.itemList[this.index].type == 'GROUP_EVENT')
                this.nextImg.src = this.groupImgUrl.interpolate(this.itemList[this.index]);
            else
                this.nextImg.src = this.userImgUrl.interpolate(this.itemList[this.index]);
        } catch(err){
           // Nothing to load yet, wait for next try.
        }
    },

    hasMore: function() {
        return (this.itemsLeft >= 0);
    },

    runningOut: function() {
        return (this.itemsLeft < 1);
    },
    
    imageComplete: function() {
        return this.nextImg.complete;
    },
    
    isTooManyFetches: function() {
        return (this.fetchAttempts > 4);
    },
    
    isFetching: function() {
        return this.fetching;
    },

    fill: function(result) {
        if(result.items.length > 0){
            this.memento = result.memento;
	        this.itemList = result.items;
	        this.itemsLeft = this.itemList.length;
	        this.preloadNext();
	        this.fetchAttempts = 0;
        }
        this.fetching = false;
    },

    fetchError: function (result) {
        this.fetching = false;
        if (console) 
            console.dir(result);
    },

    fetch: function () {
        this.fetchAttempts++;
        this.fetching = true;
        new PupAjax.JSONRequest(
            this.url, {
            parameters: {
                memento: this.memento
            },
            onSuccess: this.fill.bind(this),
            onError: this.fetchError
	    });
    }
});

