//"use strict";

// Implementation details for the 'runtime environment' we generate in
// JavaScript. The Runtime object itself is used both during compilation,
// and is available at runtime (dynamic compilation). The RuntimeGenerator
// helps to create the Runtime object (written so that the Runtime object
// itself is as optimized as possible - no unneeded runtime checks).

var RuntimeGenerator = {
  alloc: function(size, type, sep, ignoreAlign) {
    sep = sep || ';';
    var ret = type + 'TOP';
    ret += sep + type + 'TOP = (' + type + 'TOP + ' + size + ')|0';
    if ({{{ STACK_ALIGN }}} > 1 && !ignoreAlign) {
      ret += sep + RuntimeGenerator.alignMemory(type + 'TOP', {{{ STACK_ALIGN }}});
    }
    return ret;
  },

  // An allocation that lives as long as the current function call
  stackAlloc: function(size, sep) {
    sep = sep || ';';
    var ret = RuntimeGenerator.alloc(size, 'STACK', sep, (isNumber(size) && parseInt(size) % {{{ STACK_ALIGN }}} == 0));
    if (ASSERTIONS || STACK_OVERFLOW_CHECK >= 2) {
      ret += sep + '(assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)';
    }
    return ret;
  },

  stackEnter: function(initial, force) {
    if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
    var ret = 'var sp=0;sp=STACKTOP';
    if (initial > 0) ret += ';STACKTOP=(STACKTOP+' + initial + ')|0';
    assert(initial % Runtime.STACK_ALIGN == 0);
    if (ASSERTIONS && Runtime.STACK_ALIGN == 4) {
      ret += '; (assert(' + asmCoercion('!(STACKTOP&3)', 'i32') + ')|0)';
    }
    if (ASSERTIONS) {
      ret += '; (assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)';
    }
    return ret;
  },

  stackExit: function(initial, force) {
    if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
    return 'STACKTOP=sp';
  },

  // An allocation that cannot normally be free'd (except through sbrk, which once
  // called, takes control of STATICTOP)
  staticAlloc: function(size) {
    if (ASSERTIONS) size = '(assert(!staticSealed),' + size + ')'; // static area must not be sealed
#if USE_PTHREADS
    if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.staticAlloc is not available in pthreads!'; // This is because each worker has its own copy of STATICTOP, of which main thread is authoritative.
#endif
    var ret = RuntimeGenerator.alloc(size, 'STATIC');
    return ret;
  },

  // allocation on the top of memory, adjusted dynamically by sbrk
  dynamicAlloc: function(size) {
    var ret = '';
    if (ASSERTIONS) ret += 'assert(DYNAMICTOP_PTR);'; // dynamic area must be ready
    ret += 'var ret = HEAP32[DYNAMICTOP_PTR>>2];'
      + 'var end = (((ret + size + 15)|0) & -16);'
      + 'HEAP32[DYNAMICTOP_PTR>>2] = end;'
      + 'if (end >= TOTAL_MEMORY) {'
      +   'var success = enlargeMemory();'
      +     'if (!success) {'
      +       'HEAP32[DYNAMICTOP_PTR>>2] = ret;'
      +       'return 0;'
      +    '}'
      +  '}'
      + 'return ret;';
    return ret;
  },

  forceAlign: function(target, quantum) {
    quantum = quantum || {{{ QUANTUM_SIZE }}};
    if (quantum == 1) return target;
    if (isNumber(target) && isNumber(quantum)) {
      return Math.ceil(target/quantum)*quantum;
    } else if (isNumber(quantum) && isPowerOfTwo(quantum)) {
      return '(((' +target + ')+' + (quantum-1) + ')&' + -quantum + ')';
    }
    return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum;
  },

  alignMemory: function(target, quantum) {
    if (typeof quantum !== 'number') {
      quantum = '(quantum ? quantum : {{{ STACK_ALIGN }}})';
    }
    return target + ' = ' + RuntimeGenerator.forceAlign(target, quantum);
  },

  // Given two 32-bit unsigned parts of an emulated 64-bit number, combine them into a JS number (double).
  // Rounding is inevitable if the number is large. This is a particular problem for small negative numbers
  // (-1 will be rounded!), so handle negatives separately and carefully
  makeBigInt: function(low, high, unsigned) {
    var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
    var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
    if (typeof unsigned === 'string') return '(' + unsigned + ' ? ' + unsignedRet + ' : ' + signedRet + ')';
    return unsigned ? unsignedRet : signedRet;
  }
};

