var ge;
var currentTourKmlObject = null; // a KmlTour object
var currentFetchedKmlObject = null; // the original fetchKml'd target
var currentTourIndex = -1;
var currentExtraKmlsObject = null;
var loadingOverlay = null;

var appPath = document.location.protocol + '//' +
              document.location.host + document.location.pathname;

/**
 * Init function called when the DOM is ready.
 */
function init() {
  if (!checkTourHash()) {
    loadPlugin('earth', function() {
      if (loadingOverlay)
        loadingOverlay.setVisibility(false);
    });
  }

  // Generate the tour selection list.
  var tourListNode = document.getElementById('tourlist');
  for (var i = 0; i < tourList.length; i++) {
    var tour = tourList[i];

    // Create the tour link and its container.
    var linkNode = document.createElement('LI');
    linkNode.id = 'tourind-' + i;

    // Handle clicks on the tour links.
    linkNode.onclick = (function(tourIndex) {
      return function(e) {
        document.location.hash = '#' + tourList[tourIndex].id;
        loadTour(tourIndex);
      };
    })(i);

    var thumbnailNode = document.createElement('IMG');
    thumbnailNode.src = tour.thumbnail;
    linkNode.appendChild(thumbnailNode);

    var titleNode = document.createElement('SPAN');
    titleNode.className = 'title';
    titleNode.innerHTML = tour.title;
    linkNode.appendChild(titleNode);

    if (tour.duration) {
      var metaNode = document.createElement('SPAN');
      metaNode.className = 'meta';
      metaNode.innerHTML = '&nbsp;(' + tour.duration + ')';
      titleNode.appendChild(metaNode);
    }

    if (tour.narrator) {
      var narratorNode = document.createElement('SPAN');
      narratorNode.className = 'narrator';
      narratorNode.innerHTML = 'with ' + tour.narrator;
      linkNode.appendChild(narratorNode);
    }

    tourListNode.appendChild(linkNode);
  }

  // Hide the 'Loading' message and show the tourList list.
  document.getElementById('tourlist_status').style.display = 'none';
  document.getElementById('tourlist_container').style.display = '';

  // Watch the document location hash for changes.
  window.setInterval(checkTourHash, 100);
}

/**
 * Check the document location hash/anchor and load the requested tour
 * if the anchor changes.
 * @return Returns true if the hash has changed and a new tour is loading, and
 *     false otherwise.
 */
function checkTourHash() {
  var destID = document.location.hash.match(/(\w+)/);
  if (destID)
    destID = destID[1];

  if (destID && (currentTourIndex < 0 ||
                 destID != tourList[currentTourIndex].id)) {

    // Find the tour with this ID.
    var destTourIndex = -1;
    for (var i = 0; i < tourList.length; i++) {
      if (tourList[i].id == destID) {
        destTourIndex = i;
        break;
      }
    }

    // Select the tour and open it up.
    if (destTourIndex >= 0) {
      loadTour(destTourIndex);
      return true;
    }
  }

  return false;
}

/**
 * Load the Google Earth Plugin instance, deleting any existing instance.
 * @param {string} mapType The planet type to load (either 'mars' or 'earth').
 */
function loadPlugin(mapType, callback) {
  google.earth.createInstance('map3d', function(pluginInstance) {
    ge = pluginInstance;
    ge.getWindow().setVisibility(true);
    ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);

    // Create the loading overlay.
    var loadingImage = ge.createIcon('');
    loadingImage.setHref(appPath + 'images/loading.png');

    loadingOverlay = ge.createScreenOverlay('');
    loadingOverlay.setIcon(loadingImage);
    loadingOverlay.getOverlayXY().set(
        25, ge.UNITS_PIXELS, 25, ge.UNITS_INSET_PIXELS);
    loadingOverlay.getScreenXY().set(
        0, ge.UNITS_FRACTION, 1, ge.UNITS_FRACTION);
    loadingOverlay.getSize().set(-1, ge.UNITS_FRACTION, -1, ge.UNITS_FRACTION);
    ge.getFeatures().appendChild(loadingOverlay);

    // Load default view.
    var lookAt = ge.createLookAt('');
    lookAt.set(0.5, 0.5, 0, ge.ALTITUDE_RELATIVE_TO_GROUND,
        0, 0, 7000000);
    ge.getView().setAbstractView(lookAt);

    if (callback) {
      callback.call(null);
    }
  }, function(errorCode) {
    if (errorCode == 'ERR_NOT_INSTALLED' ||
        errorCode == 'ERR_CREATE_PLUGIN' && !google.earth.isInstalled()) {
      document.getElementById('tourlist_status').innerHTML =
          'To tour the world in your browser, you must first install the ' +
          'Google Earth Plugin by clicking the download link to the right.';
    } else {
      document.getElementById('tourlist_status').className = 'error';
      document.getElementById('tourlist_status').innerHTML =
          'There was an error loading the touring application.';
    }
  }, (mapType == 'mars' ?
         { database: 'http://khmdb.google.com/?db=mars' } : {}));
}

/**
 * Load the given tour and begin playback, stopping any currently playing tours.
 * Also selects the link to the tour and scrolls the link into view.
 */
