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 _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 _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 _get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return _get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }

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, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

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

/* eslint-disable no-inline-comments */
import GL from '../constants';
import VertexArray from './vertex-array';
import Resource from './resource';
import Texture from './texture';
import Framebuffer from './framebuffer';
import { parseUniformName, getUniformSetter } from './uniforms';
import { VertexShader, FragmentShader } from './shader';
import Buffer from './buffer';
import { withParameters } from '../webgl-context/context-state';
import { assertWebGL2Context, isWebGL2 } from '../webgl-utils';
import { getPrimitiveDrawMode } from '../webgl-utils/attribute-utils';
import { log, uid, isObjectEmpty } from '../utils';
import assert from '../utils/assert';
var LOG_PROGRAM_PERF_PRIORITY = 3; // const GL_TRANSFORM_FEEDBACK_BUFFER_MODE = 0x8C7F;
// const GL_TRANSFORM_FEEDBACK_VARYINGS = 0x8C83;
// MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS : 0x8C80,
// TRANSFORM_FEEDBACK_BUFFER_START: 0x8C84,
// TRANSFORM_FEEDBACK_BUFFER_SIZE : 0x8C85,
// TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 0x8C88,
// MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: 0x8C8A,
// MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: 0x8C8B,

var GL_INTERLEAVED_ATTRIBS = 0x8C8C;
var GL_SEPARATE_ATTRIBS = 0x8C8D;

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

  function Program(gl) {
    var _this;

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

    _classCallCheck(this, Program);

    _this = _possibleConstructorReturn(this, (Program.__proto__ || Object.getPrototypeOf(Program)).call(this, gl, opts));

    _this.initialize(opts);

    _this.vertexAttributes = VertexArray.getDefaultArray(gl);
    Object.seal(_assertThisInitialized(_this));

    _this._setId(opts.id);

    return _this;
  }

  _createClass(Program, [{
    key: "initialize",
    value: function initialize() {
      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          vs = _ref.vs,
          fs = _ref.fs,
          defaultUniforms = _ref.defaultUniforms,
          varyings = _ref.varyings,
          _ref$bufferMode = _ref.bufferMode,
          bufferMode = _ref$bufferMode === void 0 ? GL_SEPARATE_ATTRIBS : _ref$bufferMode;

      // Create shaders if needed
      this.vs = typeof vs === 'string' ? new VertexShader(this.gl, vs) : vs;
      this.fs = typeof fs === 'string' ? new FragmentShader(this.gl, fs) : fs;
      assert(_instanceof(this.vs, VertexShader), 'Program: bad vertex shader');
      assert(_instanceof(this.fs, FragmentShader), 'Program: bad fragment shader');
      this.defaultUniforms = defaultUniforms; // Setup varyings if supplied

      if (varyings) {
        assertWebGL2Context(this.gl);
        this.varyings = varyings;
        this.gl.transformFeedbackVaryings(this.handle, varyings, bufferMode);
        this.varyingMap = getVaryingMap(varyings, bufferMode);
      } else {
        this.varyingMap = {};
      }

      this._compileAndLink(); // Experimental flag to avoid deleting Program object while it is cached


      this._isCached = false;
      return this;
    } // Generates warning if a vertex shader attribute is not setup.

  }, {
    key: "checkAttributeBindings",
    value: function checkAttributeBindings(_ref2) {
      var vertexArray = _ref2.vertexArray;
      var filledLocations = vertexArray ? vertexArray.filledLocations : this.vertexAttributes.filledLocations;

      for (var attributeName in this._attributeToLocationMap) {
        var location = this._attributeToLocationMap[attributeName];

        if (!filledLocations[location] && !this._warnedLocations[location]) {
          // throw new Error(`Program ${this.id}: ` +
          //   `Attribute ${location}:${attributeName} not supplied`);
          log.warn("Program ".concat(this.id, ": Attribute ").concat(location, ":").concat(attributeName, " not supplied"))();
          this._warnedLocations[location] = true;
        }
      }

      return this;
    }
  }, {
    key: "delete",
    value: function _delete() {
      var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

      if (this._isCached) {
        // This object is cached, do not delete
        return this;
      }

      return _get(Program.prototype.__proto__ || Object.getPrototypeOf(Program.prototype), "delete", this).call(this, opts);
    }
  }, {
    key: "reset",
    value: function reset() {
      this.unsetBuffers(); // TODO - reset uniforms and attributes to initial state
    }
  }, {
    key: "use",
    value: function use() {
      this.gl.useProgram(this.handle);
      return this;
    } // A good thing about webGL is that there are so many ways to draw things,
    // e.g. depending on whether data is indexed and/or isInstanced.
    // This function unifies those into a single call with simple parameters
    // that have sane defaults.

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

      var _ref3$drawMode = _ref3.drawMode,
          drawMode = _ref3$drawMode === void 0 ? GL.TRIANGLES : _ref3$drawMode,
          vertexCount = _ref3.vertexCount,
          _ref3$offset = _ref3.offset,
          offset = _ref3$offset === void 0 ? 0 : _ref3$offset,
          start = _ref3.start,
          end = _ref3.end,
          _ref3$isIndexed = _ref3.isIndexed,
          isIndexed = _ref3$isIndexed === void 0 ? false : _ref3$isIndexed,
          _ref3$indexType = _ref3.indexType,
          indexType = _ref3$indexType === void 0 ? GL.UNSIGNED_SHORT : _ref3$indexType,
          _ref3$isInstanced = _ref3.isInstanced,
          isInstanced = _ref3$isInstanced === void 0 ? false : _ref3$isInstanced,
          _ref3$instanceCount = _ref3.instanceCount,
          instanceCount = _ref3$instanceCount === void 0 ? 0 : _ref3$instanceCount,
          _ref3$vertexArray = _ref3.vertexArray,
          vertexArray = _ref3$vertexArray === void 0 ? null : _ref3$vertexArray,
          _ref3$transformFeedba = _ref3.transformFeedback,
          transformFeedback = _ref3$transformFeedba === void 0 ? null : _ref3$transformFeedba,
          _ref3$uniforms = _ref3.uniforms,
          uniforms = _ref3$uniforms === void 0 ? {} : _ref3$uniforms,
          _ref3$samplers = _ref3.samplers,
          samplers = _ref3$samplers === void 0 ? {} : _ref3$samplers,
          _ref3$parameters = _ref3.parameters,
          parameters = _ref3$parameters === void 0 ? {} : _ref3$parameters;
      vertexArray = vertexArray || VertexArray.getDefaultArray(this.gl);
      vertexArray.bind(function () {
        _this2.gl.useProgram(_this2.handle);

        if (transformFeedback) {
          var primitiveMode = getPrimitiveDrawMode(drawMode);
          transformFeedback.begin(primitiveMode);
        }

        _this2.setUniforms(uniforms, samplers);

        withParameters(_this2.gl, parameters, function () {
          // TODO - Use polyfilled WebGL2RenderingContext instead of ANGLE extension
          if (isIndexed && isInstanced) {
            _this2.ext.drawElementsInstanced(drawMode, vertexCount, indexType, offset, instanceCount);
          } else if (isIndexed && isWebGL2(_this2.gl) && !isNaN(start) && !isNaN(end)) {
            _this2.gl.drawElementsRange(drawMode, start, end, vertexCount, indexType, offset);
          } else if (isIndexed) {
            _this2.gl.drawElements(drawMode, vertexCount, indexType, offset);
          } else if (isInstanced) {
            _this2.ext.drawArraysInstanced(drawMode, offset, vertexCount, instanceCount);
          } else {
            _this2.gl.drawArrays(drawMode, offset, vertexCount);
          }
        }); // this.gl.useProgram(null);

        if (transformFeedback) {
          transformFeedback.end();
        }
      });
      return this;
    }
    /**
     * Attach a map of Buffers values to a program
     * Only attributes with names actually present in the linked program
     * will be updated. Other supplied buffers will be ignored.
     *
     * @param {Object} buffers - An object map with attribute names being keys
     *  and values are expected to be instances of Buffer.
     * @returns {Program} Returns itself for chaining.
     */

    /* eslint-disable max-statements */

  }, {
    key: "setBuffers",
    value: function setBuffers(buffers) {
      var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
          _ref4$clear = _ref4.clear,
          clear = _ref4$clear === void 0 ? true : _ref4$clear,
          _ref4$drawParams = _ref4.drawParams,
          drawParams = _ref4$drawParams === void 0 ? {} : _ref4$drawParams;

      if (clear) {
        this.vertexAttributes.clearBindings();
      } // indexing is autodetected - buffer with target gl.ELEMENT_ARRAY_BUFFER
      // index type is saved for drawElement calls


      drawParams.isInstanced = false;
      drawParams.isIndexed = false;
      drawParams.indexType = null;

      var _sortBuffersByLocatio = this._sortBuffersByLocation(buffers),
          locations = _sortBuffersByLocatio.locations,
          elements = _sortBuffersByLocatio.elements; // Process locations in order


      for (var location = 0; location < locations.length; ++location) {
        var bufferName = locations[location];
        var buffer = buffers[bufferName]; // DISABLE MISSING ATTRIBUTE

        if (!buffer) {
          this.vertexAttributes.disable(location);
        } else if (_instanceof(buffer, Buffer)) {
          var divisor = buffer.layout.instanced ? 1 : 0;
          this.vertexAttributes.setBuffer({
            location: location,
            buffer: buffer
          });
          this.vertexAttributes.setDivisor(location, divisor);
          drawParams.isInstanced = buffer.layout.instanced > 0;
          this.vertexAttributes.enable(location);
        } else {
          this.vertexAttributes.setGeneric({
            location: location,
            array: buffer
          });
          this.vertexAttributes.disable(location, true);
        }
      } // SET ELEMENTS ARRAY BUFFER


      if (elements) {
        var _buffer = buffers[elements];

        _buffer.bind();

        drawParams.isIndexed = true;
        drawParams.indexType = _buffer.layout.type;
      }

      return this;
    }
    /* eslint-enable max-statements */

    /*
     * @returns {Program} Returns itself for chaining.
     */

  }, {
    key: "unsetBuffers",
    value: function unsetBuffers() {
      var length = this._attributeCount;

      for (var i = 1; i < length; ++i) {
        // this.vertexAttributes.setDivisor(i, 0);
        this.vertexAttributes.disable(i);
      } // Clear elements buffer


      this.gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
      return this;
    }
    /**
     * Apply a set of uniform values to a program
     * Only uniforms with names actually present in the linked program
     * will be updated.
     * other uniforms will be ignored
     *
     * @param {Object} uniformMap - An object with names being keys
     * @returns {Program} - returns itself for chaining.
     */

    /* eslint-disable max-depth */

  }, {
    key: "setUniforms",
    value: function setUniforms(uniforms) {
      var samplers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      for (var uniformName in uniforms) {
        var uniform = uniforms[uniformName];
        var uniformSetter = this._uniformSetters[uniformName];
        var sampler = samplers[uniformName];

        if (uniformSetter) {
          if (_instanceof(uniform, Framebuffer)) {
            uniform = uniform.texture;
          }

          if (_instanceof(uniform, Texture)) {
            if (uniformSetter.textureIndex === undefined) {
              uniformSetter.textureIndex = this._textureIndexCounter++;
            } // Bind texture to index


            var texture = uniform;
            var textureIndex = uniformSetter.textureIndex;
            texture.bind(textureIndex); // Bind a sampler (if supplied) to index

            if (sampler) {
              sampler.bind(textureIndex);
            } // Set the uniform sampler to the texture index


            uniformSetter(textureIndex);
          } else {
            // Just set the value
            uniformSetter(uniform);
          }
        }
      }

      return this;
    }
    /* eslint-enable max-depth */
    // Binds a uniform block (`blockIndex`) to a specific binding point (`blockBinding`)

  }, {
    key: "uniformBlockBinding",
    value: function uniformBlockBinding(blockIndex, blockBinding) {
      assertWebGL2Context(this.gl);
      this.gl.uniformBlockBinding(this.handle, blockIndex, blockBinding);
    } // setTransformFeedbackBuffers(buffers) {
    //   for (const buffer of buffers) {
    //     buffer.bindBase()
    //   }
    // }

    /**
     * ATTRIBUTES API
     * (Locations are numeric indices)
     * @return {Number} count
     */

  }, {
    key: "getAttributeCount",
    value: function getAttributeCount() {
      return this._getParameter(GL.ACTIVE_ATTRIBUTES);
    }
    /**
     * Returns location (index) of a name
     * @param {String} attributeName - name of an attribute
     *   (matches name in a linked shader)
     * @returns {Number} - // array of actual attribute names from shader linking
     */

  }, {
    key: "getAttributeLocation",
    value: function getAttributeLocation(attributeName) {
      return this.gl.getAttribLocation(this.handle, attributeName);
    }
    /**
     * Returns an object with info about attribute at index "location"/
     * @param {int} location - index of an attribute
     * @returns {WebGLActiveInfo} - info about an active attribute
     *   fields: {name, size, type}
     */

  }, {
    key: "getAttributeInfo",
    value: function getAttributeInfo(location) {
      return this.gl.getActiveAttrib(this.handle, location);
    }
    /**
     * UNIFORMS API
     * (Locations are numeric indices)
     * @return {Number} count
     */

  }, {
    key: "getUniformCount",
    value: function getUniformCount() {
      return this._getParameter(GL.ACTIVE_UNIFORMS);
    }
    /*
     * @returns {WebGLActiveInfo} - object with {name, size, type}
     */

  }, {
    key: "getUniformInfo",
    value: function getUniformInfo(index) {
      return this.gl.getActiveUniform(this.handle, index);
    }
    /*
     * @returns {WebGLUniformLocation} - opaque object representing location
     * of uniform, used by setter methods
     */

  }, {
    key: "getUniformLocation",
    value: function getUniformLocation(name) {
      return this.gl.getUniformLocation(this.handle, name);
    }
  }, {
    key: "getUniformValue",
    value: function getUniformValue(location) {
      return this.gl.getUniform(this.handle, location);
    }
    /* eslint-disable max-len */
    // Rretrieves information about active uniforms identifed by their indices (`uniformIndices`)
    // For valid `pname` values check :
    // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/getActiveUniforms

  }, {
    key: "getActiveUniforms",
    value: function getActiveUniforms(uniformIndices, pname) {
      return this.gl.getActiveUniforms(this.handle, uniformIndices, pname);
    }
    /* eslint-enable max-len */
    // WebGL2

    /**
     * @param {GLuint} index
     * @return {WebGLActiveInfo} - object with {name, size, type}
     */

  }, {
    key: "getVarying",
    value: function getVarying(program, index) {
      var result = this.gl.getTransformFeedbackVarying(program, index);
      return result;
    } // Retrieves the assigned color number binding for the user-defined varying
    // out variable name for program. program must have previously been linked.

  }, {
    key: "getFragDataLocation",
    value: function getFragDataLocation(varyingName) {
      assertWebGL2Context(this.gl);
      return this.gl.getFragDataLocation(this.handle, varyingName);
    } // @returns {WebGLShader[]} - array of attached WebGLShader objects

  }, {
    key: "getAttachedShaders",
    value: function getAttachedShaders() {
      return this.gl.getAttachedShaders(this.handle);
    } // Retrieves the index of a uniform block

  }, {
    key: "getUniformBlockIndex",
    value: function getUniformBlockIndex(blockName) {
      assertWebGL2Context(this.gl);
      return this.gl.getUniformBlockIndex(this.handle, blockName);
    }
    /* eslint-disable max-len */
    // Retrieves information about an active uniform block (`blockIndex`)
    // For valid `pname` values check :
    // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/getActiveUniformBlockParameter

  }, {
    key: "getActiveUniformBlockParameter",
    value: function getActiveUniformBlockParameter(blockIndex, pname) {
      assertWebGL2Context(this.gl);
      return this.gl.getActiveUniformBlockParameter(this.handle, blockIndex, pname);
    }
    /* eslint-enable max-len */
    // PRIVATE METHODS

  }, {
    key: "_compileAndLink",
    value: function _compileAndLink() {
      var gl = this.gl;
      gl.attachShader(this.handle, this.vs.handle);
      gl.attachShader(this.handle, this.fs.handle);
      log.time(LOG_PROGRAM_PERF_PRIORITY, "linkProgram for ".concat(this._getName()))();
      gl.linkProgram(this.handle);
      log.timeEnd(LOG_PROGRAM_PERF_PRIORITY, "linkProgram for ".concat(this._getName()))(); // Avoid checking program linking error in production

      if (gl.debug || log.priority > 0) {
        gl.validateProgram(this.handle);
        var linked = gl.getProgramParameter(this.handle, gl.LINK_STATUS);

        if (!linked) {
          throw new Error("Error linking ".concat(gl.getProgramInfoLog(this.handle)));
        }
      }

      this._queryAttributeLocations();

      this._queryUniformLocations();
    }
  }, {
    key: "_sortBuffersByLocation",
    value: function _sortBuffersByLocation(buffers) {
      var elements = null;
      var locations = []; // Reutrn early if no buffers to be bound.

      if (isObjectEmpty(buffers)) {
        return {
          locations: locations,
          elements: elements
        };
      }

      locations = new Array(this._attributeCount);

      for (var bufferName in buffers) {
        var buffer = buffers[bufferName];
        var location = this._attributeToLocationMap[bufferName];

        if (location === undefined) {
          if (buffer.target === GL.ELEMENT_ARRAY_BUFFER && elements) {
            throw new Error("".concat(this._print(bufferName), " duplicate GL.ELEMENT_ARRAY_BUFFER"));
          } else if (buffer.target === GL.ELEMENT_ARRAY_BUFFER) {
            elements = bufferName;
          } else if (!this._warnedLocations[location]) {
            log.log(2, "".concat(this._print(bufferName), " not used"))();
            this._warnedLocations[location] = true;
          }
        } else {
          if (buffer.target === GL.ELEMENT_ARRAY_BUFFER) {
            throw new Error("".concat(this._print(bufferName), ":").concat(location, " ") + 'has both location and type gl.ELEMENT_ARRAY_BUFFER');
          }

          locations[location] = bufferName;
        }
      }

      return {
        locations: locations,
        elements: elements
      };
    } // Check that all active attributes are enabled

  }, {
    key: "_areAllAttributesEnabled",
    value: function _areAllAttributesEnabled() {
      var length = this._attributeCount;

      for (var i = 0; i < length; ++i) {
        if (!this.vertexAttributes.isEnabled(i)) {
          return false;
        }
      }

      return true;
    }
  }, {
    key: "_print",
    value: function _print(bufferName) {
      return "Program ".concat(this.id, ": Attribute ").concat(bufferName);
    }
  }, {
    key: "_createHandle",
    value: function _createHandle() {
      return this.gl.createProgram();
    }
  }, {
    key: "_deleteHandle",
    value: function _deleteHandle() {
      this.gl.deleteProgram(this.handle);
    }
  }, {
    key: "_getName",
    value: function _getName() {
      var programName = this.vs.getName() || this.fs.getName();
      programName = programName.replace(/shader/i, '');
      programName = programName ? "".concat(programName, "-program") : 'program';
      return programName;
    }
  }, {
    key: "_getOptionsFromHandle",
    value: function _getOptionsFromHandle(handle) {
      var shaderHandles = this.gl.getAttachedShaders(handle);
      var opts = {};
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = shaderHandles[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var shaderHandle = _step.value;
          var type = this.gl.getShaderParameter(this.handle, GL.SHADER_TYPE);

          switch (type) {
            case GL.VERTEX_SHADER:
              opts.vs = new VertexShader({
                handle: shaderHandle
              });
              break;

            case GL.FRAGMENT_SHADER:
              opts.fs = new FragmentShader({
                handle: shaderHandle
              });
              break;

            default:
          }
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return != null) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }

      return opts;
    }
  }, {
    key: "_getParameter",
    value: function _getParameter(pname) {
      return this.gl.getProgramParameter(this.handle, pname);
    } // query attribute locations and build name to location map.

  }, {
    key: "_queryAttributeLocations",
    value: function _queryAttributeLocations() {
      this._attributeToLocationMap = {};
      this._attributeCount = this.getAttributeCount();

      for (var location = 0; location < this._attributeCount; location++) {
        var name = this.getAttributeInfo(location).name;
        this._attributeToLocationMap[name] = this.getAttributeLocation(name);
      }

      this._warnedLocations = {};
    } // query uniform locations and build name to setter map.

  }, {
    key: "_queryUniformLocations",
    value: function _queryUniformLocations() {
      var gl = this.gl;
      this._uniformSetters = {};
      this._uniformCount = this.getUniformCount();

      for (var i = 0; i < this._uniformCount; i++) {
        var info = this.getUniformInfo(i);
        var parsedName = parseUniformName(info.name);
        var location = this.getUniformLocation(parsedName.name);
        this._uniformSetters[parsedName.name] = getUniformSetter(gl, location, info, parsedName.isArray);
      }

      this._textureIndexCounter = 0;
    }
  }, {
    key: "_setId",
    value: function _setId(id) {
      // If program is not named, name it after shader names
      if (!id) {
        var programName = this._getName(); // TODO - this.id will already have been initialized


        this.id = uid(programName);
      }
    }
  }]);

  return Program;
}(Resource); // create uniform setters
// Map of uniform names to setter functions