function unInline(name_, params, isExpression) {
  if (isExpression) {
    var src = '(function(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })';
  } else {
    var src = '(function(' + params + ') { ' + RuntimeGenerator[name_].apply(null, params) + '})';
  }
  var ret = eval(src);
  return ret;
}

var Compiletime = {
  isPointerType: isPointerType,
  isStructType: isStructType,

  isNumberType: function(type) {
    return type in Compiletime.INT_TYPES || type in Compiletime.FLOAT_TYPES;
  },

  INT_TYPES: set('i1', 'i8', 'i16', 'i32', 'i64'),
  FLOAT_TYPES: set('float', 'double'),
};

var Runtime = {
  // When a 64 bit long is returned from a compiled function the least significant
  // 32 bit word is passed in the return value, but the most significant 32 bit
  // word is placed in tempRet0. This provides an accessor for that value.
  setTempRet0: function(value) {
    tempRet0 = value;
    return value;
  },
  getTempRet0: function() {
    return tempRet0;
  },
  stackSave: function() {
    return STACKTOP;
  },
  stackRestore: function(stackTop) {
    STACKTOP = stackTop;
  },

  //! Returns the size of a type, as C/C++ would have it (in 32-bit), in bytes.
  //! @param type The type, by name.
  getNativeTypeSize: function(type) {
    switch (type) {
      case 'i1': case 'i8': return 1;
      case 'i16': return 2;
      case 'i32': return 4;
      case 'i64': return 8;
      case 'float': return 4;
      case 'double': return 8;
      default: {
        if (type[type.length-1] === '*') {
          return Runtime.QUANTUM_SIZE; // A pointer
        } else if (type[0] === 'i') {
          var bits = parseInt(type.substr(1));
          assert(bits % 8 === 0);
          return bits/8;
        } else {
          return 0;
        }
      }
    }
  },

  //! Returns the size of a structure field, as C/C++ would have it (in 32-bit,
  //! for now).
  //! @param type The type, by name.
  getNativeFieldSize: function(type) {
    return Math.max(Runtime.getNativeTypeSize(type), Runtime.QUANTUM_SIZE);
  },

  STACK_ALIGN: {{{ STACK_ALIGN }}},

  // This must be called before reading a double or i64 vararg. It will bump the pointer properly.
  // It also does an assert on i32 values, so it's nice to call it before all varargs calls.
  prepVararg: function(ptr, type) {
    if (type === 'double' || type === 'i64') {
      // move so the load is aligned
      if (ptr & 7) {
        assert((ptr & 7) === 4);
        ptr += 4;
      }
    } else {
      assert((ptr & 3) === 0);
    }
    return ptr;
  },

  // type can be a native type or a struct (or null, for structs we only look at size here)
  getAlignSize: function(type, size, vararg) {
    // we align i64s and doubles on 64-bit boundaries, unlike x86
    if (!vararg && (type == 'i64' || type == 'double')) return 8;
    if (!type) return Math.min(size, 8); // align structures internally to 64 bits
    return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE);
  },

  dynCall: function(sig, ptr, args) {
    if (args && args.length) {
#if ASSERTIONS
      assert(args.length == sig.length-1);
#endif
#if ASSERTIONS
      assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
#endif
      return Module['dynCall_' + sig].apply(null, [ptr].concat(args));
    } else {
#if ASSERTIONS
      assert(sig.length == 1);
#endif
#if ASSERTIONS
      assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
#endif
      return Module['dynCall_' + sig].call(null, ptr);
    }
  },

#if EMULATED_FUNCTION_POINTERS
  getFunctionTables: function(module) {
    if (!module) module = Module;
    var tables = {};
    for (var t in module) {
      if (/^FUNCTION_TABLE_.*/.test(t)) {
        var table = module[t];
        if (typeof table === 'object') tables[t.substr('FUNCTION_TABLE_'.length)] = table;
      }
    }
    return tables;
  },

  alignFunctionTables: function(module) {
    var tables = Runtime.getFunctionTables(module);
    var maxx = 0;
    for (var sig in tables) {
      maxx = Math.max(maxx, tables[sig].length);
    }
    assert(maxx >= 0);
    for (var sig in tables) {
      var table = tables[sig];
      while (table.length < maxx) table.push(0);
    }
    return maxx;
  },

  registerFunctions: function(sigs, newModule) {
    sigs.forEach(function(sig) {
      if (!Module['FUNCTION_TABLE_' + sig]) {
        Module['FUNCTION_TABLE_' + sig] = [];
      }
    });
    var oldMaxx = Runtime.alignFunctionTables(); // align the new tables we may have just added
    var newMaxx = Runtime.alignFunctionTables(newModule);
    var maxx = oldMaxx + newMaxx;
    sigs.forEach(function(sig) {
      var newTable = newModule['FUNCTION_TABLE_' + sig];
      var oldTable = Module['FUNCTION_TABLE_' + sig];
      assert(newTable !== oldTable);
      assert(oldTable.length === oldMaxx);
      for (var i = 0; i < newTable.length; i++) {
        oldTable.push(newTable[i]);
      }
      assert(oldTable.length === maxx);
    });
    assert(maxx === Runtime.alignFunctionTables()); // align the ones we didn't touch
  },
