'use strict';

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

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

var _index2 = _interopRequireDefault(_index);

var _array = require('../../array.js');

var _array2 = _interopRequireDefault(_array);

var _color = require('../../color.js');

var _color2 = _interopRequireDefault(_color);

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

var _extent2 = _interopRequireDefault(_extent);

var _obj = require('../../obj.js');

var _obj2 = _interopRequireDefault(_obj);

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

var _transform2 = _interopRequireDefault(_transform);

var _defaultshader = require('../webgl/circlereplay/defaultshader.js');

var _defaultshader2 = _interopRequireDefault(_defaultshader);

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

var _locations2 = _interopRequireDefault(_locations);

var _replay = require('../webgl/replay.js');

var _replay2 = _interopRequireDefault(_replay);

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

var _webgl2 = _interopRequireDefault(_webgl);

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

var _webgl4 = _interopRequireDefault(_webgl3);

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

var _buffer2 = _interopRequireDefault(_buffer);

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

/**
 * @constructor
 * @extends {ol.render.webgl.Replay}
 * @param {number} tolerance Tolerance.
 * @param {ol.Extent} maxExtent Max extent.
 * @struct
 */
var _ol_render_webgl_CircleReplay_ = function (tolerance, maxExtent) {
  _replay2.default.call(this, tolerance, maxExtent);

  /**
   * @private
   * @type {ol.render.webgl.circlereplay.defaultshader.Locations}
   */
  this.defaultLocations_ = null;

  /**
   * @private
   * @type {Array.<Array.<Array.<number>|number>>}
   */
  this.styles_ = [];

  /**
   * @private
   * @type {Array.<number>}
   */
  this.styleIndices_ = [];

  /**
   * @private
   * @type {number}
   */
  this.radius_ = 0;

  /**
   * @private
   * @type {{fillColor: (Array.<number>|null),
   *         strokeColor: (Array.<number>|null),
   *         lineDash: Array.<number>,
   *         lineDashOffset: (number|undefined),
   *         lineWidth: (number|undefined),
   *         changed: boolean}|null}
   */
  this.state_ = {
    fillColor: null,
    strokeColor: null,
    lineDash: null,
    lineDashOffset: undefined,
    lineWidth: undefined,
    changed: false
  };
};

_index2.default.inherits(_ol_render_webgl_CircleReplay_, _replay2.default);

/**
 * @private
 * @param {Array.<number>} flatCoordinates Flat coordinates.
 * @param {number} offset Offset.
 * @param {number} end End.
 * @param {number} stride Stride.
 */
_ol_render_webgl_CircleReplay_.prototype.drawCoordinates_ = function (flatCoordinates, offset, end, stride) {
  var numVertices = this.vertices.length;
  var numIndices = this.indices.length;
  var n = numVertices / 4;
  var i, ii;
  for (i = offset, ii = end; i < ii; i += stride) {
    this.vertices[numVertices++] = flatCoordinates[i];
    this.vertices[numVertices++] = flatCoordinates[i + 1];
    this.vertices[numVertices++] = 0;
    this.vertices[numVertices++] = this.radius_;

    this.vertices[numVertices++] = flatCoordinates[i];
    this.vertices[numVertices++] = flatCoordinates[i + 1];
    this.vertices[numVertices++] = 1;
    this.vertices[numVertices++] = this.radius_;

    this.vertices[numVertices++] = flatCoordinates[i];
    this.vertices[numVertices++] = flatCoordinates[i + 1];
    this.vertices[numVertices++] = 2;
    this.vertices[numVertices++] = this.radius_;

    this.vertices[numVertices++] = flatCoordinates[i];
    this.vertices[numVertices++] = flatCoordinates[i + 1];
    this.vertices[numVertices++] = 3;
    this.vertices[numVertices++] = this.radius_;

    this.indices[numIndices++] = n;
    this.indices[numIndices++] = n + 1;
    this.indices[numIndices++] = n + 2;

    this.indices[numIndices++] = n + 2;
    this.indices[numIndices++] = n + 3;
    this.indices[numIndices++] = n;

    n += 4;
  }
};

/**
 * @inheritDoc
 */
_ol_render_webgl_CircleReplay_.prototype.drawCircle = function (circleGeometry, feature) {
  var radius = circleGeometry.getRadius();
  var stride = circleGeometry.getStride();
  if (radius) {
    this.startIndices.push(this.indices.length);
    this.startIndicesFeature.push(feature);
    if (this.state_.changed) {
      this.styleIndices_.push(this.indices.length);
      this.state_.changed = false;
    }

    this.radius_ = radius;
    var flatCoordinates = circleGeometry.getFlatCoordinates();
    flatCoordinates = _transform2.default.translate(flatCoordinates, 0, 2, stride, -this.origin[0], -this.origin[1]);
    this.drawCoordinates_(flatCoordinates, 0, 2, stride);
  } else {
    if (this.state_.changed) {
      this.styles_.pop();
      if (this.styles_.length) {
        var lastState = this.styles_[this.styles_.length - 1];
        this.state_.fillColor = /** @type {Array.<number>} */lastState[0];
        this.state_.strokeColor = /** @type {Array.<number>} */lastState[1];
        this.state_.lineWidth = /** @type {number} */lastState[2];
        this.state_.changed = false;
      }
    }
  }
};

