"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _constants = _interopRequireDefault(require("../constants"));

var _resource = _interopRequireDefault(require("./resource"));

var _webglUtils = require("../webgl-utils");

var _typedArrayUtils = require("../webgl-utils/typed-array-utils");

var _accessor = _interopRequireDefault(require("./accessor"));

var _utils = require("../utils");

var _assert = _interopRequireDefault(require("../utils/assert"));

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

function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return right[Symbol.hasInstance](left); } else { return left instanceof right; } }

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

var DEBUG_DATA_LENGTH = 10;

var Buffer =
/*#__PURE__*/
function (_Resource) {
  _inherits(Buffer, _Resource);

  function Buffer(gl) {
    var _this;

    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    _classCallCheck(this, Buffer);

    _this = _possibleConstructorReturn(this, (Buffer.__proto__ || Object.getPrototypeOf(Buffer)).call(this, gl, props)); // Supports signature `new Buffer(gl, new Float32Array(...)`

    if (ArrayBuffer.isView(props)) {
      props = {
        data: props
      };
    }

    _this.stubRemovedMethods('Buffer', 'v6.0', ['layout', 'setLayout', 'getIndexedParameter']); // In WebGL1, need to make sure we use GL.ELEMENT_ARRAY_BUFFER when initializing element buffers
    // otherwise buffer type will lock to generic (non-element) buffer
    // In WebGL2, we can use GL.COPY_READ_BUFFER which avoids locking the type here


    _this.target = props.target || (_this.gl.webgl2 ? _constants.default.COPY_READ_BUFFER : _constants.default.ARRAY_BUFFER);

    _this.initialize(props);

    Object.seal(_assertThisInitialized(_this));
    return _this;
  }

  _createClass(Buffer, [{
    key: "setAccessor",
    // Stores the accessor of data with the buffer, makes it easy to e.g. set it as an attribute later
    // {accessor,type,size = 1,offset = 0,stride = 0,normalized = false,integer = false,instanced = 0}
    value: function setAccessor(opts) {
      this.accessor = opts;
      return this;
    }
  }, {
    key: "updateAccessor",
    value: function updateAccessor(opts) {
      this.accessor.update(opts);
      return this;
    } // Creates and initializes the buffer object's data store.

  }, {
    key: "initialize",
    value: function initialize() {
      var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var data = props.data,
          bytes = props.bytes;
      var _props$usage = props.usage,
          usage = _props$usage === void 0 ? _constants.default.STATIC_DRAW : _props$usage;
      var type;

      if (data) {
        // infer type from typed array
        type = (0, _typedArrayUtils.getGLTypeFromTypedArray)(data);
        bytes = data.byteLength;
        (0, _assert.default)(type);
      } else if (!bytes || bytes === 0) {
        // Workaround needed for Safari (#291):
        // gl.bufferData with size (second argument) equal to 0 crashes.
        // hence create zero sized array.
        bytes = 0;
        data = new Float32Array(0);
      }

      this.usage = usage;
      this.bytes = bytes;
      this.bytesUsed = bytes;
      this._data = data; // TODO - remove

      this.debugData = data ? data.slice(0, DEBUG_DATA_LENGTH) : null; // Call after type is determined

      this.setAccessor(new _accessor.default(type ? {
        type: type
      } : {}, props)); // Create the buffer - binding it here for the first time locks the type
      // In WebGL2, use GL.COPY_WRITE_BUFFER to avoid locking the type

      var target = this.gl.webgl2 ? _constants.default.COPY_WRITE_BUFFER : this.target;
      this.gl.bindBuffer(target, this.handle);
      this.gl.bufferData(target, data || bytes, usage);
      this.gl.bindBuffer(target, null);
      return this;
    }
  }, {
    key: "setProps",
    value: function setProps(props) {
      if ('data' in props) {
        this.setData(props);
      }

      return this;
    }
  }, {
    key: "setData",
    value: function setData(opts) {
      return this.initialize(opts);
    } // Updates a subset of a buffer object's data store.
    // Data (Typed Array or ArrayBuffer), length is inferred unless provided
    // Offset into buffer
    // WebGL2 only: Offset into srcData
    // WebGL2 only: Number of bytes to be copied

  }, {
    key: "subData",
    value: function subData() {
      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          data = _ref.data,
          _ref$offset = _ref.offset,
          offset = _ref$offset === void 0 ? 0 : _ref$offset,
          _ref$srcOffset = _ref.srcOffset,
          srcOffset = _ref$srcOffset === void 0 ? 0 : _ref$srcOffset,
          length = _ref.length;

      (0, _assert.default)(data); // Create the buffer - binding it here for the first time locks the type
      // In WebGL2, use GL.COPY_WRITE_BUFFER to avoid locking the type

      var target = this.gl.webgl2 ? _constants.default.COPY_WRITE_BUFFER : this.target;
      this.gl.bindBuffer(target, this.handle); // WebGL2: subData supports additional srcOffset and length parameters

      if (srcOffset !== 0 || length !== undefined) {
        (0, _webglUtils.assertWebGL2Context)(this.gl);
        this.gl.bufferSubData(this.target, offset, data, srcOffset, length || 0);
      } else {
        this.gl.bufferSubData(target, offset, data);
      }

      this.gl.bindBuffer(target, null); // TODO - update local `data` if offsets are right

      this.debugData = null;
      return this;
    } // WEBGL2 ONLY: Copies part of the data of another buffer into this buffer

  }, {
    key: "copyData",
    value: function copyData(_ref2) {
      var sourceBuffer = _ref2.sourceBuffer,
          _ref2$readOffset = _ref2.readOffset,
          readOffset = _ref2$readOffset === void 0 ? 0 : _ref2$readOffset,
          _ref2$writeOffset = _ref2.writeOffset,
          writeOffset = _ref2$writeOffset === void 0 ? 0 : _ref2$writeOffset,
          size = _ref2.size;
      var gl = this.gl;
      (0, _webglUtils.assertWebGL2Context)(gl); // Use GL.COPY_READ_BUFFER+GL.COPY_WRITE_BUFFER avoid disturbing other targets and locking type

      gl.bindBuffer(_constants.default.COPY_READ_BUFFER, sourceBuffer.handle);
      gl.bindBuffer(_constants.default.COPY_WRITE_BUFFER, this.handle);
      gl.copyBufferSubData(_constants.default.COPY_READ_BUFFER, _constants.default.COPY_WRITE_BUFFER, readOffset, writeOffset, size);
      gl.bindBuffer(_constants.default.COPY_READ_BUFFER, null);
      gl.bindBuffer(_constants.default.COPY_WRITE_BUFFER, null); // TODO - update local `data` if offsets are 0

      this.debugData = null;
      return this;
    } // WEBGL2 ONLY: Reads data from buffer into an ArrayBufferView or SharedArrayBuffer.

  }, {
    key: "getData",
    value: function getData() {
      var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref3$dstData = _ref3.dstData,
          dstData = _ref3$dstData === void 0 ? null : _ref3$dstData,
          _ref3$srcByteOffset = _ref3.srcByteOffset,
          srcByteOffset = _ref3$srcByteOffset === void 0 ? 0 : _ref3$srcByteOffset,
          _ref3$dstOffset = _ref3.dstOffset,
          dstOffset = _ref3$dstOffset === void 0 ? 0 : _ref3$dstOffset,
          _ref3$length = _ref3.length,
          length = _ref3$length === void 0 ? 0 : _ref3$length;

      (0, _webglUtils.assertWebGL2Context)(this.gl);
      var ArrayType = (0, _typedArrayUtils.getTypedArrayFromGLType)(this.accessor.type || _constants.default.FLOAT, {
        clamped: false
      });

      var sourceAvailableElementCount = this._getAvailableElementCount(srcByteOffset);

      var dstElementOffset = dstOffset;
      var dstAvailableElementCount;
      var dstElementCount;

      if (dstData) {
        dstElementCount = dstData.length;
        dstAvailableElementCount = dstElementCount - dstElementOffset;
      } else {
        // Allocate ArrayBufferView with enough size to copy all eligible data.
        dstAvailableElementCount = Math.min(sourceAvailableElementCount, length || sourceAvailableElementCount);
        dstElementCount = dstElementOffset + dstAvailableElementCount;
      }

      var copyElementCount = Math.min(sourceAvailableElementCount, dstAvailableElementCount);
      length = length || copyElementCount;
      (0, _assert.default)(length <= copyElementCount);
      dstData = dstData || new ArrayType(dstElementCount); // Use GL.COPY_READ_BUFFER to avoid disturbing other targets and locking type

      this.gl.bindBuffer(_constants.default.COPY_READ_BUFFER, this.handle);
      this.gl.getBufferSubData(_constants.default.COPY_READ_BUFFER, srcByteOffset, dstData, dstOffset, length);
      this.gl.bindBuffer(_constants.default.COPY_READ_BUFFER, null); // TODO - update local `data` if offsets are 0

      return dstData;
    }
    /**
     * Binds a buffer to a given binding point (target).
     *   GL.TRANSFORM_FEEDBACK_BUFFER and GL.UNIFORM_BUFFER take an index, and optionally a range.
     * @param {Glenum} target - target for the bind operation.
     * @param {GLuint} index= - the index of the target.
     *   - GL.TRANSFORM_FEEDBACK_BUFFER and GL.UNIFORM_BUFFER need an index to affect state
     * @param {GLuint} offset=0 - the index of the target.
     *   - GL.UNIFORM_BUFFER: `offset` must be aligned to GL.UNIFORM_BUFFER_OFFSET_ALIGNMENT.
     * @param {GLuint} size= - the index of the target.
     *   - GL.UNIFORM_BUFFER: `size` must be a minimum of GL.UNIFORM_BLOCK_SIZE_DATA.
     * @returns {Buffer} - Returns itself for chaining.
     */

  }, {
    key: "bind",
    value: function bind() {
      var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref4$target = _ref4.target,
          target = _ref4$target === void 0 ? this.target : _ref4$target,
          _ref4$index = _ref4.index,
          index = _ref4$index === void 0 ? this.accessor && this.accessor.index : _ref4$index,
          _ref4$offset = _ref4.offset,
          offset = _ref4$offset === void 0 ? 0 : _ref4$offset,
          size = _ref4.size;

      // NOTE: While GL.TRANSFORM_FEEDBACK_BUFFER and GL.UNIFORM_BUFFER could
      // be used as direct binding points, they will not affect transform feedback or
      // uniform buffer state. Instead indexed bindings need to be made.
      var type = target === _constants.default.UNIFORM_BUFFER || target === _constants.default.TRANSFORM_FEEDBACK_BUFFER ? size !== undefined ? 'ranged' : 'indexed' : 'non-indexed';

      switch (type) {
        case 'indexed':
          (0, _webglUtils.assertWebGL2Context)(this.gl);
          (0, _assert.default)(offset === 0); // Make sure offset wasn't supplied

          this.gl.bindBufferBase(target, index, this.handle);
          break;

        case 'ranged':
          (0, _webglUtils.assertWebGL2Context)(this.gl);
          this.gl.bindBufferRange(target, index, this.handle, offset, size);
          break;

        case 'non-indexed':
          this.gl.bindBuffer(target, this.handle);
          break;

        default:
          (0, _assert.default)(false);
      }

      return this;
    }
  }, {
    key: "unbind",
    value: function unbind() {
      var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref5$target = _ref5.target,
          target = _ref5$target === void 0 ? this.target : _ref5$target,
          _ref5$index = _ref5.index,
          index = _ref5$index === void 0 ? this.accessor && this.accessor.index : _ref5$index;

      var isIndexedBuffer = target === _constants.default.UNIFORM_BUFFER || target === _constants.default.TRANSFORM_FEEDBACK_BUFFER;

      if (isIndexedBuffer) {
        this.gl.bindBufferBase(target, index, null);
      } else {
        this.gl.bindBuffer(target, null);
      }

      return this;
    } // PRIVATE METHODS
    // Returns a short initial data array

  }, {
    key: "getDebugData",
    value: function getDebugData() {
      if (!this.debugData) {
        this.debugData = this.getData({
          length: DEBUG_DATA_LENGTH
        });
        return {
          data: this.debugData,
          changed: true
        };
      }

      return {
        data: this.debugData,
        changed: false
      };
    }
  }, {
    key: "invalidateDebugData",
    value: function invalidateDebugData() {
      this.debugData = null;
    }
  }, {
    key: "_getAvailableElementCount",
    value: function _getAvailableElementCount(srcByteOffset) {
      var ArrayType = (0, _typedArrayUtils.getTypedArrayFromGLType)(this.accessor.type || _constants.default.FLOAT, {
        clamped: false
      });
      var sourceElementCount = this.bytes / ArrayType.BYTES_PER_ELEMENT;
      var sourceElementOffset = srcByteOffset / ArrayType.BYTES_PER_ELEMENT;
      return sourceElementCount - sourceElementOffset;
    } // RESOURCE METHODS

  }, {
    key: "_createHandle",
    value: function _createHandle() {
      return this.gl.createBuffer();
    }
  }, {
    key: "_deleteHandle",
    value: function _deleteHandle() {
      this.gl.deleteBuffer(this.handle);
    }
  }, {
    key: "_getParameter",
    value: function _getParameter(pname) {
      this.gl.bindBuffer(this.target, this.handle);
      var value = this.gl.getBufferParameter(this.target, pname);
      this.gl.bindBuffer(this.target, null);
      return value;
    }
  }, {
    key: "data",
    get: function get() {
      _utils.log.deprecated('Buffer.data', 'N/A', 'v6.0');

      return this._data;
    }
  }]);

  return Buffer;
}(_resource.default);

exports.default = Buffer;
//# sourceMappingURL=buffer.js.map