#endif

  functionPointers: new Array(RESERVED_FUNCTION_POINTERS),

  addFunction: function(func) {
#if EMULATED_FUNCTION_POINTERS == 0
    for (var i = 0; i < Runtime.functionPointers.length; i++) {
      if (!Runtime.functionPointers[i]) {
        Runtime.functionPointers[i] = func;
        return {{{ FUNCTION_POINTER_ALIGNMENT }}}*(1 + i);
      }
    }
    throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.';
#else
#if BINARYEN
    // we can simply appent to the wasm table
    var table = Module['wasmTable'];
    var ret = table.length;
    table.grow(1);
    table.set(ret, func);
    return ret;
#else
    Runtime.alignFunctionTables(); // XXX we should rely on this being an invariant
    var tables = Runtime.getFunctionTables();
    var ret = -1;
    for (var sig in tables) {
      var table = tables[sig];
      if (ret < 0) ret = table.length;
      else assert(ret === table.length);
      table.push(func);
    }
    return ret;
#endif
#endif
  },

  removeFunction: function(index) {
#if EMULATED_FUNCTION_POINTERS == 0
    Runtime.functionPointers[(index-{{{ FUNCTION_POINTER_ALIGNMENT }}})/{{{ FUNCTION_POINTER_ALIGNMENT }}}] = null;
#else
    Runtime.alignFunctionTables(); // XXX we should rely on this being an invariant
    var tables = Runtime.getFunctionTables();
    for (var sig in tables) {
      tables[sig][index] = null;
    }
#endif
  },

#if RELOCATABLE
  loadedDynamicLibraries: [],

  loadDynamicLibrary: function(lib) {
    var libModule;
#if BINARYEN
    var bin;
    if (lib.buffer) {
      // we were provided the binary, in a typed array
      bin = lib;
    } else {
      // load the binary synchronously
      bin = Module['readBinary'](lib);
    }
    libModule = Runtime.loadWebAssemblyModule(bin);
#else
    var src = Module['read'](lib);
    libModule = eval(src)(
      Runtime.alignFunctionTables(),
      Module
    );
#endif
    // add symbols into global namespace TODO: weak linking etc.
    for (var sym in libModule) {
      if (!Module.hasOwnProperty(sym)) {
        Module[sym] = libModule[sym];
      }
#if ASSERTIONS == 2
      else if (sym[0] === '_') {
        var curr = Module[sym], next = libModule[sym];
        // don't warn on functions - might be odr, linkonce_odr, etc.
        if (!(typeof curr === 'function' && typeof next === 'function')) {
          Module.printErr("warning: trying to dynamically load symbol '" + sym + "' (from '" + lib + "') that already exists (duplicate symbol? or weak linking, which isn't supported yet?)"); // + [curr, ' vs ', next]);
        }
      }
#endif
    }
    Runtime.loadedDynamicLibraries.push(libModule);
  },