/**
 * @inheritDoc
 **/
_ol_render_webgl_CircleReplay_.prototype.finish = function (context) {
  // create, bind, and populate the vertices buffer
  this.verticesBuffer = new _buffer2.default(this.vertices);

  // create, bind, and populate the indices buffer
  this.indicesBuffer = new _buffer2.default(this.indices);

  this.startIndices.push(this.indices.length);

  //Clean up, if there is nothing to draw
  if (this.styleIndices_.length === 0 && this.styles_.length > 0) {
    this.styles_ = [];
  }

  this.vertices = null;
  this.indices = null;
};

/**
 * @inheritDoc
 */
_ol_render_webgl_CircleReplay_.prototype.getDeleteResourcesFunction = function (context) {
  // We only delete our stuff here. The shaders and the program may
  // be used by other CircleReplay instances (for other layers). And
  // they will be deleted when disposing of the ol.webgl.Context
  // object.
  var verticesBuffer = this.verticesBuffer;
  var indicesBuffer = this.indicesBuffer;
  return function () {
    context.deleteBuffer(verticesBuffer);
    context.deleteBuffer(indicesBuffer);
  };
};

/**
 * @inheritDoc
 */
_ol_render_webgl_CircleReplay_.prototype.setUpProgram = function (gl, context, size, pixelRatio) {
  // get the program
  var fragmentShader, vertexShader;
  fragmentShader = _defaultshader2.default.fragment;
  vertexShader = _defaultshader2.default.vertex;
  var program = context.getProgram(fragmentShader, vertexShader);

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

  context.useProgram(program);

  // enable the vertex attrib arrays
  gl.enableVertexAttribArray(locations.a_position);
  gl.vertexAttribPointer(locations.a_position, 2, _webgl4.default.FLOAT, false, 16, 0);

  gl.enableVertexAttribArray(locations.a_instruction);
  gl.vertexAttribPointer(locations.a_instruction, 1, _webgl4.default.FLOAT, false, 16, 8);

  gl.enableVertexAttribArray(locations.a_radius);
  gl.vertexAttribPointer(locations.a_radius, 1, _webgl4.default.FLOAT, false, 16, 12);

  // Enable renderer specific uniforms.
  gl.uniform2fv(locations.u_size, size);
  gl.uniform1f(locations.u_pixelRatio, pixelRatio);

  return locations;
};

/**
 * @inheritDoc
 */
_ol_render_webgl_CircleReplay_.prototype.shutDownProgram = function (gl, locations) {
  gl.disableVertexAttribArray(locations.a_position);
  gl.disableVertexAttribArray(locations.a_instruction);
  gl.disableVertexAttribArray(locations.a_radius);
};

/**
 * @inheritDoc
 */
_ol_render_webgl_CircleReplay_.prototype.drawReplay = function (gl, context, skippedFeaturesHash, hitDetection) {
  if (!_obj2.default.isEmpty(skippedFeaturesHash)) {
    this.drawReplaySkipping_(gl, context, skippedFeaturesHash);
  } else {
    //Draw by style groups to minimize drawElements() calls.
    var i, start, end, nextStyle;
    end = this.startIndices[this.startIndices.length - 1];
    for (i = this.styleIndices_.length - 1; i >= 0; --i) {
      start = this.styleIndices_[i];
      nextStyle = this.styles_[i];
      this.setFillStyle_(gl, /** @type {Array.<number>} */nextStyle[0]);
      this.setStrokeStyle_(gl, /** @type {Array.<number>} */nextStyle[1],
      /** @type {number} */nextStyle[2]);
      this.drawElements(gl, context, start, end);
      end = start;
    }
  }
};

/**
 * @inheritDoc
 */
