"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const byzcoin_1 = require("../../byzcoin");
/**
 * TransactionBuilder handles collecting multiple instructions and signing them all
 * together before sending the transaction to the chain.
 * There are convenience methods to create spawn, invoke, or delete instructions.
 * Once all instructions are added, the send method will contact one or more nodes
 * to submit the transaction.
 * After a call to the send method, the transaction is ready for new instructions.
 *
 * If any of the instructions in this transaction fails, all other instructions will
 * fail, too.
 */
class TransactionBuilder {
    constructor(bc) {
        this.bc = bc;
        this.instructions = [];
    }
    /**
     * Signs all instructions and sends them to the nodes.
     * The `instructions` field is only emptied if the transaction has been accepted successfully.
     * If the transaction fails, it can be retried.
     *
     * @param signers one set of signers per instruction. If there is only one set for multiple
     * instructions, always the same set of signers will be used.
     * @param wait if 0, doesn't wait for inclusion. If > 0, waits for inclusion for this many blockIntervals.
     */
    send(signers, wait = 0) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const ctx = byzcoin_1.ClientTransaction.make(this.bc.getProtocolVersion(), ...this.instructions);
            yield ctx.updateCountersAndSign(this.bc, signers);
            const response = yield this.bc.sendTransactionAndWait(ctx, wait);
            this.instructions = [];
            return [ctx, response];
        });
    }
    /**
     * @return true if one or more instructions are available.
     */
    hasInstructions() {
        return this.instructions.length > 0;
    }
    /**
     * Appends a new instruction.
     *
     * @param inst the new instruction to append
     * @return the appended instruction
     */
    append(inst) {
        this.instructions.push(inst);
        return inst;
    }
    /**
     * Prepends a new instruction
     *
     * @param inst the instruction to prepend
     * @return the prepended instruction
     */
    prepend(inst) {
        this.instructions.unshift(inst);
        return inst;
    }
    /**
     * Appends a spawn instruction.
     *
     * @param iid the instance ID where the instruction is sent to
     * @param contractID the contractID to spawn
     * @param args arguments of the contract
     * @return new instruction - can be used for deriveID
     */
    spawn(iid, contractID, args) {
        return this.append(byzcoin_1.Instruction.createSpawn(iid, contractID, args));
    }
    /**
     * Appends an invoke instruction.
     *
     * @param iid the instance ID where the instruction is sent to
     * @param contractID the contractID to invoke
     * @param command to be invoked on the contract
     * @param args arguments of the command
     * @return new instruction - can be used for deriveID
     */
    invoke(iid, contractID, command, args) {
        return this.append(byzcoin_1.Instruction.createInvoke(iid, contractID, command, args));
    }
    /**
     * Appends a delete instruction.
     *
     * @param iid the instance ID where the instruction is sent to
     * @param contractID to be deleted - must match the actual contract
     * @return new instruction - can be used for deriveID
     */
    delete(iid, contractID) {
        return this.append(byzcoin_1.Instruction.createDelete(iid, contractID));
    }
    /**
     * Returns the ID that will be produced by the given instruction
     *
     * @param index of the instruction
     * @param name if given, will be passed to deriveID
     */
    deriveID(index, name = "") {
        if (index < 0 || index > this.instructions.length) {
            throw new Error("instruction out of bound");
        }
        return this.instructions[index].deriveId(name);
    }
    /**
     * returns a useful and readable representation of all instructions in this transaction.
     */
    toString() {
        return this.instructions.map((inst, i) => {
            const t = ["Spawn", "Invoke", "Delete"][inst.type];
            let cid;
            let args;
            switch (inst.type) {
                case byzcoin_1.Instruction.typeSpawn:
                    cid = inst.spawn.contractID;
                    args = inst.spawn.args;
                    break;
                case byzcoin_1.Instruction.typeInvoke:
                    cid = `${inst.invoke.contractID} / ${inst.invoke.command}`;
                    args = inst.invoke.args;
                    break;
                case byzcoin_1.Instruction.typeDelete:
                    cid = inst.delete.contractID;
                    args = [];
                    break;
            }
            return `${i}:  ${t} ${cid}: ${inst.instanceID.toString("hex")}\n\t` +
                args.map((kv) => `${kv.name}: ${kv.value.toString("hex")}`).join("\n\t");
        }).join("\n\n");
    }
}
exports.TransactionBuilder = TransactionBuilder;
