"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const byzcoin_1 = require("../../../byzcoin");
const contracts_1 = require("../../../byzcoin/contracts");
const log_1 = tslib_1.__importDefault(require("../../../log"));
const __1 = require("..");
const _1 = require("./");
/**
 * CoinStruct merges a Coin structure with an instance.
 */
class CoinStruct extends contracts_1.Coin {
    constructor(inst) {
        super(contracts_1.Coin.decode(inst.data));
        this.inst = inst;
    }
}
exports.CoinStruct = CoinStruct;
/**
 * CoinBS represents a coin with the new interface. Instead of relying on a synchronous interface,
 * this implementation allows for a more RxJS-style interface.
 */
class CoinInst extends rxjs_1.BehaviorSubject {
    constructor(coin) {
        super(coin.getValue());
        coin.subscribe(this);
        this.id = coin.getValue().inst.id;
    }
    /**
     * Retrieves a coinInstance from ByzCoin and returns a BehaviorSubject that updates whenever the
     * coin changes.
     *
     * @param bc of an initialized ByzCoinRPC instance
     * @param coinID of an existing coin instance
     * @return a BehaviorSubject pointing to a coinInstance that updates automatically
     */
    static retrieve(bc, coinID) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            log_1.default.lvl3("getting coinBS");
            const coinObs = (yield bc.instanceObservable(coinID)).pipe(operators_1.map((proof) => new CoinStruct(byzcoin_1.Instance.fromProof(coinID, proof))));
            return new CoinInst(yield __1.ObservableToBS(coinObs));
        });
    }
    /**
     * Creates an instruction to transfer coins to another account.
     *
     * @param tx used to collect one or more instructions that will be bundled together and sent as one transaction
     * to byzcoin.
     * @param dest the destination account to store the coins in. The destination must exist!
     * @param amount how many coins to transfer.
     */
    transfer(tx, dest, amount) {
        _1.CoinContract.transfer(tx, this.getValue().inst.id, dest, amount);
    }
    /**
     * Mints coins on ByzCoin. For this to work, the DARC governing this coin instance needs to have a
     * 'invoke.Coin.mint' rule and the instruction will need to be signed by the appropriate identity.
     *
     * @param tx used to collect one or more instructions
     * @param amount positive, non-zero value to mint on this coin
     * @return the coin as it will be created if the transaction is accepted - warning: other instructions in this
     * transaction might change the value of the coin.
     */
    mint(tx, amount) {
        const ci = this.getValue();
        _1.CoinContract.mint(tx, ci.inst.id, amount);
        return new contracts_1.Coin({ name: ci.name, value: ci.value.add(amount) });
    }
    /**
     * Fetches coins from a coinInstance and puts it on the ByzCoin 'stack' for use by the next instruction.
     * Unused coins are discarded by all nodes and thus lost.
     *
     * @param tx used to collect one or more instructions that will be bundled together and sent as one transaction
     * to byzcoin.
     * @param amount how many coins to put on the stack
     */
    fetch(tx, amount) {
        tx.invoke(this.getValue().inst.id, _1.CoinContract.contractID, _1.CoinContract.commandFetch, [new byzcoin_1.Argument({ name: _1.CoinContract.argumentCoins, value: Buffer.from(amount.toBytesLE()) })]);
    }
    /**
     * Stores coins from the ByzCoin 'stack' in the given coin-account. Only the coins of the stack with the same
     * name are added to the destination account.
     *
     * @param tx used to collect one or more instructions that will be bundled together and sent as one transaction
     * to byzcoin.
     */
    store(tx) {
        tx.invoke(this.getValue().inst.id, _1.CoinContract.contractID, _1.CoinContract.commandStore, []);
    }
}
exports.CoinInst = CoinInst;
