// vim: sw=4:ts=4:nu:nospell

/**
 * HyperCities session Objects
 *
 * @author    Chen-Kuei Lee
 * @copyright (c) 2008, by HyperCities Tech Team
 * @date      2008-12-22
 * @version   0.7
 *
 */
 
HyperCities.session = function() {
	// do NOT access javascript generated DOM from here; elements don't exist yet

	// Private variable goes here
	var _id         = "[HyperCities.session] ",
		_user       = null,
		_group      = null,
		_mode       = null,
		_city       = null,
		_zoom       = null,
		_center     = null,
		_baseMap    = null,
		_infoWin    = null,
		_maxInfoWin = false,
		_maps       = [],
		_maxMaps    = 2 ,
		_currentBounds = null,			// Stores a GLatLngBounds object
		_currentTimespan = null,
                _loadedSnapshots = [];
    var _permalinksRendered = false;

    // Check if the given mapId is in _maps array
    var _inSession = function ($mapId) {
        var i ;

        for ( i in _maps ) {
            if ( _maps[i].id == $mapId ) {
                return i;
            }
        }

        return -1;
    };

    // Check if the given mapId is the base Map
    var _isBaseMap = function ($mapId) {

        if ( _baseMap === null ) // BaseMap not set
            return false;
        else if ( _baseMap.id != $mapId ) // Not the baseMap
            return false;
         
        return true;
    };

    var _removeMap = function ($mapId) {
        var index, map;

//      HyperCities.debug(_id + "Remove Map " + $mapId);
        index = _inSession($mapId);

        // Map is in session, remove it.
        if ( index >= 0 ) {
            map = _maps.splice(index, 1);
            HyperCities.mainMap.removeMap(map[0]);
            HyperCities.mapList.unHighlight(map[0].id);
        }

        // If it's also the baseMap, reset the baseMap
        if ( _baseMap !== null && _baseMap.id == $mapId )
            _baseMap = null;

        // If we don't have any map overlay, remove blankMap
        if ( _maps.length === 0 )
            HyperCities.mainMap.resetMapType();

        return map;
    };

    // Add Map Object to session 
    var _addMap = function ($map, $isBaseMap) {
        var index, totalMaps, removedMap;

        // Count total maps we are overlayed now
        totalMaps = _maps.length ;

        if ( _baseMap !== null ) 
            totalMaps++;

        // To set baseMap, just replace the old one. 
        if ( $isBaseMap ) {

            // if old baseMap is not in session, remove hightlight
            if ( _baseMap !== null && ( _inSession(_baseMap.id) < 0 ) ) {
                HyperCities.debug("Session Remove Base Map");
                HyperCities.mapList.unHighlight(_baseMap.id);
                HyperCities.mainMap.removeMap(_baseMap);
            }

            _baseMap = $map;
            return;
        }

      HyperCities.debug(_id + "add Map " + $map.id);
        if ( _maps.length === 0 ) {
            HyperCities.mainMap.addBlankMap();
        } else {
            // we have at least one map,
            // so check if Map already overlaid
            index = _inSession($map.id);

            // If map already overlaid, 
            if ( index >= 0 ) {
                // If it's not the last one
                // bring the selected map to the end of queue
                if ( index != _maxMaps - 1 ) {
                    removedMap = _maps.splice(index, 1);
                    _maps.push($map);
                }
                return;
            }
        }

        // If it's not base Map, then map not yet overlay
        if ( _baseMap === null || $map.id !== _baseMap.id )
            _maps.push($map);

        // if necessary, remove extra overlay 
        if ( _maps.length > _maxMaps )  {
            removedMap = _maps.shift();
            HyperCities.mainMap.removeMap(removedMap);
            HyperCities.mapList.unHighlight(removedMap.id);
        }
    };

    var _setCity = function ($cityId) {
        _city = $cityId;
        HyperCities.mainMap.setCity(_city);
    };

    
	return {
    	
    	permalinkedItems: { type: '', id:-1, bounds: null
    			},
    	permalinksRendered: false,

		// reset cookies and client session variables
		reset: function() {
			_user       = null;
			_group      = null;
			_mode       = null;
			_city       = null;
			_zoom       = null;
			_center     = null;
			_baseMap    = null;
			_infoWin    = null;
			_currentTimespan = null;
			_currentBounds = null;
			_maxInfoWin = false;
			_maps.splice(0, _maps.length);
			HyperCities.mainMap.resetMapType();
//			HyperCities.debug(_id + "Reset Session");
			return true;
		},
       
		// Compares whether the session currently stored has the same bounds (GLatLng object) and same timespan.
		// @params $bounds should be a GLatLng Object and $timespan should be a map with elements max and min.
		// @returns true if the sessions are equal, false if the session is not initialized or is different.
		isEqualSession: function($bounds, $timespan) {
		
			// If the current session (either bounds or timespan) is not even initialized
			// then the session is not the same.
			if( _currentBounds == null || _currentTimespan == null) return false;	
			
			try{
				var result = (_currentBounds.equals($bounds) && ($timespan.max == _currentTimespan.max) 
					&& ($timespan.min == _currentTimespan.min));
			}catch(e){
				HyperCities.debug("Error in isEqualSession: "+e);
			}
			return result;	
		},


		// set client session variable 
		set: function ($key, $value) {

			if ( (typeof($key) !== 'string') || (typeof($value) === 'undefined') ) {
				return false;
			}

//          HyperCities.debug(_id + _maps.length);
            switch ($key) {
                case "user"   : _user = $value; break;
                case "group"  : _group = $value; break;
				case "mode"   : _mode = $value; break;
                case "map"    : _addMap($value, false); break;
                case "baseMap": _addMap($value, true); break;
                case "city"   : _setCity($value); break;
                case "zoom"   : _zoom = $value; break;
                case "center" : _center = $value; break;
				case "infoWin"    : _infoWin = $value; break;
				case "maxInfoWin" : _maxInfoWin = ($value === true); break;
                case "permalinkedCity" : _permalinkedCity = $value; break;
                case "permalinkedItems" : _permalinkedItems = $value; break;
				case "currentTimespan" : _currentTimespan = $value; break;
				case "currentBounds": _currentBounds = $value; break;
                default       : return false;
            }
            return true;
        },

		// get client session variable 
		get: function ($key) {

			if (typeof($key) !== 'string') {
				return 'undefined';
			}

            switch ($key) {
                case "user"   : return _user;
                case "group"  : return _group;
				case "mode"   : return _mode;
                case "map"    : return _maps;
                case "baseMap": return _baseMap;
                case "city"   : return _city;
                case "zoom"   : return _zoom;
                case "center" : return _center;
				case "infoWin"    : return _infoWin;
				case "maxInfoWin" : return _maxInfoWin;
                case "permalinksRendered": return _permalinksRendered;
				case "currentTimespan" : return _currentTimespan;
				case "currentBounds": return _currentBounds;
                default       : return 'undefined';
            }
        },

        removeMap: function ($map) {
            if (typeof($map) === 'undefined')
                return false;

            return _removeMap($map.id);
        },
        
        rezoomMap : function ($data) {
    		//rezoomMap = function ($data) {
				HyperCities.debug("Zooming to new coordinates.");
				neLat = parseFloat($($data).find('neLat').text());
				alert ("NE Lat: " + neLat);
				HyperCities.debug(_id + " NE Lat: " + neLat);
				neLon = parseFloat($($data).find('neLon').text());
				swLat = parseFloat($($data).find('swLat').text());
				swLon = parseFloat($($data).find('swLon').text());
				map = HyperCities.mainMap.getMapInstance();
				bounds = new GLatLngBounds(new GLatLng (neLat, neLon), new GLatLng(swLat, swLon));
				//map.setZoom(map.getBoundsZoomLevel(bounds));
				map.setCenter(bounds.getCenter), map.getBoundsZoomLevel(bounds);
			//}
    	},


        saveSnapshot: function () {
            HyperCities.debug(_id + "Save snapshot control called.");
            var snapShotResponse = function ($data) {
                //HyperCities.debug ($data);
                HyperCities.linkController.updateURL('snapshots', $("SnapshotData > id", $data).text());
                alert ("Snapshot successfully saved. To return to this snapshot, please use this URL: " + HyperCities.linkController.generatePermalink('snapshots', $("SnapshotData > id", $data).text()));
            }
            var snapshotData = {};
            snapshotData.description = prompt("Please enter a short descriptive title for your snapshot (limited to 255 characters).");
		if (snapshotData.description == "" || snapshotData.description == null) {
			alert ("Saving snapshot cancelled.");
			return;
		}
            snapshotData.action = 'snapshot.create';
            snapshotData.user = _user;
            // get bounds
            var viewBounds = HyperCities.mainMap.getBounds();
            var center = HyperCities.mainMap.getCenter();
            snapshotData.lat = center.lat();
            snapshotData.lon = center.lng();
            snapshotData.zoom = HyperCities.mainMap.getZoom();
            //var ts = HyperCities.mainMap.getTimespan();
            var ts = HyperCities.timebar.getTime();
            snapshotData.timeFrom = ts.startYear;
            snapshotData.timeTo  = ts.endYear;
            snapshotData.currentYear = ts.currentYear;
            snapshotData.activeYear = ts.activeYear;
            // get all open collections
            // OR: compute the intersection of opened and checked collections
            // View state info
            if (_infoWin != null) {
                if (_maxInfoWin == true) snapshotData.infoWindowState = 'max';
                else snapshotData.infoWindowState = 'min';
                
            } else {
                snapshotData.infoWindowState = 'closed';
            }
            snapshotData.infoWindowId = _infoWin;
            if (_mode == HyperCities.config.MODE_NARRATIVE) {
                //snapshotData.narrativeWindowState = 'open';
                snapshotData.narrativeWindowStructure = '1;';
                if (HyperCities.narrativePanel.getOpenedPanelIds().length == 0) snapshotData.narrativeWindowStructure += $("#panelInfo_1").data("id");
                else snapshotData.narrativeWindowStructure = HyperCities.narrativePanel.getOpenedPanelIds().join(';');
                //HyperCities.debug(_id + "Narrative panel open. Structure is :" + snapshotData.narrativeWindowStructure);
            }
            // Collections
            if (HyperCities.collectionList.getOpenedCollections() != []) snapshotData.openedCollections = HyperCities.collectionList.getOpenedCollections().join(';');
            if (HyperCities.collectionList.getCheckedCollections() != []) snapshotData.checkedCollections = HyperCities.collectionList.getCheckedCollections().join(';');
            // Maps
            if (_maps.length != 0) {
                HyperCities.debug(_id + "Number of maps" + _maps.length + "maps in this session:" + _maps);
                var mapids = new Array();
                for (var i in _maps) {
                    mapids.push(_maps[i].id);
                }
                snapshotData.maps = mapids.join(';');
            }
            snapshotData.baseMap = _baseMap;
            $.post("./snapshots.php", snapshotData, snapShotResponse, "xml");
        },
        
        /**
         * Passed a snapshot id, this function loads
         * the saved snapshot data and applies it to the map.
         */        
        loadSnapshot: function ($id) {
            //_maps = [];
            var currentState = {};
            currentState.center = HyperCities.mainMap.getCenter();
            currentState.zoom = HyperCities.mainMap.getZoom();
            currentState.timeSpan = HyperCities.mainMap.getTimespan();
            currentState.activeMode = _mode;
            // Snapshot pushed onto _loadedSnapshots here in case of errors later;
            // Since it's passed by reference, it will continue to be updated.
            _loadedSnapshots.push(currentState);
            HyperCities.mainMap.closeHCInfoWindow(false);
            HyperCities.mapList.clearMaps();
            HyperCities.collectionList.uncheckAllItems();
            HyperCities.debug(_id + "Loading snapshot from server ...");
            _infoWin = null;
            _maxInfoWin = null;
            // display selector in control function
            // reset map?
            var _parseSnapshot = function($data) {
                /**
                 * These two functions are closures because these requests
                 * need to be completed after the sync operation ends.
                 */
                var __parseMapListData = function ($data) {
                    HyperCities.util.debug (_id + "Parsing snapshot maps");
                    currentState.maps = [];
                    //if ($("#collectionTab").parent().hasClass("highlight")) {
                    if ($("#mapTab").parent().hasClass("highlight") == false) {
                        $("#collectionTab").parent().removeClass("highlight");
                        $("#mapTab").parent().addClass("highlight");
                    }
                    HyperCities.util.debug(_id + "Putting maps onto map view.");
                    if ($("Snapshot > maps > map", $data) != 0) {
                        $("Snapshot > maps > map", $data).each (
                            function () {
                                var map_id = $(this).text();
                                //HyperCities.debug (_id + " " + $(this).text());
                                //HyperCities.debug(_id + " " + i + " map:" + map_id);
                                HyperCities.mapList.addMap(map_id);
                                _addMap(HyperCities.mapList.getMap(map_id));
                                currentState.maps.push(map_id);
                            }
                        );
                    }
                    //HyperCities.mapList.overlayBaseMap(Date.getFullYear(), true);
                    __parseObjectData($data);
                    //HyperCities.collectionList.queuePostSyncOperation(__parseObjectData, $data);
                }
                var __parseObjectData = function ($data) {
                    HyperCities.util.debug(_id + "Parsing snapshot object data")
                    if ($("#collectionTab").parent().hasClass("highlight") == false) {
                        $("#mapTab").parent().removeClass("highlight");
                        $("#collectionTab").parent().addClass("highlight");
                        //HyperCities.intelliList.reset();
                        HyperCities.collectionList.update(null, null, true);
                    }
                    //HyperCities.collectionList.update(null, null, true);
                    var currentOpenedCollections = HyperCities.collectionList.getOpenedCollections();
                    currentState.openedCollections = [];
                    $("Snapshot > OpenedCollections > collection", $data).each(
                        function () {
                            //HyperCities.debug(_id + "Opening collection " + $(this).text());
                            var collectionId = $(this).text()
                            HyperCities.collectionList.openCollection(collectionId);
                            // Store collection id only if it's not currently open, because when we roll it back,
                            // the collection should still be checked.
                            if (!$.inArray (collectionId, currentOpenedCollections)) currentState.openedCollections.push(collectionId);
                        }
                    );
                    var currentCheckedCollections = HyperCities.collectionList.getCheckedCollections();
//                    HyperCities.util.debug ("currentCheckedCollections");
//                    HyperCities.util.debug (currentCheckedCollections);
                    currentState.checkedCollections = [];
                    $("Snapshot > CheckedCollections > collection", $data).each(
                        function () {
                            var collectionId = $(this).text()
                            //HyperCities.debug(_id + "Checking collection " + $(this).text());
                            HyperCities.collectionList.checkCollection($(this).text());
                        }
                        
                    );
                    var snapshotCheckedCollections = HyperCities.collectionList.getCheckedCollections();
//                    HyperCities.util.debug("Checked collections now ...")
//                    HyperCities.debug(snapshotCheckedCollections);
                    for (var i in snapshotCheckedCollections) {
                        // Store collection id only if it's not currently open, because when we roll it back,
                        // the collection should still be checked.
                        //HyperCities.util.debug ("Should we add" + snapshotCheckedCollections[i])
                        //HyperCities.util.debug (currentCheckedCollections)
                        if ($.inArray(parseInt(snapshotCheckedCollections[i]), currentCheckedCollections) == -1) {
                            //HyperCities.util.debug (_id + "We should add " + snapshotCheckedCollections[i])
                            currentState.checkedCollections.push(snapshotCheckedCollections[i]);
                        }
                    } // end for (var i in snapshotcheckedCollections)
                    _mode = HyperCities.config.MODE_NARRATIVE;
//                    HyperCities.util.debug ("Checked collections that will be removed:")
//                    HyperCities.util.debug (currentState.checkedCollections)
                    var ___openWindow = function () {};
                        switch ($("Snapshot > InfoWindow > state", $data).text()) {
                            case 'closed':
                                HyperCities.debug(_id + "Info window closed.");
                                _maxInfoWin = false;
                                break;
                            case 'min':
                                HyperCities.debug(_id + "Window state minimized");
                                ___openWindow = function () {
                                    HyperCities.mainMap.openHCInfoWindow(
                                        HyperCities.collectionList.getObjectById
                                        ($("Snapshot > InfoWindow > id", $data).text()),
                                        {
                                            maximize : false
                                        });
                                }
                                break;
                            case 'max':
                                ___openWindow = function () {
                                    HyperCities.mainMap.openHCInfoWindow(
                                        HyperCities.collectionList.getObjectById (
                                            $("Snapshot > InfoWindow > id", $data).text()),
                                            {maximize: true}
                                    );
                                        HyperCities.debug("Info window should be open now.");
                                };
                            break;
                        } // end switch ($("Snapshot > InfoWindow > state", $data).text())
                    setTimeout(___openWindow, 10000);
                    __parseViewState($data);
                } // end var __parseObjectData = function ($data)
                var __parseViewState = function ($data){//$data) {
                    HyperCities.debug (_id + "Parsing view state");
                    if (_mode == HyperCities.config.MODE_NARRATIVE) {
                        HyperCities.util.debug (_id + "In narrative mode. Viewstate will not be changed.");
                        return;
                    }
                    // Info Bubble
                    
                    // Narrative Panels
                    var panelIds = [];
                    var panelCounter = 0;
                    $("Snapshot > NarrativePanels > collection ", $data).each( function () {
                        panelIds.push($(this).text());
                        if ($(this).text() != '1')
                            HyperCities.narrativePanel.load(panelIds[panelCounter - 1], parseInt ($(this).text()), {zoom: false});
                        panelCounter++;
                    } // end $("Snapshot > NarrativePanels > collection ", $data).each( function ()
                    );
                } // end var __parseViewState = function ($data)
                HyperCities.debug(_id + "Parsing snapshot ...");
                // zoom map
                var lat = $("Snapshot > center > lat:first ", $data).text();
                var lon = $("Snapshot > center > lon:first ", $data).text();
                var zoom = $("Snapshot > center > zoom:first ", $data).text();
                // TODO: Upgrade when we switch to a finer-grained time slider
                HyperCities.timebar.setTime(
                    Date.parse($("Snapshot > timespan > currentYear ", $data).text().replace(/\((.+)\)/, "")),
                    Date.parse($("Snapshot > timespan > timeFrom ", $data).text().replace(/\((.+)\)/, "")),
                    Date.parse($("Snapshot > timespan > timeTo ", $data).text().replace(/\((.+)\)/, "")),
                    Date.parse($("Snapshot > timespan > activeYear ", $data).text().replace(/\((.+)\)/, "")),
                    true
                );
                
                if (_mode == HyperCities.config.MODE_NARRATIVE) {
                    if (HyperCities.session.permalinkedItems.id != -1) {
                        _mode = HyperCities.config.MODE_COLLECTION_LIST;
                        setTimeout (function () {
                            HyperCities.syncSession();
                            _mode = HyperCities.config.MODE_NARRATIVE;
                            },
                            2000
                        )
                    } else {
                        HyperCities.util.debug(_id + "Not following permalink; doing render instead.")
                        //HyperCities.syncSession();
                        //$("#mapTab").parent().addClass("highlight");
                        _mode = HyperCities.config.MODE_COLLECTION_LIST;
                        setTimeout (function () {
                            HyperCities.syncSession();
                            _mode = HyperCities.config.MODE_NARRATIVE;
                            },
                            1000
                        );
                    }
                } else {
                    HyperCities.syncSession();
                    //HyperCities.mapList.addPostSyncOperation(__parseMapListData, $data);
                }
                HyperCities.mapList.addPostSyncOperation(__parseMapListData, $data);
                HyperCities.mainMap.setCenter(new GLatLng(parseFloat(lat), parseFloat(lon)), parseInt(zoom));
                HyperCities.mapList.update(HyperCities.mainMap.getBounds(), HyperCities.mainMap.getZoom(), true, false);
            }
            var params = {
                action: 'snapshot.load',
                snapshot_id : $id
            }
            $.post("./snapshots.php", params, _parseSnapshot);
        },

    	rollbackSnapshot: function () {
            var lastSnapshot = _loadedSnapshots.pop();
            // remove maps first
            for (var i in lastSnapshot.maps) {
                HyperCities.mapList.removeMap(lastSnapshot.maps[i]);
            }
            // uncheck each object in reverse
            if (lastSnapshot.checkedCollections !== undefined) {
                HyperCities.collectionList.uncheck (lastSnapshot.checkedCollections.reverse());
                HyperCities.util.debug ("Unchecking collections ...")
                //HyperCities.util.debug (lastSnapshot.checkedCollections)
            }
                
            // close all folders
            if (lastSnapshot.openedCollections !== undefined)
                HyperCities.collectionList.collapseFolders(lastSnapshot.openedCollections.reverse());
            HyperCities.mainMap.setCenter(lastSnapshot.center, lastSnapshot.zoom);
            HyperCities.mainMap.setTimespan(lastSnapshot.timeSpan.min, lastSnapshot.timeSpan.max);
            
        } // end rollbackSnapshot : function
    };
}(); // end of Object

// end of file
