var _SHADER_TYPE;

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

import { VERTEX_SHADER, FRAGMENT_SHADER } from './constants';
import { resolveModules, getShaderModule } from './resolve-modules';
import { getPlatformShaderDefines, getVersionDefines } from './platform-defines';
import injectShader from './inject-shader';
import assert from '../utils/assert';
var SHADER_TYPE = (_SHADER_TYPE = {}, _defineProperty(_SHADER_TYPE, VERTEX_SHADER, 'vertex'), _defineProperty(_SHADER_TYPE, FRAGMENT_SHADER, 'fragment'), _SHADER_TYPE); // Precision prologue to inject before functions are injected in shader
// TODO - extract any existing prologue in the fragment source and move it up...

var FRAGMENT_SHADER_PROLOGUE = "precision highp float;\n\n"; // Inject a list of modules

export function assembleShaders(gl) {
  var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  var vs = opts.vs,
      fs = opts.fs;
  var modules = resolveModules(opts.modules || []);
  return {
    gl: gl,
    vs: assembleShader(gl, Object.assign({}, opts, {
      source: vs,
      type: VERTEX_SHADER,
      modules: modules
    })),
    fs: assembleShader(gl, Object.assign({}, opts, {
      source: fs,
      type: FRAGMENT_SHADER,
      modules: modules
    })),
    getUniforms: assembleGetUniforms(modules),
    modules: assembleModuleMap(modules)
  };
} // Pulls together complete source code for either a vertex or a fragment shader
// adding prologues, requested module chunks, and any final injections.

function assembleShader(gl, _ref) {
  var id = _ref.id,
      source = _ref.source,
      type = _ref.type,
      _ref$modules = _ref.modules,
      modules = _ref$modules === void 0 ? [] : _ref$modules,
      _ref$defines = _ref.defines,
      defines = _ref$defines === void 0 ? {} : _ref$defines,
      _ref$inject = _ref.inject,
      inject = _ref$inject === void 0 ? {} : _ref$inject,
      _ref$prologue = _ref.prologue,
      prologue = _ref$prologue === void 0 ? true : _ref$prologue,
      log = _ref.log;
  assert(typeof source === 'string', 'shader source must be a string');
  var isVertex = type === VERTEX_SHADER;
  var sourceLines = source.split('\n');
  var glslVersion = 100;
  var versionLine = '';
  var coreSource = source; // Extract any version directive string from source.
  // TODO : keep all pre-processor statements at the begining of the shader.

  if (sourceLines[0].indexOf('#version ') === 0) {
    glslVersion = 300; // TODO - regexp that matches atual version number

    versionLine = sourceLines[0];
    coreSource = sourceLines.slice(1).join('\n');
  } // Add platform defines (use these to work around platform-specific bugs and limitations)
  // Add common defines (GLSL version compatibility, feature detection)
  // Add precision declaration for fragment shaders


  var assembledSource = prologue ? "".concat(versionLine, "\n").concat(getShaderName({
    id: id,
    source: source,
    type: type
  }), "\n").concat(getPlatformShaderDefines(gl), "\n").concat(getVersionDefines(gl, glslVersion, !isVertex), "\n").concat(getApplicationDefines(defines), "\n").concat(isVertex ? '' : FRAGMENT_SHADER_PROLOGUE, "\n") : "".concat(versionLine, "\n"); // Add source of dependent modules in resolved order

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

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

      switch (module.name) {
        case 'inject':
          injectStandardStubs = true;
          break;

        default:
          module.checkDeprecations(coreSource, log);
          var moduleSource = module.getModuleSource(type, glslVersion); // Add the module source, and a #define that declares it presence

          assembledSource += moduleSource;
      }
    } // Add the version directive and actual source of this shader

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

  assembledSource += coreSource; // Apply any requested shader injections

  assembledSource = injectShader(assembledSource, type, inject, injectStandardStubs);
  return assembledSource;
} // Returns a combined `getUniforms` covering the options for all the modules,
// the created function will pass on options to the inidividual `getUniforms`
// function of each shader module and combine the results into one object that
// can be passed to setUniforms.


function assembleGetUniforms(modules) {
  return function getUniforms(opts) {
    var uniforms = {};
    var _iteratorNormalCompletion2 = true;
    var _didIteratorError2 = false;
    var _iteratorError2 = undefined;

    try {
      for (var _iterator2 = modules[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
        var module = _step2.value;
        // `modules` is already sorted by dependency level. This guarantees that
        // modules have access to the uniforms that are generated by their dependencies.
        var moduleUniforms = module.getUniforms(opts, uniforms);
        Object.assign(uniforms, moduleUniforms);
      }
    } catch (err) {
      _didIteratorError2 = true;
      _iteratorError2 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
          _iterator2.return();
        }
      } finally {
        if (_didIteratorError2) {
          throw _iteratorError2;
        }
      }
    }

    return uniforms;
  };
} // Returns a map with module names as keys, resolving to their module definitions
// The presence of a key indicates that the module is available in this program,
// whether directly included, or through a dependency of some other module


function assembleModuleMap(modules) {
  var result = {};
  var _iteratorNormalCompletion3 = true;
  var _didIteratorError3 = false;
  var _iteratorError3 = undefined;

  try {
    for (var _iterator3 = modules[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
      var moduleName = _step3.value;
      var shaderModule = getShaderModule(moduleName);
      result[moduleName] = shaderModule;
    }
  } catch (err) {
    _didIteratorError3 = true;
    _iteratorError3 = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
        _iterator3.return();
      }
    } finally {
      if (_didIteratorError3) {
        throw _iteratorError3;
      }
    }
  }

  return result;
} // Generate "glslify-compatible" SHADER_NAME defines
// These are understood by the GLSL error parsing function
// If id is provided and no SHADER_NAME constant is present in source, create one


function getShaderName(_ref2) {
  var id = _ref2.id,
      source = _ref2.source,
      type = _ref2.type;
  var injectShaderName = id && typeof id === 'string' && source.indexOf('SHADER_NAME') === -1;
  return injectShaderName ? "\n#define SHADER_NAME ".concat(id, "_").concat(SHADER_TYPE[type], "\n\n") : '';
} // Generates application defines from an object


function getApplicationDefines() {
  var defines = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var count = 0;
  var sourceText = '';

  for (var define in defines) {
    if (count === 0) {
      sourceText += '\n// APPLICATION DEFINES\n';
    }

    count++;
    sourceText += "#define ".concat(define.toUpperCase(), " ").concat(defines[define], "\n");
  }

  if (count === 0) {
    sourceText += '\n';
  }

  return sourceText;
}
//# sourceMappingURL=assemble-shaders.js.map