'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _index = require('../index.js');

var _index2 = _interopRequireDefault(_index);

var _imagestate = require('../imagestate.js');

var _imagestate2 = _interopRequireDefault(_imagestate);

var _observable = require('../observable.js');

var _observable2 = _interopRequireDefault(_observable);

var _tilestate = require('../tilestate.js');

var _tilestate2 = _interopRequireDefault(_tilestate);

var _asserts = require('../asserts.js');

var _asserts2 = _interopRequireDefault(_asserts);

var _events = require('../events.js');

var _events2 = _interopRequireDefault(_events);

var _eventtype = require('../events/eventtype.js');

var _eventtype2 = _interopRequireDefault(_eventtype);

var _functions = require('../functions.js');

var _functions2 = _interopRequireDefault(_functions);

var _state = require('../source/state.js');

var _state2 = _interopRequireDefault(_state);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}

/**
 * @constructor
 * @extends {ol.Observable}
 * @param {ol.layer.Layer} layer Layer.
 * @struct
 */
var _ol_renderer_Layer_ = function (layer) {

  _observable2.default.call(this);

  /**
   * @private
   * @type {ol.layer.Layer}
   */
  this.layer_ = layer;
};

_index2.default.inherits(_ol_renderer_Layer_, _observable2.default);

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {olx.FrameState} frameState Frame state.
 * @param {number} hitTolerance Hit tolerance in pixels.
 * @param {function(this: S, (ol.Feature|ol.render.Feature), ol.layer.Layer): T}
 *     callback Feature callback.
 * @param {S} thisArg Value to use as `this` when executing `callback`.
 * @return {T|undefined} Callback result.
 * @template S,T
 */
_ol_renderer_Layer_.prototype.forEachFeatureAtCoordinate = _index2.default.nullFunction;

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {olx.FrameState} frameState Frame state.
 * @return {boolean} Is there a feature at the given coordinate?
 */
_ol_renderer_Layer_.prototype.hasFeatureAtCoordinate = _functions2.default.FALSE;

/**
 * Create a function that adds loaded tiles to the tile lookup.
 * @param {ol.source.Tile} source Tile source.
 * @param {ol.proj.Projection} projection Projection of the tiles.
 * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
 *     tiles by zoom level.
 * @return {function(number, ol.TileRange):boolean} A function that can be
 *     called with a zoom level and a tile range to add loaded tiles to the
 *     lookup.
 * @protected
 */
_ol_renderer_Layer_.prototype.createLoadedTileFinder = function (source, projection, tiles) {
  return (
    /**
     * @param {number} zoom Zoom level.
     * @param {ol.TileRange} tileRange Tile range.
     * @return {boolean} The tile range is fully loaded.
     */
    function (zoom, tileRange) {
      function callback(tile) {
        if (!tiles[zoom]) {
          tiles[zoom] = {};
        }
        tiles[zoom][tile.tileCoord.toString()] = tile;
      }
      return source.forEachLoadedTile(projection, zoom, tileRange, callback);
    }
  );
};

/**
 * @return {ol.layer.Layer} Layer.
 */
_ol_renderer_Layer_.prototype.getLayer = function () {
  return this.layer_;
};

/**
 * Handle changes in image state.
 * @param {ol.events.Event} event Image change event.
 * @private
 */
_ol_renderer_Layer_.prototype.handleImageChange_ = function (event) {
  var image = /** @type {ol.Image} */event.target;
  if (image.getState() === _imagestate2.default.LOADED) {
    this.renderIfReadyAndVisible();
  }
};

/**
 * Load the image if not already loaded, and register the image change
 * listener if needed.
 * @param {ol.ImageBase} image Image.
 * @return {boolean} `true` if the image is already loaded, `false`
 *     otherwise.
 * @protected
 */
_ol_renderer_Layer_.prototype.loadImage = function (image) {
  var imageState = image.getState();
  if (imageState != _imagestate2.default.LOADED && imageState != _imagestate2.default.ERROR) {
    _events2.default.listen(image, _eventtype2.default.CHANGE, this.handleImageChange_, this);
  }
  if (imageState == _imagestate2.default.IDLE) {
    image.load();
    imageState = image.getState();
  }
  return imageState == _imagestate2.default.LOADED;
};

/**
 * @protected
 */
_ol_renderer_Layer_.prototype.renderIfReadyAndVisible = function () {
  var layer = this.getLayer();
  if (layer.getVisible() && layer.getSourceState() == _state2.default.READY) {
    this.changed();
  }
};

/**
 * @param {olx.FrameState} frameState Frame state.
 * @param {ol.source.Tile} tileSource Tile source.
 * @protected
 */
