'use strict';

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

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

var _index2 = _interopRequireDefault(_index);

var _layertype = require('../../layertype.js');

var _layertype2 = _interopRequireDefault(_layertype);

var _viewhint = require('../../viewhint.js');

var _viewhint2 = _interopRequireDefault(_viewhint);

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

var _dom2 = _interopRequireDefault(_dom);

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

var _extent2 = _interopRequireDefault(_extent);

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

var _functions2 = _interopRequireDefault(_functions);

var _type = require('../type.js');

var _type2 = _interopRequireDefault(_type);

var _layer = require('../webgl/layer.js');

var _layer2 = _interopRequireDefault(_layer);

var _transform = require('../../transform.js');

var _transform2 = _interopRequireDefault(_transform);

var _webgl = require('../../webgl.js');

var _webgl2 = _interopRequireDefault(_webgl);

var _context = require('../../webgl/context.js');

var _context2 = _interopRequireDefault(_context);

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

/**
 * @constructor
 * @extends {ol.renderer.webgl.Layer}
 * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
 * @param {ol.layer.Image} imageLayer Tile layer.
 * @api
 */
var _ol_renderer_webgl_ImageLayer_ = function (mapRenderer, imageLayer) {

  _layer2.default.call(this, mapRenderer, imageLayer);

  /**
   * The last rendered image.
   * @private
   * @type {?ol.ImageBase}
   */
  this.image_ = null;

  /**
   * @private
   * @type {CanvasRenderingContext2D}
   */
  this.hitCanvasContext_ = null;

  /**
   * @private
   * @type {?ol.Transform}
   */
  this.hitTransformationMatrix_ = null;
};

_index2.default.inherits(_ol_renderer_webgl_ImageLayer_, _layer2.default);

/**
 * Determine if this renderer handles the provided layer.
 * @param {ol.renderer.Type} type The renderer type.
 * @param {ol.layer.Layer} layer The candidate layer.
 * @return {boolean} The renderer can render the layer.
 */
_ol_renderer_webgl_ImageLayer_['handles'] = function (type, layer) {
  return type === _type2.default.WEBGL && layer.getType() === _layertype2.default.IMAGE;
};

/**
 * Create a layer renderer.
 * @param {ol.renderer.Map} mapRenderer The map renderer.
 * @param {ol.layer.Layer} layer The layer to be rendererd.
 * @return {ol.renderer.webgl.ImageLayer} The layer renderer.
 */
_ol_renderer_webgl_ImageLayer_['create'] = function (mapRenderer, layer) {
  return new _ol_renderer_webgl_ImageLayer_(
  /** @type {ol.renderer.webgl.Map} */mapRenderer,
  /** @type {ol.layer.Image} */layer);
};

/**
 * @param {ol.ImageBase} image Image.
 * @private
 * @return {WebGLTexture} Texture.
 */
_ol_renderer_webgl_ImageLayer_.prototype.createTexture_ = function (image) {

  // We meet the conditions to work with non-power of two textures.
  // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support
  // http://learningwebgl.com/blog/?p=2101

  var imageElement = image.getImage();
  var gl = this.mapRenderer.getGL();

  return _context2.default.createTexture(gl, imageElement, _webgl2.default.CLAMP_TO_EDGE, _webgl2.default.CLAMP_TO_EDGE);
};

/**
 * @inheritDoc
 */
_ol_renderer_webgl_ImageLayer_.prototype.forEachFeatureAtCoordinate = function (coordinate, frameState, hitTolerance, callback, thisArg) {
  var layer = this.getLayer();
  var source = layer.getSource();
  var resolution = frameState.viewState.resolution;
  var rotation = frameState.viewState.rotation;
  var skippedFeatureUids = frameState.skippedFeatureUids;
  return source.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, skippedFeatureUids,

  /**
   * @param {ol.Feature|ol.render.Feature} feature Feature.
   * @return {?} Callback result.
   */
  function (feature) {
    return callback.call(thisArg, feature, layer);
  });
};

