var gMap;                   // Our global map instance
var gConfig;                // global configutation variables
var gMapSearch;             // a search engine
var gSidebar;               // a search results handler

function loadApplication() {
    // These are our default configuration options.  They can be
    // overriden by parameters in the URL.
	if (!gConfig) {
		gConfig = new Config(window.location.hash.substring(1),
						  { lat: 0,
							lon: -90,
							zoom: 3,
							map: "elevation",
							snippets: 1,
							q: "" });
	}

    // Where does the data come from?
    var mapsServer="mw1.google.com/mw-planetary/mars/";
    var maps = {
        elevation : {
          location: mapsServer + "elevation",
          name: "elevation",
          zoomlevels: 9,
          copyright: "NASA / JPL / GSFC / Arizona State University",
          caption: "A shaded relief map color-coded by altitude"
        },
        visible : {
          location: mapsServer + "visible",
          name: "visible",
          zoomlevels: 10,
          copyright: "NASA / JPL / MSSS / Arizona State University",
          caption: "A mosaic of images from the visible portion of the spectrum"
        },
        infrared : {
          location: mapsServer + "infrared",
          name: "infrared",
          zoomlevels: 13,
          copyright: "NASA / JPL / Arizona State University",
          caption: "A mosaic of images from the infrared portion of the spectrum"
        }
    };

    // Create our global map instance, and add the default controls
    resizeMap();
    var mapOpts = {
      mapTypes: [ makeMarsMap(maps["elevation"]),
                  makeMarsMap(maps["visible"]),
                  makeMarsMap(maps["infrared"]) ]
    };
    gMap = new GMap2($("map"), mapOpts);
    gMap.addControl(new GLargeMapControl());
    gMap.addControl(new GMapTypeControl());
    gMap.enableDoubleClickZoom();
    gMap.enableScrollWheelZoom();
    gMap.enableContinuousZoom();
    $("mapcaption").innerHTML = maps.elevation.caption;
    gMap.setCenter(new GLatLng(0,-90));

    // Set up classes for search and the search results sidebar
    gMapSearch = new MapSearch();
    gSidebar = new Sidebar(resizeMap, gMap);
    // Add our custom controls: a caption for mapType and a scalebar
    makeCustomScalebar();


    // Handle startup options (either passed in the URL or the defaults)
    //   Open the sidebar before setCenter or it'll be off.
    //   Also setCenter has to happen before any search terms
    var q = htmlEscape(gConfig.getValue("q"));
    if (q != "") gSidebar.open();
    gMap.setCenter(new GLatLng(gConfig.getValue('lat'),
                               gConfig.getValue('lon')),
                   parseInt(gConfig.getValue('zoom'), 10),
                   maps[gConfig.getValue('map').toLowerCase()].map);

    if (q != "") {
        $("searchtext").value = q;
        doSearch(q);
    }
    updatePageUrl();

    // A few additional actions to provide a nice UI experience.
    $("searchtext").focus();
    GEvent.addListener(gMap, "moveend", updatePageUrl);
    GEvent.addListener(gMap, "moveend", updateOverlays);
    GEvent.addListener(gMap, "click",
                       function() { gMap.getInfoWindow().hide(); });
    GEvent.addListener(gMap, "maptypechanged", function() {
        $("mapcaption").innerHTML = gMap.getCurrentMapType().getCaption();
    });
}

// Make an instance of a MarsMapType.
function makeMarsMap(m) {
    var name = m.name.charAt(0).toUpperCase() + m.name.substr(1);
    var map = new GMapType(
                [new MarsTileLayer(m.location, m.zoomlevels-1)],
                new GMercatorProjection(m.zoomlevels),
                name);

    // GCopyright isn't exported by the API.  Do our own thing.
    map.getCopyright = function() { return "Image Credit: " + m.copyright;};
    map.getCaption = function() { return m.caption; };
	map.getErrorMessage = function() { return "No imagery available at this zoom level.  Try zooming out or click <a href='javascript:center(-14.6,175.4)'>here</a> to center on a high-resolution insert." };
    m.map = map;
    return(map);
};

