text: /*global define*/
  define([
          '../Core/defined',
          '../Core/defineProperties',
          '../Core/DeveloperError',
          '../Core/Rectangle',
          './QuadtreeTileLoadState'
      ], function(
          defined,
          defineProperties,
          DeveloperError,
          Rectangle,
          QuadtreeTileLoadState) {
      "use strict";
  
      /**
       * A single tile in a {@link QuadtreePrimitive}.
       *
       * @alias QuadtreeTile
       * @constructor
       * @private
       *
       * @param {Number} options.level The level of the tile in the quadtree.
       * @param {Number} options.x The X coordinate of the tile in the quadtree.  0 is the westernmost tile.
       * @param {Number} options.y The Y coordinate of the tile in the quadtree.  0 is the northernmost tile.
       * @param {TilingScheme} options.tilingScheme The tiling scheme in which this tile exists.
       * @param {QuadtreeTile} [options.parent] This tile's parent, or undefined if this is a root tile.
       */
      var QuadtreeTile = function QuadtreeTile(options) {
          //>>includeStart('debug', pragmas.debug);
          if (!defined(options)) {
              throw new DeveloperError('options is required.');
          }
          if (!defined(options.x)) {
              throw new DeveloperError('options.x is required.');
          } else if (!defined(options.y)) {
              throw new DeveloperError('options.y is required.');
          } else if (options.x < 0 || options.y < 0) {
              throw new DeveloperError('options.x and options.y must be greater than or equal to zero.');
          }
          if (!defined(options.level)) {
              throw new DeveloperError('options.level is required and must be greater than or equal to zero.');
          }
          if (!defined(options.tilingScheme)) {
              throw new DeveloperError('options.tilingScheme is required.');
          }
          //>>includeEnd('debug');
  
          this._tilingScheme = options.tilingScheme;
          this._x = options.x;
          this._y = options.y;
          this._level = options.level;
          this._parent = options.parent;
          this._rectangle = this._tilingScheme.tileXYToRectangle(this._x, this._y, this._level);
          this._children = undefined;
  
          // QuadtreeTileReplacementQueue gets/sets these private properties.
          this._replacementPrevious = undefined;
          this._replacementNext = undefined;
  
          // The distance from the camera to this tile, updated when the tile is selected
          // for rendering.  We can get rid of this if we have a better way to sort by
          // distance - for example, by using the natural ordering of a quadtree.
          // QuadtreePrimitive gets/sets this private property.
          this._distance = 0.0;
  
          this._customData = [];
          this._frameUpdated = undefined;
          this._frameRendered = undefined;
  
          /**
           * Gets or sets the current state of the tile in the tile load pipeline.
           * @type {QuadtreeTileLoadState}
           * @default {@link QuadtreeTileLoadState.START}
           */
          this.state = QuadtreeTileLoadState.START;
  
          /**
           * Gets or sets a value indicating whether or not the tile is currently renderable.
           * @type {Boolean}
           * @default false
           */
          this.renderable = false;
  
          /**
           * Gets or set a value indicating whether or not the tile was entire upsampled from its
           * parent tile.  If all four children of a parent tile were upsampled from the parent,
           * we will render the parent instead of the children even if the LOD indicates that
           * the children would be preferable.
           * @type {Boolean}
           * @default false
           */
          this.upsampledFromParent = false;
  
          /**
           * Gets or sets the additional data associated with this tile.  The exact content is specific to the
           * {@link QuadtreeTileProvider}.
           * @type {Object}
           * @default undefined
           */
          this.data = undefined;
      };
  
      /**
       * Creates a rectangular set of tiles for level of detail zero, the coarsest, least detailed level.
       *
       * @memberof QuadtreeTile
       *
       * @param {TilingScheme} tilingScheme The tiling scheme for which the tiles are to be created.
       * @returns {QuadtreeTile[]} An array containing the tiles at level of detail zero, starting with the
       * tile in the northwest corner and followed by the tile (if any) to its east.
       */
      QuadtreeTile.createLevelZeroTiles = function(tilingScheme) {
          if (!defined(tilingScheme)) {
              throw new DeveloperError('tilingScheme is required.');
          }
  
          var numberOfLevelZeroTilesX = tilingScheme.getNumberOfXTilesAtLevel(0);
          var numberOfLevelZeroTilesY = tilingScheme.getNumberOfYTilesAtLevel(0);
  
          var result = new Array(numberOfLevelZeroTilesX * numberOfLevelZeroTilesY);
  
          var index = 0;
          for (var y = 0; y < numberOfLevelZeroTilesY; ++y) {
              for (var x = 0; x < numberOfLevelZeroTilesX; ++x) {
                  result[index++] = new QuadtreeTile({
                      tilingScheme : tilingScheme,
                      x : x,
                      y : y,
                      level : 0
                  });
              }
          }
  
          return result;
      };
  
      QuadtreeTile.prototype._updateCustomData = function(frameNumber, added, removed) {
          var customData = this.customData;
  
          var i;
          var data;
          var rectangle;
  
          if (defined(added) && defined(removed)) {
              // level zero tile
              for (i = 0; i < removed.length; ++i) {
                  data = removed[i];
                  for (var j = 0; j < customData.length; ++j) {
                      if (customData[j] === data) {
                          customData.splice(j, 1);
                          break;
                      }
                  }
              }
  
              rectangle = this._rectangle;
              for (i = 0; i < added.length; ++i) {
                  data = added[i];
                  if (Rectangle.contains(rectangle, data.positionCartographic)) {
                      customData.push(data);
                  }
              }
  
              this._frameUpdated = frameNumber;
          } else {
              // interior or leaf tile, update from parent
              var parent = this._parent;
              if (defined(parent) && this._frameUpdated !== parent._frameUpdated) {
                  customData.length = 0;
  
                  rectangle = this._rectangle;
                  var parentCustomData = parent.customData;
                  for (i = 0; i < parentCustomData.length; ++i) {
                      data = parentCustomData[i];
                      if (Rectangle.contains(rectangle, data.positionCartographic)) {
                          customData.push(data);
                      }
                  }
  
                  this._frameUpdated = parent._frameUpdated;
              }
          }
      };
  
      defineProperties(QuadtreeTile.prototype, {
          /**
           * Gets the tiling scheme used to tile the surface.
           * @memberof QuadtreeTile.prototype
           * @type {TilingScheme}
           */
          tilingScheme : {
              get : function() {
                  return this._tilingScheme;
              }
          },
  
          /**
           * Gets the tile X coordinate.
           * @memberof QuadtreeTile.prototype
           * @type {Number}
           */
          x : {
              get : function() {
                  return this._x;
              }
          },
  
          /**
           * Gets the tile Y coordinate.
           * @memberof QuadtreeTile.prototype
           * @type {Number}
           */
          y : {
              get : function() {
                  return this._y;
              }
          },
  
          /**
           * Gets the level-of-detail, where zero is the coarsest, least-detailed.
           * @memberof QuadtreeTile.prototype
           * @type {Number}
           */
          level : {
              get : function() {
                  return this._level;
              }
          },
  
          /**
           * Gets the parent tile of this tile.
           * @memberof QuadtreeTile.prototype
           * @type {QuadtreeTile}
           */
          parent : {
              get : function() {
                  return this._parent;
              }
          },
  
          /**
           * Gets the cartographic rectangle of the tile, with north, south, east and
           * west properties in radians.
           * @memberof QuadtreeTile.prototype
           * @type {Rectangle}
           */
          rectangle : {
              get : function() {
                  return this._rectangle;
              }
          },
  
          /**
           * An array of tiles that is at the next level of the tile tree.
           * @memberof QuadtreeTile.prototype
           * @type {QuadtreeTile[]}
           */
          children : {
              get : function() {
                  if (!defined(this._children)) {
                      var tilingScheme = this.tilingScheme;
                      var level = this.level + 1;
                      var x = this.x * 2;
                      var y = this.y * 2;
                      this._children = [new QuadtreeTile({
                          tilingScheme : tilingScheme,
                          x : x,
                          y : y,
                          level : level,
                          parent : this
                      }), new QuadtreeTile({
                          tilingScheme : tilingScheme,
                          x : x + 1,
                          y : y,
                          level : level,
                          parent : this
                      }), new QuadtreeTile({
                          tilingScheme : tilingScheme,
                          x : x,
                          y : y + 1,
                          level : level,
                          parent : this
                      }), new QuadtreeTile({
                          tilingScheme : tilingScheme,
                          x : x + 1,
                          y : y + 1,
                          level : level,
                          parent : this
                      })];
                  }
  
                  return this._children;
              }
          },
  
          /**
           * An array of objects associated with this tile.
           * @memberof QuadtreeTile.prototype
           * @type {Array}
           */
          customData : {
              get : function() {
                  return this._customData;
              }
          },
  
          /**
           * Gets a value indicating whether or not this tile needs further loading.
           * This property will return true if the {@link QuadtreeTile#state} is
           * <code>START</code> or <code>LOADING</code>.
           * @memberof QuadtreeTile.prototype
           * @type {Boolean}
           */
          needsLoading : {
              get : function() {
                  return this.state < QuadtreeTileLoadState.DONE;
              }
          },
  
          /**
           * Gets a value indicating whether or not this tile is eligible to be unloaded.
           * Typically, a tile is ineligible to be unloaded while an asynchronous operation,
           * such as a request for data, is in progress on it.  A tile will never be
           * unloaded while it is needed for rendering, regardless of the value of this
           * property.  If {@link QuadtreeTile#data} is defined and has an
           * <code>eligibleForUnloading</code> property, the value of that property is returned.
           * Otherwise, this property returns true.
           * @memberof QuadtreeTile.prototype
           * @type {Boolean}
           */
          eligibleForUnloading : {
              get : function() {
                  var result = true;
  
                  if (defined(this.data)) {
                      result = this.data.eligibleForUnloading;
                      if (!defined(result)) {
                          result = true;
                      }
                  }
  
                  return result;
              }
          }
      });
  
      /**
       * Frees the resources associated with this tile and returns it to the <code>START</code>
       * {@link QuadtreeTileLoadState}.  If the {@link QuadtreeTile#data} property is defined and it
       * has a <code>freeResources</code> method, the method will be invoked.
       *
       * @memberof QuadtreeTile
       */
      QuadtreeTile.prototype.freeResources = function() {
          this.state = QuadtreeTileLoadState.START;
          this.renderable = false;
          this.upsampledFromParent = false;
  
          if (defined(this.data) && defined(this.data.freeResources)) {
              this.data.freeResources();
          }
  
          if (defined(this._children)) {
              for (var i = 0, len = this._children.length; i < len; ++i) {
                  this._children[i].freeResources();
              }
              this._children = undefined;
          }
      };
  
      return QuadtreeTile;
  });
  