import defined from "../Core/defined.js";
import Event from "../Core/Event.js";
/**
 * Describes a KmlTour, which uses KmlTourFlyTo, and KmlTourWait to
 * guide the camera to a specified destinations on given time intervals.
 *
 * @alias KmlTour
 * @constructor
 *
 * @param {String} name name parsed from KML
 * @param {String} id id parsed from KML
 * @param {Array} playlist array with KmlTourFlyTos and KmlTourWaits
 *
 * @see KmlTourFlyTo
 * @see KmlTourWait
 *
 * @demo {@link https://sandcastle.cesium.com/?src=KML%20Tours.html|KML Tours}
 */
function KmlTour(name, id) {
  /**
   * Id of kml gx:Tour entry
   * @type String
   */
  this.id = id;
  /**
   * Tour name
   * @type String
   */
  this.name = name;
  /**
   * Index of current entry from playlist
   * @type Number
   */
  this.playlistIndex = 0;
  /**
   * Array of playlist entries
   * @type Array
   */
  this.playlist = [];
  /**
   * Event will be called when tour starts to play,
   * before any playlist entry starts to play.
   * @type Event
   */
  this.tourStart = new Event();
  /**
   * Event will be called when all playlist entries are
   * played, or tour playback being canceled.
   *
   * If tour playback was terminated, event callback will
   * be called with terminated=true parameter.
   * @type Event
   */
  this.tourEnd = new Event();
  /**
   * Event will be called when entry from playlist starts to play.
   *
   * Event callback will be called with curent entry as first parameter.
   * @type Event
   */
  this.entryStart = new Event();
  /**
   * Event will be called when entry from playlist ends to play.
   *
   * Event callback will be called with following parameters:
   * 1. entry - entry
   * 2. terminated - true if playback was terminated by calling {@link KmlTour#stop}
   * @type Event
   */
  this.entryEnd = new Event();

  this._activeEntries = [];
}

/**
 * Add entry to this tour playlist.
 *
 * @param {KmlTourFlyTo|KmlTourWait} entry an entry to add to the playlist.
 */
KmlTour.prototype.addPlaylistEntry = function (entry) {
  this.playlist.push(entry);
};

/**
 * Play this tour.
 *
 * @param {Viewer} viewer viewer widget.
 * @param {Object} [cameraOptions] these options will be merged with {@link Camera#flyTo}
 * options for FlyTo playlist entries.
 */
KmlTour.prototype.play = function (viewer, cameraOptions) {
  this.tourStart.raiseEvent();

  var tour = this;
  playEntry.call(this, viewer, cameraOptions, function (terminated) {
    tour.playlistIndex = 0;
    // Stop nonblocking entries
    if (!terminated) {
      cancelAllEntries(tour._activeEntries);
    }
    tour.tourEnd.raiseEvent(terminated);
  });
};

/**
 * Stop curently playing tour.
 */
KmlTour.prototype.stop = function () {
  cancelAllEntries(this._activeEntries);
};

// Stop all activeEntries.
function cancelAllEntries(activeEntries) {
  for (
    var entry = activeEntries.pop();
    entry !== undefined;
    entry = activeEntries.pop()
  ) {
    entry.stop();
  }
}

// Play playlist entry.
// This function is called recursevly with playNext and iterates over all entries from playlist.
function playEntry(viewer, cameraOptions, allDone) {
  var entry = this.playlist[this.playlistIndex];
  if (entry) {
    var _playNext = playNext.bind(this, viewer, cameraOptions, allDone);
    this._activeEntries.push(entry);
    this.entryStart.raiseEvent(entry);
    if (entry.blocking) {
      entry.play(_playNext, viewer.scene.camera, cameraOptions);
    } else {
      var tour = this;
      entry.play(function () {
        tour.entryEnd.raiseEvent(entry);
        var indx = tour._activeEntries.indexOf(entry);
        if (indx >= 0) {
          tour._activeEntries.splice(indx, 1);
        }
      });
      _playNext(viewer, cameraOptions, allDone);
    }
  } else if (defined(allDone)) {
    allDone(false);
  }
}

// Increment playlistIndex and call playEntry if terminated isn't true.
function playNext(viewer, cameraOptions, allDone, terminated) {
  var entry = this.playlist[this.playlistIndex];
  this.entryEnd.raiseEvent(entry, terminated);

  if (terminated) {
    allDone(terminated);
  } else {
    var indx = this._activeEntries.indexOf(entry);
    if (indx >= 0) {
      this._activeEntries.splice(indx, 1);
    }
    this.playlistIndex++;
    playEntry.call(this, viewer, cameraOptions, allDone);
  }
}
export default KmlTour;