_ol_render_webgl_CircleReplay_.prototype.drawHitDetectionReplayOneByOne = function (gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {
  var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex;
  featureIndex = this.startIndices.length - 2;
  end = this.startIndices[featureIndex + 1];
  for (i = this.styleIndices_.length - 1; i >= 0; --i) {
    nextStyle = this.styles_[i];
    this.setFillStyle_(gl, /** @type {Array.<number>} */nextStyle[0]);
    this.setStrokeStyle_(gl, /** @type {Array.<number>} */nextStyle[1],
    /** @type {number} */nextStyle[2]);
    groupStart = this.styleIndices_[i];

    while (featureIndex >= 0 && this.startIndices[featureIndex] >= groupStart) {
      start = this.startIndices[featureIndex];
      feature = this.startIndicesFeature[featureIndex];
      featureUid = _index2.default.getUid(feature).toString();

      if (skippedFeaturesHash[featureUid] === undefined && feature.getGeometry() && (opt_hitExtent === undefined || _extent2.default.intersects(
      /** @type {Array<number>} */opt_hitExtent, feature.getGeometry().getExtent()))) {
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        this.drawElements(gl, context, start, end);

        var result = featureCallback(feature);

        if (result) {
          return result;
        }
      }
      featureIndex--;
      end = start;
    }
  }
  return undefined;
};

/**
 * @private
 * @param {WebGLRenderingContext} gl gl.
 * @param {ol.webgl.Context} context Context.
 * @param {Object} skippedFeaturesHash Ids of features to skip.
 */
_ol_render_webgl_CircleReplay_.prototype.drawReplaySkipping_ = function (gl, context, skippedFeaturesHash) {
  var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart;
  featureIndex = this.startIndices.length - 2;
  end = start = this.startIndices[featureIndex + 1];
  for (i = this.styleIndices_.length - 1; i >= 0; --i) {
    nextStyle = this.styles_[i];
    this.setFillStyle_(gl, /** @type {Array.<number>} */nextStyle[0]);
    this.setStrokeStyle_(gl, /** @type {Array.<number>} */nextStyle[1],
    /** @type {number} */nextStyle[2]);
    groupStart = this.styleIndices_[i];

    while (featureIndex >= 0 && this.startIndices[featureIndex] >= groupStart) {
      featureStart = this.startIndices[featureIndex];
      feature = this.startIndicesFeature[featureIndex];
      featureUid = _index2.default.getUid(feature).toString();

      if (skippedFeaturesHash[featureUid]) {
        if (start !== end) {
          this.drawElements(gl, context, start, end);
        }
        end = featureStart;
      }
      featureIndex--;
      start = featureStart;
    }
    if (start !== end) {
      this.drawElements(gl, context, start, end);
    }
    start = end = groupStart;
  }
};

/**
 * @private
 * @param {WebGLRenderingContext} gl gl.
 * @param {Array.<number>} color Color.
 */
_ol_render_webgl_CircleReplay_.prototype.setFillStyle_ = function (gl, color) {
  gl.uniform4fv(this.defaultLocations_.u_fillColor, color);
};

/**
 * @private
 * @param {WebGLRenderingContext} gl gl.
 * @param {Array.<number>} color Color.
 * @param {number} lineWidth Line width.
 */
_ol_render_webgl_CircleReplay_.prototype.setStrokeStyle_ = function (gl, color, lineWidth) {
  gl.uniform4fv(this.defaultLocations_.u_strokeColor, color);
  gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth);
};

/**
 * @inheritDoc
 */
_ol_render_webgl_CircleReplay_.prototype.setFillStrokeStyle = function (fillStyle, strokeStyle) {
  var strokeStyleColor, strokeStyleWidth;
  if (strokeStyle) {
    var strokeStyleLineDash = strokeStyle.getLineDash();
    this.state_.lineDash = strokeStyleLineDash ? strokeStyleLineDash : _webgl2.default.defaultLineDash;
    var strokeStyleLineDashOffset = strokeStyle.getLineDashOffset();
    this.state_.lineDashOffset = strokeStyleLineDashOffset ? strokeStyleLineDashOffset : _webgl2.default.defaultLineDashOffset;
    strokeStyleColor = strokeStyle.getColor();
    if (!(strokeStyleColor instanceof CanvasGradient) && !(strokeStyleColor instanceof CanvasPattern)) {
      strokeStyleColor = _color2.default.asArray(strokeStyleColor).map(function (c, i) {
        return i != 3 ? c / 255 : c;
      }) || _webgl2.default.defaultStrokeStyle;
    } else {
      strokeStyleColor = _webgl2.default.defaultStrokeStyle;
    }
    strokeStyleWidth = strokeStyle.getWidth();
    strokeStyleWidth = strokeStyleWidth !== undefined ? strokeStyleWidth : _webgl2.default.defaultLineWidth;
  } else {
    strokeStyleColor = [0, 0, 0, 0];
    strokeStyleWidth = 0;
  }
  var fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0];
  if (!(fillStyleColor instanceof CanvasGradient) && !(fillStyleColor instanceof CanvasPattern)) {
    fillStyleColor = _color2.default.asArray(fillStyleColor).map(function (c, i) {
      return i != 3 ? c / 255 : c;
    }) || _webgl2.default.defaultFillStyle;
  } else {
    fillStyleColor = _webgl2.default.defaultFillStyle;
  }
  if (!this.state_.strokeColor || !_array2.default.equals(this.state_.strokeColor, strokeStyleColor) || !this.state_.fillColor || !_array2.default.equals(this.state_.fillColor, fillStyleColor) || this.state_.lineWidth !== strokeStyleWidth) {
    this.state_.changed = true;
    this.state_.fillColor = fillStyleColor;
    this.state_.strokeColor = strokeStyleColor;
    this.state_.lineWidth = strokeStyleWidth;
    this.styles_.push([fillStyleColor, strokeStyleColor, strokeStyleWidth]);
  }
};
exports.default = _ol_render_webgl_CircleReplay_;
module.exports = exports['default'];