/**
 * @inheritDoc
 */
_ol_renderer_webgl_ImageLayer_.prototype.prepareFrame = function (frameState, layerState, context) {

  var gl = this.mapRenderer.getGL();

  var pixelRatio = frameState.pixelRatio;
  var viewState = frameState.viewState;
  var viewCenter = viewState.center;
  var viewResolution = viewState.resolution;
  var viewRotation = viewState.rotation;

  var image = this.image_;
  var texture = this.texture;
  var imageLayer = /** @type {ol.layer.Image} */this.getLayer();
  var imageSource = imageLayer.getSource();

  var hints = frameState.viewHints;

  var renderedExtent = frameState.extent;
  if (layerState.extent !== undefined) {
    renderedExtent = _extent2.default.getIntersection(renderedExtent, layerState.extent);
  }
  if (!hints[_viewhint2.default.ANIMATING] && !hints[_viewhint2.default.INTERACTING] && !_extent2.default.isEmpty(renderedExtent)) {
    var projection = viewState.projection;
    if (!_index2.default.ENABLE_RASTER_REPROJECTION) {
      var sourceProjection = imageSource.getProjection();
      if (sourceProjection) {
        projection = sourceProjection;
      }
    }
    var image_ = imageSource.getImage(renderedExtent, viewResolution, pixelRatio, projection);
    if (image_) {
      var loaded = this.loadImage(image_);
      if (loaded) {
        image = image_;
        texture = this.createTexture_(image_);
        if (this.texture) {
          /**
           * @param {WebGLRenderingContext} gl GL.
           * @param {WebGLTexture} texture Texture.
           */
          var postRenderFunction = function (gl, texture) {
            if (!gl.isContextLost()) {
              gl.deleteTexture(texture);
            }
          }.bind(null, gl, this.texture);
          frameState.postRenderFunctions.push(
          /** @type {ol.PostRenderFunction} */postRenderFunction);
        }
      }
    }
  }

  if (image) {
    var canvas = this.mapRenderer.getContext().getCanvas();

    this.updateProjectionMatrix_(canvas.width, canvas.height, pixelRatio, viewCenter, viewResolution, viewRotation, image.getExtent());
    this.hitTransformationMatrix_ = null;

    // Translate and scale to flip the Y coord.
    var texCoordMatrix = this.texCoordMatrix;
    _transform2.default.reset(texCoordMatrix);
    _transform2.default.scale(texCoordMatrix, 1, -1);
    _transform2.default.translate(texCoordMatrix, 0, -1);

    this.image_ = image;
    this.texture = texture;

    this.updateLogos(frameState, imageSource);
  }

  return !!image;
};

/**
 * @param {number} canvasWidth Canvas width.
 * @param {number} canvasHeight Canvas height.
 * @param {number} pixelRatio Pixel ratio.
 * @param {ol.Coordinate} viewCenter View center.
 * @param {number} viewResolution View resolution.
 * @param {number} viewRotation View rotation.
 * @param {ol.Extent} imageExtent Image extent.
 * @private
 */
_ol_renderer_webgl_ImageLayer_.prototype.updateProjectionMatrix_ = function (canvasWidth, canvasHeight, pixelRatio, viewCenter, viewResolution, viewRotation, imageExtent) {

  var canvasExtentWidth = canvasWidth * viewResolution;
  var canvasExtentHeight = canvasHeight * viewResolution;

  var projectionMatrix = this.projectionMatrix;
  _transform2.default.reset(projectionMatrix);
  _transform2.default.scale(projectionMatrix, pixelRatio * 2 / canvasExtentWidth, pixelRatio * 2 / canvasExtentHeight);
  _transform2.default.rotate(projectionMatrix, -viewRotation);
  _transform2.default.translate(projectionMatrix, imageExtent[0] - viewCenter[0], imageExtent[1] - viewCenter[1]);
  _transform2.default.scale(projectionMatrix, (imageExtent[2] - imageExtent[0]) / 2, (imageExtent[3] - imageExtent[1]) / 2);
  _transform2.default.translate(projectionMatrix, 1, 1);
};