_ol_renderer_Layer_.prototype.scheduleExpireCache = function (frameState, tileSource) {
  if (tileSource.canExpireCache()) {
    /**
     * @param {ol.source.Tile} tileSource Tile source.
     * @param {ol.PluggableMap} map Map.
     * @param {olx.FrameState} frameState Frame state.
     */
    var postRenderFunction = function (tileSource, map, frameState) {
      var tileSourceKey = _index2.default.getUid(tileSource).toString();
      if (tileSourceKey in frameState.usedTiles) {
        tileSource.expireCache(frameState.viewState.projection, frameState.usedTiles[tileSourceKey]);
      }
    }.bind(null, tileSource);

    frameState.postRenderFunctions.push(
    /** @type {ol.PostRenderFunction} */postRenderFunction);
  }
};

/**
 * @param {olx.FrameState} frameState Frame state.
 * @param {ol.source.Source} source Source.
 * @protected
 */
_ol_renderer_Layer_.prototype.updateLogos = function (frameState, source) {
  var logo = source.getLogo();
  if (logo !== undefined) {
    if (typeof logo === 'string') {
      frameState.logos[logo] = '';
    } else if (logo) {
      _asserts2.default.assert(typeof logo.href == 'string', 44); // `logo.href` should be a string.
      _asserts2.default.assert(typeof logo.src == 'string', 45); // `logo.src` should be a string.
      frameState.logos[logo.src] = logo.href;
    }
  }
};

/**
 * @param {Object.<string, Object.<string, ol.TileRange>>} usedTiles Used tiles.
 * @param {ol.source.Tile} tileSource Tile source.
 * @param {number} z Z.
 * @param {ol.TileRange} tileRange Tile range.
 * @protected
 */
_ol_renderer_Layer_.prototype.updateUsedTiles = function (usedTiles, tileSource, z, tileRange) {
  // FIXME should we use tilesToDrawByZ instead?
  var tileSourceKey = _index2.default.getUid(tileSource).toString();
  var zKey = z.toString();
  if (tileSourceKey in usedTiles) {
    if (zKey in usedTiles[tileSourceKey]) {
      usedTiles[tileSourceKey][zKey].extend(tileRange);
    } else {
      usedTiles[tileSourceKey][zKey] = tileRange;
    }
  } else {
    usedTiles[tileSourceKey] = {};
    usedTiles[tileSourceKey][zKey] = tileRange;
  }
};

/**
 * Manage tile pyramid.
 * This function performs a number of functions related to the tiles at the
 * current zoom and lower zoom levels:
 * - registers idle tiles in frameState.wantedTiles so that they are not
 *   discarded by the tile queue
 * - enqueues missing tiles
 * @param {olx.FrameState} frameState Frame state.
 * @param {ol.source.Tile} tileSource Tile source.
 * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
 * @param {number} pixelRatio Pixel ratio.
 * @param {ol.proj.Projection} projection Projection.
 * @param {ol.Extent} extent Extent.
 * @param {number} currentZ Current Z.
 * @param {number} preload Load low resolution tiles up to 'preload' levels.
 * @param {function(this: T, ol.Tile)=} opt_tileCallback Tile callback.
 * @param {T=} opt_this Object to use as `this` in `opt_tileCallback`.
 * @protected
 * @template T
 */
_ol_renderer_Layer_.prototype.manageTilePyramid = function (frameState, tileSource, tileGrid, pixelRatio, projection, extent, currentZ, preload, opt_tileCallback, opt_this) {
  var tileSourceKey = _index2.default.getUid(tileSource).toString();
  if (!(tileSourceKey in frameState.wantedTiles)) {
    frameState.wantedTiles[tileSourceKey] = {};
  }
  var wantedTiles = frameState.wantedTiles[tileSourceKey];
  var tileQueue = frameState.tileQueue;
  var minZoom = tileGrid.getMinZoom();
  var tile, tileRange, tileResolution, x, y, z;
  for (z = minZoom; z <= currentZ; ++z) {
    tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange);
    tileResolution = tileGrid.getResolution(z);
    for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
      for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
        if (currentZ - z <= preload) {
          tile = tileSource.getTile(z, x, y, pixelRatio, projection);
          if (tile.getState() == _tilestate2.default.IDLE) {
            wantedTiles[tile.getKey()] = true;
            if (!tileQueue.isKeyQueued(tile.getKey())) {
              tileQueue.enqueue([tile, tileSourceKey, tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]);
            }
          }
          if (opt_tileCallback !== undefined) {
            opt_tileCallback.call(opt_this, tile);
          }
        } else {
          tileSource.useTile(z, x, y, projection);
        }
      }
    }
  }
};
exports.default = _ol_renderer_Layer_;
module.exports = exports['default'];