'use strict';

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

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

var _index2 = _interopRequireDefault(_index);

var _tile = require('./tile.js');

var _tile2 = _interopRequireDefault(_tile);

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

var _tilestate2 = _interopRequireDefault(_tilestate);

var _dom = require('./dom.js');

var _dom2 = _interopRequireDefault(_dom);

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

var _events2 = _interopRequireDefault(_events);

var _extent = require('./extent.js');

var _extent2 = _interopRequireDefault(_extent);

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

var _eventtype2 = _interopRequireDefault(_eventtype);

var _featureloader = require('./featureloader.js');

var _featureloader2 = _interopRequireDefault(_featureloader);

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

/**
 * @constructor
 * @extends {ol.Tile}
 * @param {ol.TileCoord} tileCoord Tile coordinate.
 * @param {ol.TileState} state State.
 * @param {number} sourceRevision Source revision.
 * @param {ol.format.Feature} format Feature format.
 * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
 * @param {ol.TileCoord} urlTileCoord Wrapped tile coordinate for source urls.
 * @param {ol.TileUrlFunctionType} tileUrlFunction Tile url function.
 * @param {ol.tilegrid.TileGrid} sourceTileGrid Tile grid of the source.
 * @param {ol.tilegrid.TileGrid} tileGrid Tile grid of the renderer.
 * @param {Object.<string,ol.VectorTile>} sourceTiles Source tiles.
 * @param {number} pixelRatio Pixel ratio.
 * @param {ol.proj.Projection} projection Projection.
 * @param {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string,
 *     ol.format.Feature, ol.TileLoadFunctionType)} tileClass Class to
 *     instantiate for source tiles.
 * @param {function(this: ol.source.VectorTile, ol.events.Event)} handleTileChange
 *     Function to call when a source tile's state changes.
 * @param {olx.TileOptions=} opt_options Tile options.
 */
var _ol_VectorImageTile_ = function (tileCoord, state, sourceRevision, format, tileLoadFunction, urlTileCoord, tileUrlFunction, sourceTileGrid, tileGrid, sourceTiles, pixelRatio, projection, tileClass, handleTileChange, opt_options) {

  _tile2.default.call(this, tileCoord, state, opt_options);

  /**
   * @private
   * @type {Object.<string, CanvasRenderingContext2D>}
   */
  this.context_ = {};

  /**
   * @private
   * @type {ol.FeatureLoader}
   */
  this.loader_;

  /**
   * @private
   * @type {Object.<string, ol.TileReplayState>}
   */
  this.replayState_ = {};

  /**
   * @private
   * @type {Object.<string,ol.VectorTile>}
   */
  this.sourceTiles_ = sourceTiles;

  /**
   * Keys of source tiles used by this tile. Use with {@link #getTile}.
   * @type {Array.<string>}
   */
  this.tileKeys = [];

  /**
   * @type {number}
   */
  this.sourceRevision_ = sourceRevision;

  /**
   * @type {ol.TileCoord}
   */
  this.wrappedTileCoord = urlTileCoord;

  /**
   * @type {Array.<ol.EventsKey>}
   */
  this.loadListenerKeys_ = [];

  /**
   * @type {Array.<ol.EventsKey>}
   */
  this.sourceTileListenerKeys_ = [];

  if (urlTileCoord) {
    var extent = tileGrid.getTileCoordExtent(urlTileCoord);
    var resolution = tileGrid.getResolution(tileCoord[0]);
    var sourceZ = sourceTileGrid.getZForResolution(resolution);
    sourceTileGrid.forEachTileCoord(extent, sourceZ, function (sourceTileCoord) {
      var sharedExtent = _extent2.default.getIntersection(extent, sourceTileGrid.getTileCoordExtent(sourceTileCoord));
      var sourceExtent = sourceTileGrid.getExtent();
      if (sourceExtent) {
        sharedExtent = _extent2.default.getIntersection(sharedExtent, sourceExtent);
      }
      if (_extent2.default.getWidth(sharedExtent) / resolution >= 0.5 && _extent2.default.getHeight(sharedExtent) / resolution >= 0.5) {
        // only include source tile if overlap is at least 1 pixel
        var sourceTileKey = sourceTileCoord.toString();
        var sourceTile = sourceTiles[sourceTileKey];
        if (!sourceTile) {
          var tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection);
          sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord, tileUrl == undefined ? _tilestate2.default.EMPTY : _tilestate2.default.IDLE, tileUrl == undefined ? '' : tileUrl, format, tileLoadFunction);
          this.sourceTileListenerKeys_.push(_events2.default.listen(sourceTile, _eventtype2.default.CHANGE, handleTileChange));
        }
        sourceTile.consumers++;
        this.tileKeys.push(sourceTileKey);
      }
    }.bind(this));
  }
};

_index2.default.inherits(_ol_VectorImageTile_, _tile2.default);

/**
 * @inheritDoc
 */
