import defined from "../Core/defined.js";
import ImageryState from "./ImageryState.js";

/**
 * The assocation between a terrain tile and an imagery tile.
 *
 * @alias TileImagery
 * @private
 *
 * @param {Imagery} imagery The imagery tile.
 * @param {Cartesian4} textureCoordinateRectangle The texture rectangle of the tile that is covered
 *        by the imagery, where X=west, Y=south, Z=east, W=north.
 * @param {Boolean} useWebMercatorT true to use the Web Mercator texture coordinates for this imagery tile.
 */
function TileImagery(imagery, textureCoordinateRectangle, useWebMercatorT) {
  this.readyImagery = undefined;
  this.loadingImagery = imagery;
  this.textureCoordinateRectangle = textureCoordinateRectangle;
  this.textureTranslationAndScale = undefined;
  this.useWebMercatorT = useWebMercatorT;
}

/**
 * Frees the resources held by this instance.
 */
TileImagery.prototype.freeResources = function () {
  if (defined(this.readyImagery)) {
    this.readyImagery.releaseReference();
  }

  if (defined(this.loadingImagery)) {
    this.loadingImagery.releaseReference();
  }
};

/**
 * Processes the load state machine for this instance.
 *
 * @param {Tile} tile The tile to which this instance belongs.
 * @param {FrameState} frameState The frameState.
 * @param {Boolean} skipLoading True to skip loading, e.g. new requests, creating textures. This function will
 *                  still synchronously process imagery that's already mostly ready to go, e.g. use textures
 *                  already loaded on ancestor tiles.
 * @returns {Boolean} True if this instance is done loading; otherwise, false.
 */
TileImagery.prototype.processStateMachine = function (
  tile,
  frameState,
  skipLoading
) {
  const loadingImagery = this.loadingImagery;
  const imageryLayer = loadingImagery.imageryLayer;

  loadingImagery.processStateMachine(
    frameState,
    !this.useWebMercatorT,
    skipLoading
  );

  if (loadingImagery.state === ImageryState.READY) {
    if (defined(this.readyImagery)) {
      this.readyImagery.releaseReference();
    }
    this.readyImagery = this.loadingImagery;
    this.loadingImagery = undefined;
    this.textureTranslationAndScale = imageryLayer._calculateTextureTranslationAndScale(
      tile,
      this
    );
    return true; // done loading
  }

  // Find some ancestor imagery we can use while this imagery is still loading.
  let ancestor = loadingImagery.parent;
  let closestAncestorThatNeedsLoading;
  while (
    defined(ancestor) &&
    (ancestor.state !== ImageryState.READY ||
      (!this.useWebMercatorT && !defined(ancestor.texture)))
  ) {
    if (
      ancestor.state !== ImageryState.FAILED &&
      ancestor.state !== ImageryState.INVALID
    ) {
      // ancestor is still loading
      closestAncestorThatNeedsLoading =
        closestAncestorThatNeedsLoading || ancestor;
    }
    ancestor = ancestor.parent;
  }

  if (this.readyImagery !== ancestor) {
    if (defined(this.readyImagery)) {
      this.readyImagery.releaseReference();
    }

    this.readyImagery = ancestor;

    if (defined(ancestor)) {
      ancestor.addReference();
      this.textureTranslationAndScale = imageryLayer._calculateTextureTranslationAndScale(
        tile,
        this
      );
    }
  }

  if (
    loadingImagery.state === ImageryState.FAILED ||
    loadingImagery.state === ImageryState.INVALID
  ) {
    // The imagery tile is failed or invalid, so we'd like to use an ancestor instead.
    if (defined(closestAncestorThatNeedsLoading)) {
      // Push the ancestor's load process along a bit.  This is necessary because some ancestor imagery
      // tiles may not be attached directly to a terrain tile.  Such tiles will never load if
      // we don't do it here.
      closestAncestorThatNeedsLoading.processStateMachine(
        frameState,
        !this.useWebMercatorT,
        skipLoading
      );
      return false; // not done loading
    }
    // This imagery tile is failed or invalid, and we have the "best available" substitute.
    return true; // done loading
  }

  return false; // not done loading
};
export default TileImagery;