// Add a custom scalebar for just the elevation map
function makeCustomScalebar() {
    var scalebar = new Scalebar(document,
               [-9, 21],
               [-9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21], "km");
    gMap.getContainer().appendChild(scalebar.getContainer());
    GEvent.addListener(gMap, "maptypechanged", function() {
        var name = gMap.getCurrentMapType().getName();
        if (name.toLowerCase() == "elevation") {
            scalebar.show();
        } else {
            scalebar.hide();
        }
    });
}


// makeUrl - create a url that will get the application back
//           to its current state, for bookmarking.
function makeUrl() {
    gConfig.setValues({
        lat:      formatFloat(gMap.getCenter().lat(), 6),
        lon:      formatFloat(gMap.getCenter().lng(), 6),
        zoom:     gMap.getZoom(),
        map:      gMap.getCurrentMapType().getName().toLowerCase(),
        snippets: gConfig.getValue("snippets"),
        q:        gSidebar.isOpen() ? gMapSearch.lastSearchQuery : ""
	});

	var loc = window.location.href;
	loc = loc.replace(/[?#]+.*/,"");
	return(loc + "#" + gConfig.getChanged().join("&"));
}

function updatePageUrl() {
	if ($("link")) $("link").href = makeUrl();
}

// Resize the UI components because something's been opened or closed
function resizeMap() {
    var map = $("map");
    var sidebar = $("sidebar");
    // var location = gMap.getCenter();

    var height = getWindowHeight() - getPosition(map).top;
    if (height >= 0) {
        map.style.height = px(height);
        sidebar.style.height = px(height);
    }

    // Set the width of the map to accomidate the search sidebar
    var sidebarWidth = getPosition(sidebar).width;
    if (gSidebar && gSidebar.isOpen()) {
        map.style.left = px(sidebarWidth + 10);
        gSidebar.resize();
        map.style.width = px(getWindowWidth() - (sidebarWidth +10));
    } else {
        map.style.left = px(0);
        map.style.width = px(getWindowWidth());
    }
    if (gMap) gMap.checkResize();
    // if (location) gMap.setCenter(location);
}


// Do everything necessary to execute a search query.
function doSearch(queryText) {
    if (!queryText) queryText = htmlEscape($("searchtext").value);
    var results = gMapSearch.doSearch(queryText);

    gSidebar.open();
    gSidebar.displaySearchResults(queryText,
                                  results,
                                  gConfig.getValue("snippets"),
                                  gMap.getBounds());
    updatePageUrl();
}
function q(queryText) {
    $("searchtext").value = queryText;
    doSearch();
}

// These functions are used in various places as the href on HTML links
// or in places where one of the globals in this file is needed.
//
// (un)limit search to the current window bounds
function limit() { gSidebar.limitSearch(gMap.getBounds()); }
function unlimit() { gSidebar.unlimitSearch(); }
// close the sidebar.  Used in the sidebar's close button's html
function closeSidebar() { gSidebar.close(); }
// open a specific marker.  Used in the sidebar's result's html links
function openMarker(i) { gSidebar.openMarker(i); }
// Show a page.  Used in the sidebar's paging controls.
function showPage(index, dir) {
    gSidebar.showPage(index, dir, gConfig.getValue("snippets"));
}
function center(lat,lon) {
	if (arguments.length == 1) gMap.panTo(lat);
	else gMap.panTo(new GLatLng(lat, lon));
}



///////////////////////////////////////////////////////////////////////////
// This is a fix for the bug that causes markers to not be redrawn on pan.
// When you set the map type, they're handled properly, so... set it.
function updateOverlays() {
    if (window.ending == 1) return;
    window.ending = 1;
    gMap.setMapType(gMap.getCurrentMapType());
    window.ending = 0;
}