#if BINARYEN
  // Loads a side module from binary data
  loadWebAssemblyModule: function(binary) {
    var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer);
    assert(int32View[0] == 0x6d736100, 'need to see wasm magic number'); // \0wasm
    // we should see the dylink section right after the magic number and wasm version
    assert(binary[8] === 0, 'need the dylink section to be first')
    var next = 9;
    function getLEB() {
      var ret = 0;
      var mul = 1;
      while (1) {
        var byte = binary[next++];
        ret += ((byte & 0x7f) * mul);
        mul *= 0x80;
        if (!(byte & 0x80)) break;
      }
      return ret;
    }
    var sectionSize = getLEB();
    assert(binary[next] === 6);                 next++; // size of "dylink" string
    assert(binary[next] === 'd'.charCodeAt(0)); next++;
    assert(binary[next] === 'y'.charCodeAt(0)); next++;
    assert(binary[next] === 'l'.charCodeAt(0)); next++;
    assert(binary[next] === 'i'.charCodeAt(0)); next++;
    assert(binary[next] === 'n'.charCodeAt(0)); next++;
    assert(binary[next] === 'k'.charCodeAt(0)); next++;
    var memorySize = getLEB();
    var tableSize = getLEB();
    var env = Module['asmLibraryArg'];
    // TODO: use only memoryBase and tableBase, need to update asm.js backend
    var table = Module['wasmTable'];
    var oldTableSize = table.length;
    env['memoryBase'] = env['gb'] = Runtime.alignMemory(getMemory(memorySize + Runtime.STACK_ALIGN), Runtime.STACK_ALIGN); // TODO: add to cleanups
    env['tableBase'] = env['fb'] = oldTableSize;
    var originalTable = table;
    table.grow(tableSize);
    assert(table === originalTable);
    // zero-initialize memory and table TODO: in some cases we can tell it is already zero initialized
    for (var i = env['memoryBase']; i < env['memoryBase'] + memorySize; i++) {
      HEAP8[i] = 0;
    }
    for (var i = env['tableBase']; i < env['tableBase'] + tableSize; i++) {
      table.set(i, null);
    }
    // copy currently exported symbols so the new module can import them
    for (var x in Module) {
      if (!(x in env)) {
        env[x] = Module[x];
      }
    }
    var info = {
      global: {
        'NaN': NaN,
        'Infinity': Infinity,
        'Math': Math
      },
      env: env
    };
#if ASSERTIONS
    var oldTable = [];
    for (var i = 0; i < oldTableSize; i++) {
      oldTable.push(table.get(i));
    }
#endif
    // create a module from the instance
    var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), info);
#if ASSERTIONS
    // the table should be unchanged
    assert(table === originalTable);
    assert(table === Module['wasmTable']);
    if (instance.exports['table']) {
      assert(table === instance.exports['table']);
    }
    // the old part of the table should be unchanged
    for (var i = 0; i < oldTableSize; i++) {
      assert(table.get(i) === oldTable[i], 'old table entries must remain the same');
    }
    // verify that the new table region was filled in
    for (var i = 0; i < tableSize; i++) {
      assert(table.get(oldTableSize + i) !== undefined, 'table entry was not filled in');
    }
#endif
    var exports = {};
    for (var e in instance.exports) {
      var value = instance.exports[e];
      if (typeof value === 'number') {
        // relocate it - modules export the absolute value, they can't relocate before they export
        value = value + env['memoryBase'];
      }
      exports[e] = value;
    }
    // initialize the module
    var init = exports['__post_instantiate'];
    if (init) {
      if (runtimeInitialized) {
        init();
      } else {
        // we aren't ready to run compiled code yet
        __ATINIT__.push(init);
      }
    }
    return exports;
  },
#endif
#endif

  warnOnce: function(text) {
    if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {};
    if (!Runtime.warnOnce.shown[text]) {
      Runtime.warnOnce.shown[text] = 1;
      Module.printErr(text);
    }
  },

  funcWrappers: {},

  getFuncWrapper: function(func, sig) {
    if (!func) return; // on null pointer, return undefined
    assert(sig);
    if (!Runtime.funcWrappers[sig]) {
      Runtime.funcWrappers[sig] = {};
    }
    var sigCache = Runtime.funcWrappers[sig];
    if (!sigCache[func]) {
      // optimize away arguments usage in common cases
      if (sig.length === 1) {
        sigCache[func] = function dynCall_wrapper() {
          return Runtime.dynCall(sig, func);
        };
      } else if (sig.length === 2) {
        sigCache[func] = function dynCall_wrapper(arg) {
          return Runtime.dynCall(sig, func, [arg]);
        };
      } else {
        // general case
        sigCache[func] = function dynCall_wrapper() {
          return Runtime.dynCall(sig, func, Array.prototype.slice.call(arguments));
        };
      }
    }
    return sigCache[func];
  },

#if RETAIN_COMPILER_SETTINGS
  compilerSettings: {},
#endif

  getCompilerSetting: function(name) {
#if RETAIN_COMPILER_SETTINGS == 0
    throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work';
#else
    if (!(name in Runtime.compilerSettings)) return 'invalid compiler setting: ' + name;
    return Runtime.compilerSettings[name];
#endif
  },