/**
 * @inheritDoc
 */
_ol_renderer_webgl_ImageLayer_.prototype.hasFeatureAtCoordinate = function (coordinate, frameState) {
  var hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, _functions2.default.TRUE, this);
  return hasFeature !== undefined;
};

/**
 * @inheritDoc
 */
_ol_renderer_webgl_ImageLayer_.prototype.forEachLayerAtPixel = function (pixel, frameState, callback, thisArg) {
  if (!this.image_ || !this.image_.getImage()) {
    return undefined;
  }

  if (this.getLayer().getSource().forEachFeatureAtCoordinate !== _index2.default.nullFunction) {
    // for ImageCanvas sources use the original hit-detection logic,
    // so that for example also transparent polygons are detected
    var coordinate = _transform2.default.apply(frameState.pixelToCoordinateTransform, pixel.slice());
    var hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, _functions2.default.TRUE, this);

    if (hasFeature) {
      return callback.call(thisArg, this.getLayer(), null);
    } else {
      return undefined;
    }
  } else {
    var imageSize = [this.image_.getImage().width, this.image_.getImage().height];

    if (!this.hitTransformationMatrix_) {
      this.hitTransformationMatrix_ = this.getHitTransformationMatrix_(frameState.size, imageSize);
    }

    var pixelOnFrameBuffer = _transform2.default.apply(this.hitTransformationMatrix_, pixel.slice());

    if (pixelOnFrameBuffer[0] < 0 || pixelOnFrameBuffer[0] > imageSize[0] || pixelOnFrameBuffer[1] < 0 || pixelOnFrameBuffer[1] > imageSize[1]) {
      // outside the image, no need to check
      return undefined;
    }

    if (!this.hitCanvasContext_) {
      this.hitCanvasContext_ = _dom2.default.createCanvasContext2D(1, 1);
    }

    this.hitCanvasContext_.clearRect(0, 0, 1, 1);
    this.hitCanvasContext_.drawImage(this.image_.getImage(), pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, 0, 0, 1, 1);

    var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data;
    if (imageData[3] > 0) {
      return callback.call(thisArg, this.getLayer(), imageData);
    } else {
      return undefined;
    }
  }
};

/**
 * The transformation matrix to get the pixel on the image for a
 * pixel on the map.
 * @param {ol.Size} mapSize The map size.
 * @param {ol.Size} imageSize The image size.
 * @return {ol.Transform} The transformation matrix.
 * @private
 */
_ol_renderer_webgl_ImageLayer_.prototype.getHitTransformationMatrix_ = function (mapSize, imageSize) {
  // the first matrix takes a map pixel, flips the y-axis and scales to
  // a range between -1 ... 1
  var mapCoordTransform = _transform2.default.create();
  _transform2.default.translate(mapCoordTransform, -1, -1);
  _transform2.default.scale(mapCoordTransform, 2 / mapSize[0], 2 / mapSize[1]);
  _transform2.default.translate(mapCoordTransform, 0, mapSize[1]);
  _transform2.default.scale(mapCoordTransform, 1, -1);

  // the second matrix is the inverse of the projection matrix used in the
  // shader for drawing
  var projectionMatrixInv = _transform2.default.invert(this.projectionMatrix.slice());

  // the third matrix scales to the image dimensions and flips the y-axis again
  var transform = _transform2.default.create();
  _transform2.default.translate(transform, 0, imageSize[1]);
  _transform2.default.scale(transform, 1, -1);
  _transform2.default.scale(transform, imageSize[0] / 2, imageSize[1] / 2);
  _transform2.default.translate(transform, 1, 1);

  _transform2.default.multiply(transform, projectionMatrixInv);
  _transform2.default.multiply(transform, mapCoordTransform);

  return transform;
};
exports.default = _ol_renderer_webgl_ImageLayer_;
module.exports = exports['default'];