"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const es6_promisify_1 = require("es6-promisify");
const commonTypes_1 = require("../commonTypes");
const configService_1 = require("../configService");
const contractWrapperBase_1 = require("../contractWrapperBase");
const contractWrapperFactory_1 = require("../contractWrapperFactory");
const transactionService_1 = require("../transactionService");
const utils_1 = require("../utils");
class VestingSchemeWrapper extends contractWrapperBase_1.ContractWrapperBase {
    constructor() {
        super(...arguments);
        this.name = "VestingScheme";
        this.friendlyName = "Vesting Scheme";
        this.factory = exports.VestingSchemeFactory;
        /**
         * Events
         */
        /* tslint:disable:max-line-length */
        this.ProposalExecuted = this.createEventFetcherFactory("ProposalExecuted");
        this.AgreementProposal = this.createEventFetcherFactory("AgreementProposal");
        this.NewVestedAgreement = this.createEventFetcherFactory("NewVestedAgreement");
        this.SignToCancelAgreement = this.createEventFetcherFactory("SignToCancelAgreement");
        this.RevokeSignToCancelAgreement = this.createEventFetcherFactory("RevokeSignToCancelAgreement");
        this.AgreementCancel = this.createEventFetcherFactory("AgreementCancel");
        this.Collect = this.createEventFetcherFactory("Collect");
        /* tslint:enable:max-line-length */
        /**
         * see CreateVestingAgreementConfig
         */
        this.defaultCreateOptions = {
            amountPerPeriod: undefined,
            beneficiaryAddress: undefined,
            cliffInPeriods: undefined,
            numOfAgreedPeriods: undefined,
            periodLength: undefined,
            returnOnCancelAddress: undefined,
            signaturesReqToCancel: undefined,
            signers: undefined,
            startingBlock: null,
        };
    }
    /**
     * Propose a new vesting agreement
     * @param {ProposeVestingAgreementConfig} options
     */
    propose(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            /**
             * see ProposeVestingAgreementConfig
             */
            options = Object.assign({}, this.defaultCreateOptions, options);
            if (!options.avatar) {
                throw new Error("avatar is not defined");
            }
            yield this.validateCreateParams(options);
            this.logContractFunctionCall("VestingScheme.proposeVestingAgreement", options);
            const web3 = yield utils_1.Utils.getWeb3();
            const txResult = yield this.wrapTransactionInvocation("VestingScheme.propose", options, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
                return this.contract.proposeVestingAgreement(options.beneficiaryAddress, options.returnOnCancelAddress, options.startingBlock, web3.toBigNumber(options.amountPerPeriod), options.periodLength, options.numOfAgreedPeriods, options.cliffInPeriods, options.signaturesReqToCancel, options.signers, options.avatar);
            }));
            return new contractWrapperBase_1.ArcTransactionProposalResult(txResult.tx);
        });
    }
    /**
     * Create a new vesting agreement
     * @param {CreateVestingAgreementConfig} options
     */
    create(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            /**
             * See these properties in CreateVestingAgreementConfig
             */
            options = Object.assign({}, this.defaultCreateOptions, options);
            yield this.validateCreateParams(options);
            if (!options.token) {
                throw new Error("token is not defined");
            }
            const web3 = yield utils_1.Utils.getWeb3();
            const amountPerPeriod = web3.toBigNumber(options.amountPerPeriod);
            const autoApproveTransfer = configService_1.ConfigService.get("autoApproveTokenTransfers");
            const eventTopic = "txReceipts.VestingScheme.create";
            const txReceiptEventPayload = transactionService_1.TransactionService.publishKickoffEvent(eventTopic, options, 1 + (autoApproveTransfer ? 1 : 0));
            let tx;
            /**
             * approve immediate transfer of the given tokens from currentAccount to the VestingScheme
             */
            if (autoApproveTransfer) {
                const token = yield (yield utils_1.Utils.requireContract("StandardToken")).at(options.token);
                tx = yield token.approve(this.address, amountPerPeriod.mul(options.numOfAgreedPeriods));
                transactionService_1.TransactionService.publishTxEvent(eventTopic, txReceiptEventPayload, tx);
            }
            this.logContractFunctionCall("VestingScheme.createVestedAgreement", options);
            tx = yield this.contract.createVestedAgreement(options.token, options.beneficiaryAddress, options.returnOnCancelAddress, options.startingBlock, amountPerPeriod, options.periodLength, options.numOfAgreedPeriods, options.cliffInPeriods, options.signaturesReqToCancel, options.signers);
            transactionService_1.TransactionService.publishTxEvent(eventTopic, txReceiptEventPayload, tx);
            return new ArcTransactionAgreementResult(tx);
        });
    }
    /**
     * Sign to cancel a vesting agreement
     * @param {SignToCancelVestingAgreementConfig} options
     */
    signToCancel(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (options.agreementId === null) {
                throw new Error("agreementId is not defined");
            }
            this.logContractFunctionCall("VestingScheme.signToCancelAgreement", options);
            return this.wrapTransactionInvocation("VestingScheme.signToCancel", options, () => {
                return this.contract.signToCancelAgreement(options.agreementId);
            });
        });
    }
    /**
     * Revoke vote for cancelling a vesting agreement
     * @param {RevokeSignToCancelVestingAgreementConfig} options
     */
    revokeSignToCancel(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (options.agreementId === null) {
                throw new Error("agreementId is not defined");
            }
            this.logContractFunctionCall("VestingScheme.revokeSignToCancelAgreement", options);
            return this.wrapTransactionInvocation("VestingScheme.revokeSignToCancel", options, () => {
                return this.contract.revokeSignToCancelAgreement(options.agreementId);
            });
        });
    }
    /**
     * Collects for a beneficiary, according to the agreement
     * @param {CollectVestingAgreementConfig} options
     */
    collect(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (options.agreementId === null) {
                throw new Error("agreementId is not defined");
            }
            this.logContractFunctionCall("VestingScheme.collect", options);
            return this.wrapTransactionInvocation("VestingScheme.collect", options, () => {
                return this.contract.collect(options.agreementId);
            });
        });
    }
    /**
     * Return all agreements ever created by this scheme
     * Filter by the optional agreementId.
     */
    getAgreements(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const defaults = {
                agreementId: null,
            };
            options = Object.assign({}, defaults, options);
            if (!options.avatar) {
                throw new Error("avatar address is not defined");
            }
            const agreements = new Array();
            if (options.agreementId) {
                const agreement = this.schemeAgreementToAgreement(yield this.contract.agreements(options.agreementId), options.agreementId);
                agreements.push(agreement);
            }
            else {
                const eventFetcher = this.NewVestedAgreement({}, { fromBlock: 0 });
                yield new Promise((resolve) => {
                    eventFetcher.get((err, log) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                        for (const event of log) {
                            const agreementId = event.args._agreementId.toNumber();
                            const agreement = this.schemeAgreementToAgreement(yield this.contract.agreements(agreementId), agreementId);
                            agreements.push(agreement);
                        }
                        resolve();
                    }));
                });
            }
            return agreements;
        });
    }
    setParameters(params) {
        const _super = name => super[name];
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.validateStandardSchemeParams(params);
            return _super("_setParameters").call(this, "VestingScheme.setParameters", params.voteParametersHash, params.votingMachineAddress);
        });
    }
    getDefaultPermissions() {
        return commonTypes_1.DefaultSchemePermissions.VestingScheme;
    }
    getSchemePermissions(avatarAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return this._getSchemePermissions(avatarAddress);
        });
    }
    getSchemeParameters(avatarAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return this._getSchemeParameters(avatarAddress);
        });
    }
    getParameters(paramsHash) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const params = yield this.getParametersArray(paramsHash);
            return {
                voteParametersHash: params[0],
                votingMachineAddress: params[1],
            };
        });
    }
    validateCreateParams(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!Number.isInteger(options.periodLength) || (options.periodLength <= 0)) {
                throw new Error("periodLength must be an integer greater than zero");
            }
            const web3 = yield utils_1.Utils.getWeb3();
            if (yield web3.toBigNumber(options.amountPerPeriod).lte(0)) {
                throw new Error("amountPerPeriod must be greater than zero");
            }
            if (!Number.isInteger(options.numOfAgreedPeriods) || (options.numOfAgreedPeriods <= 0)) {
                throw new Error("numOfAgreedPeriods must be greater than zero");
            }
            if (!Number.isInteger(options.cliffInPeriods) || (options.cliffInPeriods < 0)) {
                throw new Error("cliffInPeriods must be greater than or equal to zero");
            }
            if ((typeof options.startingBlock === "undefined") || (options.startingBlock === null)) {
                options.startingBlock = yield es6_promisify_1.promisify(web3.eth.getBlockNumber)().then((bn) => bn);
            }
            if (!Number.isInteger(options.startingBlock) || (options.startingBlock < 0)) {
                throw new Error("startingBlock must be greater than or equal to zero");
            }
        });
    }
    schemeAgreementToAgreement(schemeAgreement, agreementId) {
        return {
            agreementId,
            amountPerPeriod: schemeAgreement[4],
            beneficiaryAddress: schemeAgreement[1],
            cliffInPeriods: schemeAgreement[7],
            collectedPeriods: schemeAgreement[9],
            numOfAgreedPeriods: schemeAgreement[6],
            periodLength: schemeAgreement[5],
            returnOnCancelAddress: schemeAgreement[2],
            signaturesReqToCancel: schemeAgreement[8],
            startingBlock: schemeAgreement[3],
            tokenAddress: schemeAgreement[0],
        };
    }
}
exports.VestingSchemeWrapper = VestingSchemeWrapper;
class ArcTransactionAgreementResult extends contractWrapperBase_1.ArcTransactionResult {
    constructor(tx) {
        super(tx);
        this.agreementId = this.getValueFromTx("_agreementId").toNumber();
    }
}
exports.ArcTransactionAgreementResult = ArcTransactionAgreementResult;
exports.VestingSchemeFactory = new contractWrapperFactory_1.ContractWrapperFactory("VestingScheme", VestingSchemeWrapper);
//# sourceMappingURL=vestingScheme.js.map