function loadTour(tourIndex) {
  if (currentTourIndex == tourIndex)
    return;

  // Deselect the currently playing tour's link.
  var oldTourIndex = currentTourIndex;
  if (currentTourIndex >= 0) {
    document.getElementById('tourind-' + oldTourIndex).className = '';
    currentTourIndex = -1;
  }

  // Mark the link as selected and scroll it into view.
  var linkNode = document.getElementById('tourind-' + tourIndex);
  if (!linkNode)
    return;

  linkNode.className = 'selected';
  if ('linkNode' in linkNode)
    linkContainerNode.scrollIntoView(false);

  currentTourIndex = tourIndex;

  // Set up the embed link.
  resetEmbedLink(currentTourIndex);

  // Reload the plugin if necessary.
  if (!ge || oldTourIndex < 0 || tourList[currentTourIndex].mapType !=
                                 tourList[oldTourIndex].mapType) {
    // Destroy the existing plugin.
    // TODO(romannurik): assess the implications of this
    ge = null;
    document.getElementById('map3d').innerHTML = '';

    currentFetchedKmlObject = null;
    loadingOverlay = null;

    loadPlugin(tourList[currentTourIndex].mapType, continueLoadTour_);
  } else {
    if (loadingOverlay)
      loadingOverlay.setVisibility(true);

    continueLoadTour_(null);
  }
}

/**
 * Continuation function for loadTour()
 */
function continueLoadTour_() {
  // Stop any currently playing tour.
  if (currentTourKmlObject) {
    //ge.getTourPlayer().reset();
    ge.getTourPlayer().setTour(null);
  }

  // Turn on/off extra layers.
  if (tourList[currentTourIndex].options) {
    ge.getLayerRoot().enableLayerById(ge.LAYER_BUILDINGS,
        tourList[currentTourIndex].options.buildings ? true : false);
  }

  // Load the first (or tourNumber'th) <gx:Tour> in the tour's KML URL.
  var tourNumber = tourList[currentTourIndex].tourNumber || 1;
  var tourIndexAtFetch = currentTourIndex;

  google.earth.fetchKml(
    ge,
    tourList[currentTourIndex].url,
    function(kmlObject) {
      if (!kmlObject) {
        // TODO(romannurik): non-obtrusive error.
        return;
      }

      // If the user clicks a different tour while this one is being fetched,
      // cancel the loading of this tour.
      if (tourIndexAtFetch != currentTourIndex) {
        return;
      }

      if (currentFetchedKmlObject) {
        ge.getFeatures().removeChild(currentFetchedKmlObject);
        currentFetchedKmlObject = null;
      }

      currentFetchedKmlObject = kmlObject;
      ge.getFeatures().appendChild(currentFetchedKmlObject);

      // Walk the loaded KML object hierarchy looking for a <gx:Tour>.
      walkKmlDom(kmlObject, function(context) {
        if (this.getType() == 'KmlTour' && !--tourNumber) {
          ge.getTourPlayer().setTour(this);

          // Hide the loading overlay.
          if (loadingOverlay)
            loadingOverlay.setVisibility(false);

          ge.getTourPlayer().play();

          currentTourKmlObject = this;
          return false;
        }
      });
      
      // Remove any existing extra KMLs folder.
      if (currentExtraKmlsObject) {
        ge.getFeatures().removeChild(currentExtraKmlsObject);
        currentExtraKmlsObject = null;
      }
      
      // Load any extra KML files necessary to view this tour into a new
      // folder.
      var extraKmls = tourList[currentTourIndex].extraKmls || [];
      var currentExtraKmlsObject = ge.createFolder('');
      ge.getFeatures().appendChild(currentExtraKmlsObject);
      
      for (var i = 0; i < extraKmls.length; i++) {
        // Create a network link to this extra KML file
        // and place it in the extra KMLs folder.
        var link = ge.createLink('');
        link.setHref(extraKmls[i]);
        
        var networkLink = ge.createNetworkLink('');
        networkLink.setLink(link);
        
        currentExtraKmlsObject.getFeatures().appendChild(networkLink);
      }
    }
  );
}

/**
 * Resets the 'embed this tour...' link to point to the Gadget wizard for
 * the tour at the given index in the tour list.
 */
function resetEmbedLink(tourIndex) {
  // Construct the embed link URL.
  var tourSpec = tourList[tourIndex];
  var tourOptions = tourSpec.options || {};

  var urlParams = {
    url: 'http://code.google.com/apis/kml/embed/tourgadget.xml',
    synd: 'open',
    title: tourSpec.title, // Gadgets apparently doesn't use this :(
    up_kml_url: tourSpec.url,
    up_tour_index: tourSpec.tourNumber || 1,
    up_show_navcontrols: 0,
    up_show_buildings: tourOptions.buildings ? 1 : 0,
    up_type_mars: (tourSpec.mapType == 'mars') ? 1 : 0
  };

  var urlParamArr = [];
  for (var key in urlParams) {
    urlParamArr.push(key + '=' + encodeURIComponent(urlParams[key]));
  }

  var baseUrl = 'http://www.gmodules.com/ig/creator';
  var embedUrl = baseUrl + '?' + urlParamArr.join('&');

  // Set the embed link properties.
  var embedSectionNode = document.getElementById('embedsection');
  embedSectionNode.style.visibility = 'visible';

  var embedLinkNode = document.getElementById('embedlink');
  embedLinkNode.href = embedUrl;
}
