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 _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return 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 _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

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

function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }

function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

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 _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; }

// WebGL2 VertexArrayObject class (polyfilled/extended in WebGL1)
import GL from '../constants';
import Resource from './resource';
import Accessor from './accessor';
import Buffer from './buffer';
import { isWebGL2 } from '../webgl-utils';
import { glKey } from '../webgl-utils/constants-to-keys';
import { getCompositeGLType } from '../webgl-utils/attribute-utils';
import { log, formatValue, assert } from '../utils';
/* eslint-disable camelcase */

var OES_vertex_array_object = 'OES_vertex_array_object';
var GL_ELEMENT_ARRAY_BUFFER = 0x8893;
var ERR_WEBGL1 = 'import "luma.gl/webgl1" to make luma.gl run in minimal WebGL1 environments';
var ERR_ELEMENTS = 'elements must be GL.ELEMENT_ARRAY_BUFFER';
var ERR_ATTRIBUTE_TYPE = 'VertexArray: attributes must be Buffer or typed array constant';

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

  _createClass(VertexArray, null, [{
    key: "isSupported",
    // Now always supported via client side polyfill
    value: function isSupported(gl) {
      return isWebGL2(gl) || gl.getExtension(OES_vertex_array_object);
    } // Returns the global (null) vertex array object. Exists even when no extension available

  }, {
    key: "getDefaultArray",
    value: function getDefaultArray(gl) {
      gl.luma = gl.luma || {};

      if (!gl.luma.defaultVertexArray) {
        gl.luma.defaultVertexArray = new VertexArray(gl, {
          handle: null
        });
      }

      return gl.luma.defaultVertexArray;
    }
  }, {
    key: "getMaxAttributes",
    value: function getMaxAttributes(gl) {
      VertexArray.MAX_VERTEX_ATTRIBS = VertexArray.MAX_VERTEX_ATTRIBS || gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
      return VertexArray.MAX_VERTEX_ATTRIBS;
    } // Create a VertexArray

  }]);

  function VertexArray(gl) {
    var _this;

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

    _classCallCheck(this, VertexArray);

    if (opts.handle && !VertexArray.isSupported(gl)) {
      throw new Error(ERR_WEBGL1);
    } // Use program's id if program but no id is supplied


    var id = opts.id || opts.program && opts.program.id;
    _this = _possibleConstructorReturn(this, (VertexArray.__proto__ || Object.getPrototypeOf(VertexArray)).call(this, gl, Object.assign({}, opts, {
      id: id
    })));
    _this.configuration = null; // Extracted information

    _this.elements = null;
    _this.values = null;
    _this.infos = null;
    _this.accessors = null;
    _this.unused = null;
    _this.drawParams = null;
    _this.unbindBuffer = null; // Created when unbinding buffers

    _this.stubRemovedMethods('VertexArray', 'v6.0', ['setBuffers', 'setGeneric', 'clearBindings', 'setLocations', 'setGenericValues', 'setDivisor', 'enable', 'disable']);

    _this._initialize(opts);

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

  _createClass(VertexArray, [{
    key: "delete",
    value: function _delete() {
      _get(VertexArray.prototype.__proto__ || Object.getPrototypeOf(VertexArray.prototype), "delete", this).call(this);

      if (this.unbindBuffer) {
        this.unbindBuffer.delete();
      }
    }
  }, {
    key: "setProps",
    value: function setProps(props) {
      if ('program' in props) {
        this.configuration = props.program && props.program.configuration;
      }

      if ('configuration' in props) {
        this.configuration = props.configuration;
      }

      if ('bindOnUse' in props) {
        props = props.bindOnUse;
      }

      if ('attributes' in props) {
        this.setAttributes(props.attributes);
      }

      if ('elements' in props) {
        this.setElements(props.elements);
      }

      return this;
    } // Resets all attributes (to default valued constants)

  }, {
    key: "reset",
    value: function reset() {
      var _this2 = this;

      var clear = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
      var disableZero = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

      if (clear) {
        this._unbindBuffers();

        this.bind(function () {
          // Clear elements buffer
          _this2.gl.bindBuffer(_this2.gl.ELEMENT_ARRAY_BUFFER, null);

          for (var i = 0; i < _this2.MAX_ATTRIBUTES; i++) {
            if (i > 0 || disableZero) {
              _this2.setConstant(i, [0, 0, 0, 1]); // match assumed WebGL defaults

            }
          }
        });
      }

      this.elements = null;
      this.values = new Array(this.MAX_VERTEX_ATTRIBS).fill(null);
      this.infos = new Array(this.MAX_VERTEX_ATTRIBS).fill({});
      this.accessors = new Array(this.MAX_VERTEX_ATTRIBS).fill(null);
      this.unused = []; // Auto detects draw params

      this.drawParams = {
        isInstanced: false,
        // indexing is autodetected - buffer with target gl.ELEMENT_ARRAY_BUFFER
        // index type is saved for drawElement calls
        isIndexed: false,
        indexType: null
      };
      return this;
    } // Set (bind) an array or map of vertex array buffers, either in numbered or named locations.
    // For names that are not present in `location`, the supplied buffers will be ignored.
    // if a single buffer of type GL.ELEMENT_ARRAY_BUFFER is present, it will be set as elements

  }, {
    key: "setAttributes",
    value: function setAttributes(attributes) {
      var _this3 = this;

      this.bind(function () {
        for (var locationOrName in attributes) {
          var value = attributes[locationOrName];

          if (Array.isArray(value) && value.length && _instanceof(value[0], Buffer)) {
            var buffer = value[0];
            var accessor = value[1];

            _this3.setBuffer(locationOrName, buffer, accessor);
          } else if (_instanceof(value, Buffer)) {
            _this3.setBuffer(locationOrName, value);
          } else if (ArrayBuffer.isView(value) || Array.isArray(value)) {
            _this3.setConstant(locationOrName, value);
          } else {
            throw new Error(ERR_ATTRIBUTE_TYPE);
          }
        }
      });
      return this;
    } // Set (bind) an elements buffer, for indexed rendering.
    // Must be a Buffer bound to GL.ELEMENT_ARRAY_BUFFER. Constants not supported

  }, {
    key: "setElements",
    value: function setElements() {
      var _this4 = this;

      var elementBuffer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      assert(!elementBuffer || elementBuffer.target === GL_ELEMENT_ARRAY_BUFFER, ERR_ELEMENTS);
      this.bind(function () {
        _this4.gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer ? elementBuffer.handle : null);
      });
      this.elements = elementBuffer; // Auto-deduce isIndexed draw param

      this.drawParams.isIndexed = Boolean(elementBuffer);

      if (elementBuffer) {
        this.drawParams.indexType = elementBuffer.accessor.getOptions(opts).type;
      } else {
        delete this.drawParams.indexType;
      }

      return this;
    } // Set a location in vertex attributes array to a buffer

  }, {
    key: "setBuffer",
    value: function setBuffer(locationOrName, buffer) {
      var _this5 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      var gl = this.gl; // Check target

      if (buffer.target === gl.ELEMENT_ARRAY_BUFFER) {
        return this.setElements(buffer);
      }

      var location = this._getAttributeIndex(locationOrName);

      if (location < 0) {
        this.unused[locationOrName] = buffer;
        log.warn(function () {
          return "".concat(_this5.id, " unused buffer attribute ").concat(locationOrName);
        })();
        return this;
      }

      this.bind(function () {
        var accessInfo = _this5._getAttributeInfo(locationOrName, buffer, opts);

        var name = accessInfo ? accessInfo.name : String(location); // Override with any additional attribute configuration params

        var accessor = accessInfo ? accessInfo.accessor : new Accessor();
        accessor = accessor.getOptions(buffer, buffer.accessor, opts);
        _this5.values[location] = buffer;
        _this5.accessors[location] = accessor;
        _this5.infos[location] = {
          location: location,
          name: name,
          accessor: accessor
        };
        var _accessor = accessor,
            size = _accessor.size,
            type = _accessor.type,
            stride = _accessor.stride,
            offset = _accessor.offset,
            normalized = _accessor.normalized,
            divisor = _accessor.divisor;
        assert(Number.isFinite(size) && Number.isFinite(type)); // A non-zero buffer object must be bound to the GL_ARRAY_BUFFER target

        buffer.bind({
          target: gl.ARRAY_BUFFER
        }); // Attach ARRAY_BUFFER with specified buffer format to location

        if (!opts.integer) {
          gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
        } else {
          // specifies *integer* data formats and locations of vertex attributes
          assert(isWebGL2(gl));
          gl.vertexAttribIPointer(location, size, type, stride, offset);
        }

        gl.enableVertexAttribArray(location);
        gl.vertexAttribDivisor(location, divisor || 0); // NOTE We don't unbind buffer here
        // Auto deduce isInstanced drawParam

        var isInstanced = divisor > 0;
        _this5.drawParams.isInstanced = _this5.drawParams.isInstanced || isInstanced;
      });
      return this;
    } // Set attribute to constant value (small typed array corresponding to one vertex' worth of data)
    // TODO - handle single values for size 1 attributes?
    // TODO - convert classic arrays based on known type?

  }, {
    key: "setConstant",
    value: function setConstant(locationOrName, arrayValue, opts) {
      var _this6 = this;

      var accessInfo = this._getAttributeInfo(locationOrName, arrayValue, opts);

      if (!accessInfo) {
        this.unused[locationOrName] = arrayValue;
        log.warn(function () {
          return "".concat(_this6.id, " unused constant attribute ").concat(locationOrName);
        })();
        return this;
      } // TODO - read type if provided


      if (Array.isArray(arrayValue)) {
        arrayValue = new Float32Array(arrayValue);
      }

      this.bind(function () {
        var location = accessInfo.location; // TODO - use known type in configuration to allow non-typed arrays?

        switch (arrayValue.constructor) {
          case Float32Array:
            _this6._setConstantFloatArray(location, arrayValue);

            break;

          case Int32Array:
            _this6._setConstantIntArray(location, arrayValue);

            break;

          case Uint32Array:
            _this6._setConstantUintArray(location, arrayValue);

            break;

          default:
            assert(false);
        } // To use the constant value, disable reading from arrays
        // NOTE: Possible perf penalty when disabling attribute 0:
        // https://stackoverflow.com/questions/20305231/webgl-warning-attribute-0-is-disabled-
        // this-has-significant-performance-penalt


        _this6.gl.disableVertexAttribArray(location); // Reset instanced divisor


        _this6.gl.vertexAttribDivisor(location, 0); // Save the value for debugging


        _this6.values[location] = arrayValue;
      });
      return this;
    } // PRIVATE

  }, {
    key: "_getAttributeInfo",
    value: function _getAttributeInfo(attributeName) {
      return this.configuration && this.configuration.getAttributeInfo(attributeName);
    }
  }, {
    key: "_getAttributeIndex",
    value: function _getAttributeIndex(locationOrName) {
      if (this.configuration) {
        return this.configuration.getLocation(locationOrName);
      }

      var location = Number(locationOrName);

      if (Number.isFinite(location)) {
        return location;
      }

      return -1;
    }
  }, {
    key: "_initialize",
    value: function _initialize() {
      var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      this.reset(false);
      this.configuration = null;
      this.bindOnUse = false;
      return this.setProps(props);
    }
  }, {
    key: "_setConstantFloatArray",
    value: function _setConstantFloatArray(location, array) {
      var gl = this.gl;

      switch (array.length) {
        case 1:
          gl.vertexAttrib1fv(location, array);
          break;

        case 2:
          gl.vertexAttrib2fv(location, array);
          break;

        case 3:
          gl.vertexAttrib3fv(location, array);
          break;

        case 4:
          gl.vertexAttrib4fv(location, array);
          break;

        default:
          assert(false);
      }
    }
  }, {
    key: "_setConstantIntArray",
    value: function _setConstantIntArray(location, array) {
      var gl = this.gl;
      assert(isWebGL2(gl));

      switch (array.length) {
        case 1:
          gl.vertexAttribI1iv(location, array);
          break;

        case 2:
          gl.vertexAttribI2iv(location, array);
          break;

        case 3:
          gl.vertexAttribI3iv(location, array);
          break;

        case 4:
          gl.vertexAttribI4iv(location, array);
          break;

        default:
          assert(false);
      }
    }
  }, {
    key: "_setConstantUintArray",
    value: function _setConstantUintArray(location, array) {
      var gl = this.gl;
      assert(isWebGL2(gl));

      switch (array.length) {
        case 1:
          gl.vertexAttribI1uiv(location, array);
          break;

        case 2:
          gl.vertexAttribI2uiv(location, array);
          break;

        case 3:
          gl.vertexAttribI3uiv(location, array);
          break;

        case 4:
          gl.vertexAttribI4uiv(location, array);
          break;

        default:
          assert(false);
      }
    } // Workaround for Chrome issue, unbind temporarily to avoid conflicting with TransformFeednack

  }, {
    key: "unbindBuffers",
    value: function unbindBuffers() {
      var _this7 = this;

      var disableZero = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
      this.bind(function () {
        // No clear way to set a buffer to null, so create a minimal "dummy buffer"
        // Save it to avoid inflating buffer creation statistics
        _this7.unbindBuffer = _this7.unbindBuffer || new Buffer(_this7.gl, {
          size: 4
        });

        for (var location in _this7.values) {
          if (_instanceof(_this7.values[location], Buffer)) {
            _this7.gl.disableVertexAttribArray(location);

            _this7.gl.bindBuffer(_this7.gl.ARRAY_BUFFER, _this7.unbindBuffer.handle);

            _this7.gl.vertexAttribPointer(location, 1, _this7.gl.FLOAT, false, 0, 0);
          }
        }
      });
    } // Workaround for Chrome issue, rebind after temporary unbind

  }, {
    key: "bindBuffers",
    value: function bindBuffers() {
      var _this8 = this;

      this.bind(function () {
        for (var location in _this8.values) {
          var buffer = _this8.values[location];

          if (_instanceof(buffer, Buffer)) {
            _this8.setBuffer(location, buffer);
          }
        }
      });
    } // RESOURCE IMPLEMENTATION

  }, {
    key: "_createHandle",
    value: function _createHandle() {
      return this.gl.createVertexArray();
    }
  }, {
    key: "_deleteHandle",
    value: function _deleteHandle(handle) {
      this.gl.deleteVertexArray(handle);
      return [this.elements]; // return [this.elements, ...this.buffers];
    }
  }, {
    key: "_bindHandle",
    value: function _bindHandle(handle) {
      this.gl.bindVertexArray(handle);
    }
  }, {
    key: "_bind",
    value: function _bind(handle) {
      this.gl.bindVertexArray(handle);
    } // Generic getter for information about a vertex attribute at a given position

  }, {
    key: "_getParameter",
    value: function _getParameter(pname, _ref) {
      var _this9 = this;

      var location = _ref.location;
      assert(Number.isFinite(location));
      return this.bind(function () {
        switch (pname) {
          case GL.VERTEX_ATTRIB_ARRAY_POINTER:
            return _this9.gl.getVertexAttribOffset(location, pname);

          default:
            return _this9.gl.getVertexAttrib(location, pname);
        }
      });
    }
  }, {
    key: "_getDebugTable",
    value: function _getDebugTable() {
      var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref2$header = _ref2.header,
          header = _ref2$header === void 0 ? 'Attributes' : _ref2$header;

      if (!this.configuration) {
        return {};
      }

      var table = {}; // {[header]: {}};
      // Add index (elements) if available

      if (this.elements) {
        // const elements = Object.assign({size: 1}, this.elements);
        table.ELEMENT_ARRAY_BUFFER = this._getDebugTableRow(this.elements, null, header);
      } // Add used attributes


      var attributes = this.values;

      for (var attributeName in attributes) {
        var info = this._getAttributeInfo(attributeName);

        if (info) {
          var rowHeader = "".concat(attributeName, ": ").concat(info.name);
          var accessor = this.accessors[info.location];

          if (accessor) {
            var typeAndName = getCompositeGLType(accessor.type, accessor.size);

            if (typeAndName) {
              // eslint-disable-line
              rowHeader = "".concat(attributeName, ": ").concat(info.name, " (").concat(typeAndName.name, ")");
            }
          }

          table[rowHeader] = this._getDebugTableRow(attributes[attributeName], accessor, header);
        }
      }

      return table;
    }
    /* eslint-disable max-statements */

  }, {
    key: "_getDebugTableRow",
    value: function _getDebugTableRow(attribute, accessor, header) {
      var _ref4;

      var gl = this.gl; // const round = xnum => Math.round(num * 10) / 10;

      var type = 'NOT PROVIDED';
      var size = 'N/A';
      var verts = 'N/A';
      var bytes = 'N/A';
      var isInteger;
      var marker;
      var value;

      if (accessor) {
        type = accessor.type;
        size = accessor.size; // Generate a type name by dropping Array from Float32Array etc.

        type = String(type).replace('Array', ''); // Look for 'nt' to detect integer types, e.g. Int32Array, Uint32Array

        isInteger = type.indexOf('nt') !== -1;
      }

      if (_instanceof(attribute, Buffer)) {
        var _ref3;

        var buffer = attribute;

        var _buffer$getDebugData = buffer.getDebugData(),
            data = _buffer$getDebugData.data,
            modified = _buffer$getDebugData.modified;

        marker = modified ? '*' : '';
        value = data;
        bytes = buffer.bytes;
        verts = bytes / data.BYTES_PER_ELEMENT / size;
        var format;

        if (accessor) {
          var instanced = accessor.divisor > 0;
          format = "".concat(instanced ? 'I ' : 'P ', " ").concat(verts, " (x").concat(size, "=").concat(bytes, " bytes ").concat(glKey(gl, type), ")");
        } else {
          // element buffer
          isInteger = true;
          format = "".concat(bytes, " bytes");
        }

        return _ref3 = {}, _defineProperty(_ref3, header, "".concat(marker).concat(formatValue(value, {
          size: size,
          isInteger: isInteger
        }))), _defineProperty(_ref3, 'Format ', format), _ref3;
      } // CONSTANT VALUE


      value = attribute;
      size = attribute.length; // Generate a type name by dropping Array from Float32Array etc.

      type = String(attribute.constructor.name).replace('Array', ''); // Look for 'nt' to detect integer types, e.g. Int32Array, Uint32Array

      isInteger = type.indexOf('nt') !== -1;
      return _ref4 = {}, _defineProperty(_ref4, header, "".concat(formatValue(value, {
        size: size,
        isInteger: isInteger
      }), " (constant)")), _defineProperty(_ref4, 'Format ', "".concat(size, "x").concat(type, " (constant)")), _ref4;
    }
    /* eslint-ensable max-statements */

  }, {
    key: "MAX_ATTRIBUTES",
    get: function get() {
      return VertexArray.getMaxAttributes(this.gl);
    }
  }]);

  return VertexArray;
}(Resource);

export { VertexArray as default };
//# sourceMappingURL=vertex-array.js.map