const emsimp = (f_map_imports, s_tag) => {
    s_tag += ': ';
    let AB_HEAP;
    let ATU8_HEAP;
    let ATU32_HEAP;
    // eslint-disable-next-line no-console
    const console_out = (s_channel, s_out) => console[s_channel](s_tag + s_out.replace(/\0/g, '\n'));
    let s_error = '';
    // for converting bytes to text
    const utf8 = new TextDecoder();
    const h_fds = {
        // stdout
        1(s_out) {
            console_out('debug', s_out);
        },
        // stderr
        2(s_out) {
            console_out('error', (s_error = s_out));
        }
    };
    const g_imports = f_map_imports({
        abort() {
            throw Error(s_tag + (s_error || 'An unknown error occurred'));
        },
        memcpy: (ip_dst, ip_src, nb_size) => ATU8_HEAP.copyWithin(ip_dst, ip_src, ip_src + nb_size),
        resize(_) {
            throw Error(s_tag + 'Out of memory');
        },
        write(i_fd, ip_iov, nl_iovs, ip_written) {
            // output string
            let s_out = '';
            // track number of bytes read from buffers
            let cb_read = 0;
            // each pending iov
            for (let i_iov = 0; i_iov < nl_iovs; i_iov++) {
                // start of buffer in memory
                const ip_start = ATU32_HEAP[ip_iov >> 2];
                // size of buffer
                const nb_len = ATU32_HEAP[(ip_iov + 4) >> 2];
                ip_iov += 8;
                // extract text from buffer
                s_out += utf8.decode(ATU8_HEAP.subarray(ip_start, ip_start + nb_len));
                // update number of bytes read
                cb_read += nb_len;
            }
            // route to fd
            if (h_fds[i_fd]) {
                h_fds[i_fd](s_out);
            }
            else {
                // no fd found
                throw new Error(`libsecp256k1 tried writing to non-open file descriptor: ${i_fd}\n${s_out}`);
            }
            // write bytes read
            ATU32_HEAP[ip_written >> 2] = cb_read;
            // no error
            return 0;
        }
    });
    return [
        g_imports,
        (d_memory) => [
            (AB_HEAP = d_memory.buffer),
            (ATU8_HEAP = new Uint8Array(AB_HEAP)),
            (ATU32_HEAP = new Uint32Array(AB_HEAP))
        ]
    ];
};

/*
* ================================
*     GENERATED FILE WARNING
* Do not edit this file manually.
* ================================
*/
const map_wasm_imports = (g_imports) => ({
    a: {
        a: g_imports.abort,
        f: g_imports.memcpy,
        d: g_imports.resize,
        e: () => 52, // _fd_close,
        c: () => 70, // _fd_seek,
        b: g_imports.write,
    },
});
const map_wasm_exports = (g_exports) => ({
    malloc: g_exports['i'],
    free: g_exports['j'],
    sha256_initialize: g_exports['l'],
    sha256_write: g_exports['m'],
    sha256_finalize: g_exports['n'],
    context_create: g_exports['o'],
    xonly_pubkey_parse: g_exports['p'],
    xonly_pubkey_serialize: g_exports['q'],
    keypair_create: g_exports['r'],
    keypair_xonly_pub: g_exports['s'],
    schnorrsig_sign32: g_exports['t'],
    schnorrsig_verify: g_exports['u'],
    sbrk: g_exports['sbrk'],
    memory: g_exports['g'],
    init: () => g_exports['h'](),
});

const S_TAG_BIP340_VERIFY = 'BIP340 verify: ';
const S_REASON_INVALID_SK = 'Invalid private key';
const S_REASON_INVALID_PK = 'Invalid public key';
/**
 * Creates a new instance of the secp256k1 WASM and returns its ES wrapper
 * @param z_src - a Response containing the WASM binary, a Promise that resolves to one,
 * 	or the raw bytes to the WASM binary as a {@link BufferSource}
 * @returns the wrapper API
 */