_ol_VectorImageTile_.prototype.disposeInternal = function () {
  for (var i = 0, ii = this.tileKeys.length; i < ii; ++i) {
    var sourceTileKey = this.tileKeys[i];
    var sourceTile = this.getTile(sourceTileKey);
    sourceTile.consumers--;
    if (sourceTile.consumers == 0) {
      delete this.sourceTiles_[sourceTileKey];
      sourceTile.dispose();
    }
  }
  this.tileKeys.length = 0;
  this.sourceTiles_ = null;
  this.loadListenerKeys_.forEach(_events2.default.unlistenByKey);
  this.loadListenerKeys_.length = 0;
  if (this.interimTile) {
    this.interimTile.dispose();
  }
  this.state = _tilestate2.default.ABORT;
  this.changed();
  this.sourceTileListenerKeys_.forEach(_events2.default.unlistenByKey);
  this.sourceTileListenerKeys_.length = 0;
  _tile2.default.prototype.disposeInternal.call(this);
};

/**
 * @param {ol.layer.Layer} layer Layer.
 * @return {CanvasRenderingContext2D} The rendering context.
 */
_ol_VectorImageTile_.prototype.getContext = function (layer) {
  var key = _index2.default.getUid(layer).toString();
  if (!(key in this.context_)) {
    this.context_[key] = _dom2.default.createCanvasContext2D();
  }
  return this.context_[key];
};

/**
 * Get the Canvas for this tile.
 * @param {ol.layer.Layer} layer Layer.
 * @return {HTMLCanvasElement} Canvas.
 */
_ol_VectorImageTile_.prototype.getImage = function (layer) {
  return this.getReplayState(layer).renderedTileRevision == -1 ? null : this.getContext(layer).canvas;
};

/**
 * @param {ol.layer.Layer} layer Layer.
 * @return {ol.TileReplayState} The replay state.
 */
_ol_VectorImageTile_.prototype.getReplayState = function (layer) {
  var key = _index2.default.getUid(layer).toString();
  if (!(key in this.replayState_)) {
    this.replayState_[key] = {
      dirty: false,
      renderedRenderOrder: null,
      renderedRevision: -1,
      renderedTileRevision: -1
    };
  }
  return this.replayState_[key];
};

/**
 * @inheritDoc
 */
_ol_VectorImageTile_.prototype.getKey = function () {
  return this.tileKeys.join('/') + '-' + this.sourceRevision_;
};

/**
 * @param {string} tileKey Key (tileCoord) of the source tile.
 * @return {ol.VectorTile} Source tile.
 */
_ol_VectorImageTile_.prototype.getTile = function (tileKey) {
  return this.sourceTiles_[tileKey];
};

/**
 * @inheritDoc
 */
_ol_VectorImageTile_.prototype.load = function () {
  // Source tiles with LOADED state - we just count them because once they are
  // loaded, we're no longer listening to state changes.
  var leftToLoad = 0;
  // Source tiles with ERROR state - we track them because they can still have
  // an ERROR state after another load attempt.
  var errorSourceTiles = {};

  if (this.state == _tilestate2.default.IDLE) {
    this.setState(_tilestate2.default.LOADING);
  }
  if (this.state == _tilestate2.default.LOADING) {
    this.tileKeys.forEach(function (sourceTileKey) {
      var sourceTile = this.getTile(sourceTileKey);
      if (sourceTile.state == _tilestate2.default.IDLE) {
        sourceTile.setLoader(this.loader_);
        sourceTile.load();
      }
      if (sourceTile.state == _tilestate2.default.LOADING) {
        var key = _events2.default.listen(sourceTile, _eventtype2.default.CHANGE, function (e) {
          var state = sourceTile.getState();
          if (state == _tilestate2.default.LOADED || state == _tilestate2.default.ERROR) {
            var uid = _index2.default.getUid(sourceTile);
            if (state == _tilestate2.default.ERROR) {
              errorSourceTiles[uid] = true;
            } else {
              --leftToLoad;
              delete errorSourceTiles[uid];
            }
            if (leftToLoad - Object.keys(errorSourceTiles).length == 0) {
              this.finishLoading_();
            }
          }
        }.bind(this));
        this.loadListenerKeys_.push(key);
        ++leftToLoad;
      }
    }.bind(this));
  }
  if (leftToLoad - Object.keys(errorSourceTiles).length == 0) {
    setTimeout(this.finishLoading_.bind(this), 0);
  }
};

/**
 * @private
 */
_ol_VectorImageTile_.prototype.finishLoading_ = function () {
  var loaded = this.tileKeys.length;
  var empty = 0;
  for (var i = loaded - 1; i >= 0; --i) {
    var state = this.getTile(this.tileKeys[i]).getState();
    if (state != _tilestate2.default.LOADED) {
      --loaded;
    }
    if (state == _tilestate2.default.EMPTY) {
      ++empty;
    }
  }
  if (loaded == this.tileKeys.length) {
    this.loadListenerKeys_.forEach(_events2.default.unlistenByKey);
    this.loadListenerKeys_.length = 0;
    this.setState(_tilestate2.default.LOADED);
  } else {
    this.setState(empty == this.tileKeys.length ? _tilestate2.default.EMPTY : _tilestate2.default.ERROR);
  }
};

/**
 * Sets the loader for a tile.
 * @param {ol.VectorTile} tile Vector tile.
 * @param {string} url URL.
 */
_ol_VectorImageTile_.defaultLoadFunction = function (tile, url) {
  var loader = _featureloader2.default.loadFeaturesXhr(url, tile.getFormat(), tile.onLoad.bind(tile), tile.onError.bind(tile));

  tile.setLoader(loader);
};
exports.default = _ol_VectorImageTile_;
module.exports = exports['default'];