'use strict';

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

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

var _index2 = _interopRequireDefault(_index);

var _event = require('../../render/event.js');

var _event2 = _interopRequireDefault(_event);

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

var _eventtype2 = _interopRequireDefault(_eventtype);

var _immediate = require('../../render/webgl/immediate.js');

var _immediate2 = _interopRequireDefault(_immediate);

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

var _layer2 = _interopRequireDefault(_layer);

var _defaultmapshader = require('../webgl/defaultmapshader.js');

var _defaultmapshader2 = _interopRequireDefault(_defaultmapshader);

var _locations = require('../webgl/defaultmapshader/locations.js');

var _locations2 = _interopRequireDefault(_locations);

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

var _transform2 = _interopRequireDefault(_transform);

var _mat = require('../../vec/mat4.js');

var _mat2 = _interopRequireDefault(_mat);

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

var _webgl2 = _interopRequireDefault(_webgl);

var _buffer = require('../../webgl/buffer.js');

var _buffer2 = _interopRequireDefault(_buffer);

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

var _context2 = _interopRequireDefault(_context);

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

/**
 * @constructor
 * @abstract
 * @extends {ol.renderer.Layer}
 * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
 * @param {ol.layer.Layer} layer Layer.
 */
var _ol_renderer_webgl_Layer_ = function (mapRenderer, layer) {

  _layer2.default.call(this, layer);

  /**
   * @protected
   * @type {ol.renderer.webgl.Map}
   */
  this.mapRenderer = mapRenderer;

  /**
   * @private
   * @type {ol.webgl.Buffer}
   */
  this.arrayBuffer_ = new _buffer2.default([-1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1]);

  /**
   * @protected
   * @type {WebGLTexture}
   */
  this.texture = null;

  /**
   * @protected
   * @type {WebGLFramebuffer}
   */
  this.framebuffer = null;

  /**
   * @protected
   * @type {number|undefined}
   */
  this.framebufferDimension = undefined;

  /**
   * @protected
   * @type {ol.Transform}
   */
  this.texCoordMatrix = _transform2.default.create();

  /**
   * @protected
   * @type {ol.Transform}
   */
  this.projectionMatrix = _transform2.default.create();

  /**
   * @type {Array.<number>}
   * @private
   */
  this.tmpMat4_ = _mat2.default.create();

  /**
   * @private
   * @type {ol.renderer.webgl.defaultmapshader.Locations}
   */
  this.defaultLocations_ = null;
};

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

/**
 * @param {olx.FrameState} frameState Frame state.
 * @param {number} framebufferDimension Framebuffer dimension.
 * @protected
 */
_ol_renderer_webgl_Layer_.prototype.bindFramebuffer = function (frameState, framebufferDimension) {

  var gl = this.mapRenderer.getGL();

  if (this.framebufferDimension === undefined || this.framebufferDimension != framebufferDimension) {
    /**
     * @param {WebGLRenderingContext} gl GL.
     * @param {WebGLFramebuffer} framebuffer Framebuffer.
     * @param {WebGLTexture} texture Texture.
     */
    var postRenderFunction = function (gl, framebuffer, texture) {
      if (!gl.isContextLost()) {
        gl.deleteFramebuffer(framebuffer);
        gl.deleteTexture(texture);
      }
    }.bind(null, gl, this.framebuffer, this.texture);

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

    var texture = _context2.default.createEmptyTexture(gl, framebufferDimension, framebufferDimension);

    var framebuffer = gl.createFramebuffer();
    gl.bindFramebuffer(_webgl2.default.FRAMEBUFFER, framebuffer);
    gl.framebufferTexture2D(_webgl2.default.FRAMEBUFFER, _webgl2.default.COLOR_ATTACHMENT0, _webgl2.default.TEXTURE_2D, texture, 0);

    this.texture = texture;
    this.framebuffer = framebuffer;
    this.framebufferDimension = framebufferDimension;
  } else {
    gl.bindFramebuffer(_webgl2.default.FRAMEBUFFER, this.framebuffer);
  }
};

/**
 * @param {olx.FrameState} frameState Frame state.
 * @param {ol.LayerState} layerState Layer state.
 * @param {ol.webgl.Context} context Context.
 */