const WasmSecp256k1 = async (z_src) => {
    // prepare the runtime
    const [g_imports, f_bind_heap] = emsimp(map_wasm_imports, 'wasm-secp256k1');
    // prep the wasm module
    let d_wasm;
    // instantiate wasm binary by streaming the response bytes
    if (z_src instanceof Response || z_src instanceof Promise) {
        d_wasm = await WebAssembly.instantiateStreaming(z_src, g_imports);
    }
    else {
        // instantiate using raw binary
        d_wasm = await WebAssembly.instantiate(z_src, g_imports);
    }
    // create the libsecp256k1 exports struct
    const g_wasm = map_wasm_exports(d_wasm.instance.exports);
    // bind the heap and ref its view(s)
    const [, ATU8_HEAP] = f_bind_heap(g_wasm.memory);
    // call into the wasm module's init method
    g_wasm.init();
    const ip_sk = g_wasm.malloc(32 /* ByteLens.PRIVATE_KEY */);
    const ip_ent = g_wasm.malloc(32 /* ByteLens.NONCE_ENTROPY */);
    const ip_msg_hash = g_wasm.malloc(32 /* ByteLens.MSG_HASH */);
    // scratch spaces
    const ip_pubkey_scratch = g_wasm.malloc(32 /* ByteLens.XONLY_PUBKEY */);
    const ip_sig_scratch = g_wasm.malloc(64 /* ByteLens.BIP340_SIG */);
    // library handle: secp256k1_keypair;
    const ip_keypair = g_wasm.malloc(96 /* ByteLens.KEYPAIR_LIB */);
    // library handle: secp256k1_xonly_pubkey;
    const ip_xonly_pubkey = g_wasm.malloc(64 /* ByteLens.XONLY_KEY_LIB */);
    // library handle: secp256k1_sha256;
    const ip_sha256 = g_wasm.malloc(104 /* ByteLens.SHA256_LIB */);
    // create a reusable context
    const ip_ctx = g_wasm.context_create(513 /* Flags.CONTEXT_SIGN */ | 257 /* Flags.CONTEXT_VERIFY */);
    // an encoder for hashing strings
    const utf8 = new TextEncoder();
    /**
     * Puts the given private key into program memory, runs the given callback, then zeroes out the key
     * @param atu8_sk - the private key
     * @param f_use - callback to use the key
     * @returns whatever the callback returns
     */
    const with_keypair = (atu8_sk, f_use) => {
        // prep callback return
        let w_return;
        // in case of any exception..
        try {
            // copy input bytes into place
            ATU8_HEAP.set(atu8_sk, ip_sk);
            // instantiate keypair
            g_wasm.keypair_create(ip_ctx, ip_keypair, ip_sk);
            // use private key
            w_return = f_use();
        }
        finally {
            // zero-out private key and keypair
            ATU8_HEAP.fill(1, ip_sk, ip_sk + 32 /* ByteLens.PRIVATE_KEY */);
            ATU8_HEAP.fill(2, ip_keypair, ip_keypair + 96 /* ByteLens.KEYPAIR_LIB */);
        }
        // forward result
        return w_return;
    };
    return {
        gen_secret_key: () => crypto.getRandomValues(new Uint8Array(32 /* ByteLens.PRIVATE_KEY */)),
        get_public_key(atu8_sk) {
            // while using the private key, compute its corresponding public key; from the docs:
            if (1 /* BinaryResult.SUCCESS */ !==
                with_keypair(atu8_sk, () => g_wasm.keypair_xonly_pub(ip_ctx, ip_xonly_pubkey, null, ip_keypair))) {
                throw Error('sk_to_pk: ' + S_REASON_INVALID_SK);
            }
            // serialize the public key
            g_wasm.xonly_pubkey_serialize(ip_ctx, ip_pubkey_scratch, ip_xonly_pubkey);
            // extract result
            return ATU8_HEAP.slice(ip_pubkey_scratch, ip_pubkey_scratch + 32 /* ByteLens.XONLY_PUBKEY */);
        },
        sign(atu8_sk, atu8_hash, atu8_ent) {
            // copy message hash bytes into place
            ATU8_HEAP.set(atu8_hash, ip_msg_hash);
            // copy entropy bytes into place
            if (!atu8_ent && crypto.getRandomValues) {
                ATU8_HEAP.set(crypto.getRandomValues(new Uint8Array(32)), ip_ent);
            }
            // while using the private key, sign the given message hash
            if (1 /* BinaryResult.SUCCESS */ !==
                with_keypair(atu8_sk, () => g_wasm.schnorrsig_sign32(ip_ctx, ip_sig_scratch, ip_msg_hash, ip_keypair, ip_ent))) {
                throw Error('BIP-340 sign: ' + S_REASON_INVALID_SK);
            }
            // return serialized signature
            return ATU8_HEAP.slice(ip_sig_scratch, ip_sig_scratch + 64 /* ByteLens.BIP340_SIG */);
        },
        verify(atu8_signature, atu8_hash, atu8_pk) {
            // copy signature bytes into place
            ATU8_HEAP.set(atu8_signature, ip_sig_scratch);
            // copy message hash bytes into place
            ATU8_HEAP.set(atu8_hash, ip_msg_hash);
            // copy pubkey bytes into place
            ATU8_HEAP.set(atu8_pk, ip_pubkey_scratch);
            // parse the public key
            if (1 /* BinaryResult.SUCCESS */ !==
                g_wasm.xonly_pubkey_parse(ip_ctx, ip_xonly_pubkey, ip_pubkey_scratch)) {
                throw Error(S_TAG_BIP340_VERIFY + S_REASON_INVALID_PK);
            }
            // verify the signature
            return (1 /* BinaryResult.SUCCESS */ ===
                g_wasm.schnorrsig_verify(ip_ctx, ip_sig_scratch, ip_msg_hash, 32 /* ByteLens.MSG_HASH */, ip_xonly_pubkey));
        },
        sha256(message) {
            const ip_message = g_wasm.malloc(message.length);
            const data = utf8.encode(message);
            ATU8_HEAP.set(data, ip_message);
            g_wasm.sha256_initialize(ip_sha256);
            g_wasm.sha256_write(ip_sha256, ip_message, message.length);
            g_wasm.sha256_finalize(ip_sha256, ip_msg_hash);
            return ATU8_HEAP.slice(ip_msg_hash, ip_msg_hash + 32 /* ByteLens.MSG_HASH */);
        }
    };
};

export { WasmSecp256k1 as W };