export { Program as default };
export function getUniformDescriptors(gl, program) {
  var uniformDescriptors = {};
  var length = program.getUniformCount();

  for (var i = 0; i < length; i++) {
    var info = program.getUniformInfo(i);
    var location = program.getUniformLocation(info.name);
    var descriptor = getUniformSetter(gl, location, info);
    uniformDescriptors[descriptor.name] = descriptor;
  }

  return uniformDescriptors;
} // Get a map of buffer indices

export function getVaryingMap(varyings, bufferMode) {
  var varyingMap = {};
  var index = 0;
  assert(bufferMode === GL_SEPARATE_ATTRIBS || bufferMode === GL_INTERLEAVED_ATTRIBS);
  var indexIncrement = bufferMode === GL_SEPARATE_ATTRIBS ? 1 : 0;
  var _iteratorNormalCompletion2 = true;
  var _didIteratorError2 = false;
  var _iteratorError2 = undefined;

  try {
    for (var _iterator2 = varyings[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
      var varying = _step2.value;
      varyingMap[varying] = index;
      index += indexIncrement;
    }
  } catch (err) {
    _didIteratorError2 = true;
    _iteratorError2 = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
        _iterator2.return();
      }
    } finally {
      if (_didIteratorError2) {
        throw _iteratorError2;
      }
    }
  }

  return varyingMap;
}
//# sourceMappingURL=program.js.map