_ol_renderer_webgl_Layer_.prototype.composeFrame = function (frameState, layerState, context) {

  this.dispatchComposeEvent_(_eventtype2.default.PRECOMPOSE, context, frameState);

  context.bindBuffer(_webgl2.default.ARRAY_BUFFER, this.arrayBuffer_);

  var gl = context.getGL();

  var fragmentShader = _defaultmapshader2.default.fragment;
  var vertexShader = _defaultmapshader2.default.vertex;

  var program = context.getProgram(fragmentShader, vertexShader);

  var locations;
  if (!this.defaultLocations_) {
    locations = new _locations2.default(gl, program);
    this.defaultLocations_ = locations;
  } else {
    locations = this.defaultLocations_;
  }

  if (context.useProgram(program)) {
    gl.enableVertexAttribArray(locations.a_position);
    gl.vertexAttribPointer(locations.a_position, 2, _webgl2.default.FLOAT, false, 16, 0);
    gl.enableVertexAttribArray(locations.a_texCoord);
    gl.vertexAttribPointer(locations.a_texCoord, 2, _webgl2.default.FLOAT, false, 16, 8);
    gl.uniform1i(locations.u_texture, 0);
  }

  gl.uniformMatrix4fv(locations.u_texCoordMatrix, false, _mat2.default.fromTransform(this.tmpMat4_, this.getTexCoordMatrix()));
  gl.uniformMatrix4fv(locations.u_projectionMatrix, false, _mat2.default.fromTransform(this.tmpMat4_, this.getProjectionMatrix()));
  gl.uniform1f(locations.u_opacity, layerState.opacity);
  gl.bindTexture(_webgl2.default.TEXTURE_2D, this.getTexture());
  gl.drawArrays(_webgl2.default.TRIANGLE_STRIP, 0, 4);

  this.dispatchComposeEvent_(_eventtype2.default.POSTCOMPOSE, context, frameState);
};

/**
 * @param {ol.render.EventType} type Event type.
 * @param {ol.webgl.Context} context WebGL context.
 * @param {olx.FrameState} frameState Frame state.
 * @private
 */
_ol_renderer_webgl_Layer_.prototype.dispatchComposeEvent_ = function (type, context, frameState) {
  var layer = this.getLayer();
  if (layer.hasListener(type)) {
    var viewState = frameState.viewState;
    var resolution = viewState.resolution;
    var pixelRatio = frameState.pixelRatio;
    var extent = frameState.extent;
    var center = viewState.center;
    var rotation = viewState.rotation;
    var size = frameState.size;

    var render = new _immediate2.default(context, center, resolution, rotation, size, extent, pixelRatio);
    var composeEvent = new _event2.default(type, render, frameState, null, context);
    layer.dispatchEvent(composeEvent);
  }
};

/**
 * @return {!ol.Transform} Matrix.
 */
_ol_renderer_webgl_Layer_.prototype.getTexCoordMatrix = function () {
  return this.texCoordMatrix;
};

/**
 * @return {WebGLTexture} Texture.
 */
_ol_renderer_webgl_Layer_.prototype.getTexture = function () {
  return this.texture;
};

/**
 * @return {!ol.Transform} Matrix.
 */
_ol_renderer_webgl_Layer_.prototype.getProjectionMatrix = function () {
  return this.projectionMatrix;
};

/**
 * Handle webglcontextlost.
 */
_ol_renderer_webgl_Layer_.prototype.handleWebGLContextLost = function () {
  this.texture = null;
  this.framebuffer = null;
  this.framebufferDimension = undefined;
};

/**
 * @abstract
 * @param {olx.FrameState} frameState Frame state.
 * @param {ol.LayerState} layerState Layer state.
 * @param {ol.webgl.Context} context Context.
 * @return {boolean} whether composeFrame should be called.
 */
_ol_renderer_webgl_Layer_.prototype.prepareFrame = function (frameState, layerState, context) {};

/**
 * @abstract
 * @param {ol.Pixel} pixel Pixel.
 * @param {olx.FrameState} frameState FrameState.
 * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer
 *     callback.
 * @param {S} thisArg Value to use as `this` when executing `callback`.
 * @return {T|undefined} Callback result.
 * @template S,T,U
 */
_ol_renderer_webgl_Layer_.prototype.forEachLayerAtPixel = function (pixel, frameState, callback, thisArg) {};
exports.default = _ol_renderer_webgl_Layer_;
module.exports = exports['default'];