'use strict';

// generates c wrappers with off-thread versions of specified functions

let path = require('path');

let { functions } = require('./parse-api.js');
let asyncFns = require('./async-fns.js');

let wrappers = [];

for (let fnName of asyncFns) {
  let fn = functions.find(f => f.name === fnName);
  if (fn == null) {
    throw new Error(`could not find definition for ${fnName}`);
  }
  let wrapper;
  if (fn.cRet === 'Z3_string') {
    wrapper = `wrapper_str`;
  } else if (['int', 'unsigned', 'void'].includes(fn.cRet) || fn.cRet.startsWith('Z3_')) {
    wrapper = `wrapper`;
  } else {
    throw new Error(`async function with unknown return type ${fn.cRet}`);
  }

  wrappers.push(
    `
extern "C" void async_${fn.name}(${fn.params
      .map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
      .join(', ')}) {
  ${wrapper}<decltype(&${fn.name}), &${fn.name}>(${fn.params.map(p => `${p.name}`).join(', ')});
}
`.trim(),
  );
}

console.log(`// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
// DO NOT EDIT IT BY HAND

#include <thread>

#include <emscripten.h>

#include "../../z3.h"

template<typename Fn, Fn fn, typename... Args>
void wrapper(Args&&... args) {
  std::thread t([...args = std::forward<Args>(args)] {
    try {
      auto result = fn(args...);
      MAIN_THREAD_ASYNC_EM_ASM({
        resolve_async($0);
      }, result);
    } catch (...) {
      MAIN_THREAD_ASYNC_EM_ASM({
        reject_async('failed with unknown exception');
      });
      throw;
    }
  });
  t.detach();
}

template<typename Fn, Fn fn, typename... Args>
void wrapper_str(Args&&... args) {
  std::thread t([...args = std::forward<Args>(args)] {
    try {
      auto result = fn(args...);
      MAIN_THREAD_ASYNC_EM_ASM({
        resolve_async(UTF8ToString($0));
      }, result);
    } catch (...) {
      MAIN_THREAD_ASYNC_EM_ASM({
        reject_async('failed with unknown exception');
      });
      throw;
    }
  });
  t.detach();
}

${wrappers.join('\n\n')}`);
