// These types roughly model the object that the JS files generated by Emscripten define. Copied from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/emscripten/index.d.ts and turned into a type definition rather than a global to support our way of using Emscripten.
// TODO(@surma): Upstream this?
declare namespace EmscriptenWasm {
  type ModuleFactory<T extends Module = Module> = (
    moduleOverrides?: ModuleOpts,
  ) => Promise<T>;

  type EnvironmentType = 'WEB' | 'NODE' | 'SHELL' | 'WORKER';

  // Options object for modularized Emscripten files. Shoe-horned by @surma.
  // FIXME: This an incomplete definition!
  interface ModuleOpts {
    mainScriptUrlOrBlob?: string;
    noInitialRun?: boolean;
    locateFile?: (url: string) => string;
    onRuntimeInitialized?: () => void;
    instantiateWasm?: (
      imports: WebAssembly.Imports,
      successCallback: (module: WebAssembly.Module) => void,
    ) => WebAssembly.Exports;
  }

  interface Module {
    print(str: string): void;
    printErr(str: string): void;
    arguments: string[];
    environment: EnvironmentType;
    preInit: { (): void }[];
    preRun: { (): void }[];
    postRun: { (): void }[];
    preinitializedWebGLContext: WebGLRenderingContext;
    noInitialRun: boolean;
    noExitRuntime: boolean;
    logReadFiles: boolean;
    filePackagePrefixURL: string;
    wasmBinary: ArrayBuffer;

    destroy(object: object): void;
    getPreloadedPackage(
      remotePackageName: string,
      remotePackageSize: number,
    ): ArrayBuffer;
    instantiateWasm(
      imports: WebAssembly.Imports,
      successCallback: (module: WebAssembly.Module) => void,
    ): WebAssembly.Exports;
    locateFile(url: string): string;
    onCustomMessage(event: MessageEvent): void;

    Runtime: any;

    ccall(
      ident: string,
      returnType: string | null,
      argTypes: string[],
      args: any[],
    ): any;
    cwrap(ident: string, returnType: string | null, argTypes: string[]): any;

    setValue(ptr: number, value: any, type: string, noSafe?: boolean): void;
    getValue(ptr: number, type: string, noSafe?: boolean): number;

    ALLOC_NORMAL: number;
    ALLOC_STACK: number;
    ALLOC_STATIC: number;
    ALLOC_DYNAMIC: number;
    ALLOC_NONE: number;

    allocate(slab: any, types: string, allocator: number, ptr: number): number;
    allocate(
      slab: any,
      types: string[],
      allocator: number,
      ptr: number,
    ): number;

    Pointer_stringify(ptr: number, length?: number): string;
    UTF16ToString(ptr: number): string;
    stringToUTF16(str: string, outPtr: number): void;
    UTF32ToString(ptr: number): string;
    stringToUTF32(str: string, outPtr: number): void;

    // USE_TYPED_ARRAYS == 1
    HEAP: Int32Array;
    IHEAP: Int32Array;
    FHEAP: Float64Array;

    // USE_TYPED_ARRAYS == 2
    HEAP8: Int8Array;
    HEAP16: Int16Array;
    HEAP32: Int32Array;
    HEAPU8: Uint8Array;
    HEAPU16: Uint16Array;
    HEAPU32: Uint32Array;
    HEAPF32: Float32Array;
    HEAPF64: Float64Array;

    TOTAL_STACK: number;
    TOTAL_MEMORY: number;
    FAST_MEMORY: number;

    addOnPreRun(cb: () => any): void;
    addOnInit(cb: () => any): void;
    addOnPreMain(cb: () => any): void;
    addOnExit(cb: () => any): void;
    addOnPostRun(cb: () => any): void;

    // Tools
    intArrayFromString(
      stringy: string,
      dontAddNull?: boolean,
      length?: number,
    ): number[];
    intArrayToString(array: number[]): string;
    writeStringToMemory(
      str: string,
      buffer: number,
      dontAddNull: boolean,
    ): void;
    writeArrayToMemory(array: number[], buffer: number): void;
    writeAsciiToMemory(str: string, buffer: number, dontAddNull: boolean): void;

    addRunDependency(id: any): void;
    removeRunDependency(id: any): void;

    preloadedImages: any;
    preloadedAudios: any;

    _malloc(size: number): number;
    _free(ptr: number): void;

    // Augmentations below by @surma.
    onRuntimeInitialized: () => void | null;
  }
}