#if RUNTIME_DEBUG
  debug: true, // Switch to false at runtime to disable logging at the right times

  printObjectList: [],

  prettyPrint: function(arg) {
    if (typeof arg == 'undefined') return '!UNDEFINED!';
    if (typeof arg == 'boolean') arg = arg + 0;
    if (!arg) return arg;
    var index = Runtime.printObjectList.indexOf(arg);
    if (index >= 0) return '<' + arg + '|' + index + '>';
    if (arg.toString() == '[object HTMLImageElement]') {
      return arg + '\n\n';
    }
    if (arg.byteLength) {
      return '{' + Array.prototype.slice.call(arg, 0, Math.min(arg.length, 400)) + '}'; // Useful for correct arrays, less so for compiled arrays, see the code below for that
      var buf = new ArrayBuffer(32);
      var i8buf = new Int8Array(buf);
      var i16buf = new Int16Array(buf);
      var f32buf = new Float32Array(buf);
      switch(arg.toString()) {
        case '[object Uint8Array]':
          i8buf.set(arg.subarray(0, 32));
          break;
        case '[object Float32Array]':
          f32buf.set(arg.subarray(0, 5));
          break;
        case '[object Uint16Array]':
          i16buf.set(arg.subarray(0, 16));
          break;
        default:
          alert('unknown array for debugging: ' + arg);
          throw 'see alert';
      }
      var ret = '{' + arg.byteLength + ':\n';
      var arr = Array.prototype.slice.call(i8buf);
      ret += 'i8:' + arr.toString().replace(/,/g, ',') + '\n';
      arr = Array.prototype.slice.call(f32buf, 0, 8);
      ret += 'f32:' + arr.toString().replace(/,/g, ',') + '}';
      return ret;
    }
    if (typeof arg == 'object') {
      Runtime.printObjectList.push(arg);
      return '<' + arg + '|' + (Runtime.printObjectList.length-1) + '>';
    }
    if (typeof arg == 'number') {
      if (arg > 0) return '0x' + arg.toString(16) + ' (' + arg + ')';
    }
    return arg;
  }
#endif
};

Runtime.stackAlloc = unInline('stackAlloc', ['size'], true);
Runtime.staticAlloc = unInline('staticAlloc', ['size'], true);
Runtime.dynamicAlloc = unInline('dynamicAlloc', ['size'], false);
Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum'], true);
Runtime.makeBigInt = unInline('makeBigInt', ['low', 'high', 'unsigned'], true);

if (MAIN_MODULE || SIDE_MODULE) {
  Runtime.tempRet0 = 0;
  Runtime.getTempRet0 = function() {
    return Runtime.tempRet0;
  };
  Runtime.setTempRet0 = function(x) {
    Runtime.tempRet0 = x;
    return x;
  };
}

function getRuntime() {
  var ret = 'var Runtime = {\n';
  for (i in Runtime) {
    var item = Runtime[i];
    ret += '  ' + i + ': ';
    if (typeof item === 'function') {
      ret += item.toString();
    } else {
      ret += JSON.stringify(item);
    }
    ret += ',\n';
  }
  return ret + '  __dummy__: 0\n}\n';
}

// Additional runtime elements, that need preprocessing

// Converts a value we have as signed, into an unsigned value. For
// example, -1 in int32 would be a very large number as unsigned.
function unSign(value, bits, ignore) {
  if (value >= 0) {
    return value;
  }
  return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts
                    : Math.pow(2, bits)         + value;
}

// Converts a value we have as unsigned, into a signed value. For
// example, 200 in a uint8 would be a negative number.
function reSign(value, bits, ignore) {
  if (value <= 0) {
    return value;
  }
  var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32
                        : Math.pow(2, bits-1);
  if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that
                                                       // but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors
                                                       // TODO: In i64 mode 1, resign the two parts separately and safely
    value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts
  }
  return value;
}

// The address globals begin at. Very low in memory, for code size and optimization opportunities.
// Above 0 is static memory, starting with globals.
// Then the stack.
// Then 'dynamic' memory for sbrk.
Runtime.GLOBAL_BASE = {{{ GLOBAL_BASE }}};

if (RETAIN_COMPILER_SETTINGS) {
  var blacklist = set('STRUCT_INFO');
  for (var x in this) {
    try {
      if (x[0] !== '_' && !(x in blacklist) && x == x.toUpperCase() && (typeof this[x] === 'number' || typeof this[x] === 'string' || this.isArray())) Runtime.compilerSettings[x] = this[x];
    } catch(e){}
  }
}

