"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.parseUniformName = parseUniformName;
exports.getUniformSetter = getUniformSetter;
exports.checkUniformValues = checkUniformValues;
exports.areUniformsEqual = areUniformsEqual;
exports.getUniformsTable = getUniformsTable;

var _framebuffer = _interopRequireDefault(require("./framebuffer"));

var _texture = _interopRequireDefault(require("./texture"));

var _sampler = _interopRequireDefault(require("./sampler"));

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

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

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

var _UNIFORM_SETTERS;

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

var UNIFORM_SETTERS = (_UNIFORM_SETTERS = {}, _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT, function (gl, location, value) {
  return gl.uniform1f(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_VEC2, function (gl, location, value) {
  return gl.uniform2fv(location, toFloatArray(value, 2));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_VEC3, function (gl, location, value) {
  return gl.uniform3fv(location, toFloatArray(value, 3));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_VEC4, function (gl, location, value) {
  return gl.uniform4fv(location, toFloatArray(value, 4));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT_VEC2, function (gl, location, value) {
  return gl.uniform2iv(location, toIntArray(value, 2));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT_VEC3, function (gl, location, value) {
  return gl.uniform3iv(location, toIntArray(value, 3));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT_VEC4, function (gl, location, value) {
  return gl.uniform4iv(location, toIntArray(value, 4));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.BOOL, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.BOOL_VEC2, function (gl, location, value) {
  return gl.uniform2iv(location, toIntArray(value, 2));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.BOOL_VEC3, function (gl, location, value) {
  return gl.uniform3iv(location, toIntArray(value, 3));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.BOOL_VEC4, function (gl, location, value) {
  return gl.uniform4iv(location, toIntArray(value, 4));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT2, function (gl, location, value) {
  return gl.uniformMatrix2fv(location, false, toFloatArray(value, 4));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT3, function (gl, location, value) {
  return gl.uniformMatrix3fv(location, false, toFloatArray(value, 9));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT4, function (gl, location, value) {
  return gl.uniformMatrix4fv(location, false, toFloatArray(value, 16));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.SAMPLER_2D, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.SAMPLER_CUBE, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT, function (gl, location, value) {
  return gl.uniform1ui(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT_VEC2, function (gl, location, value) {
  return gl.uniform2uiv(location, toUIntArray(value, 2));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT_VEC3, function (gl, location, value) {
  return gl.uniform3uiv(location, toUIntArray(value, 3));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT_VEC4, function (gl, location, value) {
  return gl.uniform4uiv(location, toUIntArray(value, 4));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT2x3, function (gl, location, value) {
  return gl.uniformMatrix2x3fv(location, false, toFloatArray(value, 6));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT2x4, function (gl, location, value) {
  return gl.uniformMatrix2x4fv(location, false, toFloatArray(value, 8));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT3x2, function (gl, location, value) {
  return gl.uniformMatrix3x2fv(location, false, toFloatArray(value, 6));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT3x4, function (gl, location, value) {
  return gl.uniformMatrix3x4fv(location, false, toFloatArray(value, 12));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT4x2, function (gl, location, value) {
  return gl.uniformMatrix4x2fv(location, false, toFloatArray(value, 8));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.FLOAT_MAT4x3, function (gl, location, value) {
  return gl.uniformMatrix4x3fv(location, false, toFloatArray(value, 12));
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.SAMPLER_3D, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.SAMPLER_2D_SHADOW, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.SAMPLER_2D_ARRAY, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.SAMPLER_2D_ARRAY_SHADOW, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.SAMPLER_CUBE_SHADOW, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT_SAMPLER_2D, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT_SAMPLER_3D, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT_SAMPLER_CUBE, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.INT_SAMPLER_2D_ARRAY, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT_SAMPLER_2D, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT_SAMPLER_3D, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT_SAMPLER_CUBE, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _defineProperty(_UNIFORM_SETTERS, _constants.default.UNSIGNED_INT_SAMPLER_2D_ARRAY, function (gl, location, value) {
  return gl.uniform1i(location, value);
}), _UNIFORM_SETTERS); // Pre-allocated typed arrays for temporary conversion

var FLOAT_ARRAY = {};
var INT_ARRAY = {};
var UINT_ARRAY = {};
/* Functions to ensure the type of uniform values */

function toTypedArray(value, uniformLength, Type, cache) {
  var length = value.length;

  if (length % uniformLength) {
    _utils.log.warn("Uniform size should be multiples of ".concat(uniformLength), value)();
  }

  if (_instanceof(value, Type)) {
    return value;
  }

  var result = cache[length];

  if (!result) {
    result = new Type(length);
    cache[length] = result;
  }

  for (var i = 0; i < length; i++) {
    result[i] = value[i];
  }

  return result;
}

function toFloatArray(value, uniformLength) {
  return toTypedArray(value, uniformLength, Float32Array, FLOAT_ARRAY);
}

function toIntArray(value, uniformLength) {
  return toTypedArray(value, uniformLength, Int32Array, INT_ARRAY);
}

function toUIntArray(value, uniformLength) {
  return toTypedArray(value, uniformLength, Uint32Array, UINT_ARRAY);
}

function parseUniformName(name) {
  // name = name[name.length - 1] === ']' ?
  // name.substr(0, name.length - 3) : name;
  // if array name then clean the array brackets
  var UNIFORM_NAME_REGEXP = /([^\[]*)(\[[0-9]+\])?/;
  var matches = name.match(UNIFORM_NAME_REGEXP);

  if (!matches || matches.length < 2) {
    throw new Error("Failed to parse GLSL uniform name ".concat(name));
  }

  return {
    name: matches[1],
    length: matches[2] || 1,
    isArray: Boolean(matches[2])
  };
} // Returns a Magic Uniform Setter

/* eslint-disable complexity */


function getUniformSetter(gl, location, info) {
  var setter = UNIFORM_SETTERS[info.type];

  if (!setter) {
    throw new Error("Unknown GLSL uniform type ".concat(info.type));
  }

  return setter.bind(null, gl, location);
} // Basic checks of uniform values without knowledge of program
// To facilitate early detection of e.g. undefined values in JavaScript


function checkUniformValues(uniforms, source) {
  for (var uniformName in uniforms) {
    var value = uniforms[uniformName];

    if (!checkUniformValue(value)) {
      // Add space to source
      source = source ? "".concat(source, " ") : ''; // Value could be unprintable so write the object on console

      console.error("".concat(source, " Bad uniform ").concat(uniformName), value); // eslint-disable-line

      /* eslint-enable no-console */

      throw new Error("".concat(source, " Bad uniform ").concat(uniformName));
    }
  }

  return true;
} // TODO use type information during validation


function checkUniformValue(value) {
  if (Array.isArray(value) || ArrayBuffer.isView(value)) {
    return checkUniformArray(value);
  } // Check if single value is a number


  if (isFinite(value)) {
    return true;
  } else if (value === true || value === false) {
    return true;
  } else if (_instanceof(value, _texture.default) || _instanceof(value, _sampler.default)) {
    return true;
  } else if (_instanceof(value, _framebuffer.default)) {
    return Boolean(value.texture);
  }

  return false;
}

function checkUniformArray(value) {
  // Check that every element in array is a number, and at least 1 element
  if (value.length === 0) {
    return false;
  }

  var checkLength = Math.min(value.length, 16);

  for (var i = 0; i < checkLength; ++i) {
    if (!Number.isFinite(value[i])) {
      return false;
    }
  }

  return true;
}
/**
 * Given two values of a uniform, returns `true` if they are equal
 */


function areUniformsEqual(uniform1, uniform2) {
  if (Array.isArray(uniform1) || ArrayBuffer.isView(uniform1)) {
    if (!uniform2) {
      return false;
    }

    var len = uniform1.length;

    if (uniform2.length !== len) {
      return false;
    }

    for (var i = 0; i < len; i++) {
      if (uniform1[i] !== uniform2[i]) {
        return false;
      }
    }

    return true;
  }

  return uniform1 === uniform2;
} // Prepares a table suitable for console.table

/* eslint-disable max-statements */


function getUniformsTable() {
  var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
      _ref$header = _ref.header,
      header = _ref$header === void 0 ? 'Uniforms' : _ref$header,
      program = _ref.program,
      uniforms = _ref.uniforms,
      _ref$undefinedOnly = _ref.undefinedOnly,
      undefinedOnly = _ref$undefinedOnly === void 0 ? false : _ref$undefinedOnly;

  (0, _assert.default)(program);
  var SHADER_MODULE_UNIFORM_REGEXP = '.*_.*';
  var PROJECT_MODULE_UNIFORM_REGEXP = '.*Matrix'; // TODO - Use explicit list

  var uniformLocations = program._uniformSetters;
  var table = {}; // {[header]: {}};
  // Add program's provided uniforms (in alphabetical order)

  var uniformNames = Object.keys(uniformLocations).sort();
  var count = 0; // First add non-underscored uniforms (assumed not coming from shader modules)

  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = uniformNames[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var _uniformName = _step.value;

      if (!_uniformName.match(SHADER_MODULE_UNIFORM_REGEXP) && !_uniformName.match(PROJECT_MODULE_UNIFORM_REGEXP)) {
        if (addUniformToTable({
          table: table,
          header: header,
          uniforms: uniforms,
          uniformName: _uniformName,
          undefinedOnly: undefinedOnly
        })) {
          count++;
        }
      }
    } // add underscored uniforms (assumed from shader modules)

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

  var _iteratorNormalCompletion2 = true;
  var _didIteratorError2 = false;
  var _iteratorError2 = undefined;

  try {
    for (var _iterator2 = uniformNames[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
      var _uniformName2 = _step2.value;

      if (_uniformName2.match(PROJECT_MODULE_UNIFORM_REGEXP)) {
        if (addUniformToTable({
          table: table,
          header: header,
          uniforms: uniforms,
          uniformName: _uniformName2,
          undefinedOnly: undefinedOnly
        })) {
          count++;
        }
      }
    }
  } catch (err) {
    _didIteratorError2 = true;
    _iteratorError2 = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
        _iterator2.return();
      }
    } finally {
      if (_didIteratorError2) {
        throw _iteratorError2;
      }
    }
  }

  var _iteratorNormalCompletion3 = true;
  var _didIteratorError3 = false;
  var _iteratorError3 = undefined;

  try {
    for (var _iterator3 = uniformNames[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
      var _uniformName3 = _step3.value;

      if (!table[_uniformName3]) {
        if (addUniformToTable({
          table: table,
          header: header,
          uniforms: uniforms,
          uniformName: _uniformName3,
          undefinedOnly: undefinedOnly
        })) {
          count++;
        }
      }
    } // Create a table of unused uniforms

  } catch (err) {
    _didIteratorError3 = true;
    _iteratorError3 = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
        _iterator3.return();
      }
    } finally {
      if (_didIteratorError3) {
        throw _iteratorError3;
      }
    }
  }

  var unusedCount = 0;
  var unusedTable = {};

  if (!undefinedOnly) {
    for (var uniformName in uniforms) {
      var uniform = uniforms[uniformName];

      if (!table[uniformName]) {
        unusedCount++;
        unusedTable[uniformName] = _defineProperty({
          Type: "NOT USED: ".concat(uniform)
        }, header, (0, _utils.formatValue)(uniform));
      }
    }
  }

  return {
    table: table,
    count: count,
    unusedTable: unusedTable,
    unusedCount: unusedCount
  };
} // Helper


function addUniformToTable(_ref2) {
  var table = _ref2.table,
      header = _ref2.header,
      uniforms = _ref2.uniforms,
      uniformName = _ref2.uniformName,
      undefinedOnly = _ref2.undefinedOnly;
  var value = uniforms[uniformName];
  var isDefined = isUniformDefined(value);

  if (!undefinedOnly || !isDefined) {
    var _table$uniformName;

    table[uniformName] = (_table$uniformName = {}, _defineProperty(_table$uniformName, header, isDefined ? (0, _utils.formatValue)(value) : 'N/A'), _defineProperty(_table$uniformName, 'Uniform Type', isDefined ? value : 'NOT PROVIDED'), _table$uniformName);
    return true;
  }

  return false;
}

function isUniformDefined(value) {
  return value !== undefined && value !== null;
}
//# sourceMappingURL=uniforms.js.map