"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const gasLimits_js_1 = require("../../gasLimits.js");
const avatarService_1 = require("../avatarService");
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");
const wrapperService_1 = require("../wrapperService");
class DaoCreatorWrapper extends contractWrapperBase_1.ContractWrapperBase {
    constructor() {
        super(...arguments);
        this.name = "DaoCreator";
        this.friendlyName = "Dao Creator";
        this.factory = exports.DaoCreatorFactory;
        /**
         * Events
         */
        /* tslint:disable:max-line-length */
        this.NewOrg = this.createEventFetcherFactory("NewOrg");
        this.InitialSchemesSet = this.createEventFetcherFactory("InitialSchemesSet");
    }
    /* tslint:enable:max-line-length */
    /**
     * Create a new DAO
     * @param {ForgeOrgConfig} options
     */
    forgeOrg(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const web3 = yield utils_1.Utils.getWeb3();
            /**
             * See these properties in ForgeOrgConfig
             */
            const defaults = {
                founders: [],
                tokenCap: web3.toBigNumber(0),
                universalController: true,
            };
            options = Object.assign({}, defaults, options);
            if (!options.name) {
                throw new Error("DAO name is not defined");
            }
            if (!options.tokenName) {
                throw new Error("DAO token name is not defined");
            }
            if (!options.tokenSymbol) {
                throw new Error("DAO token symbol is not defined");
            }
            let controllerAddress;
            if (options.universalController) {
                const contract = yield utils_1.Utils.requireContract("UController");
                controllerAddress = (yield contract.deployed()).address;
            }
            else {
                controllerAddress = 0;
            }
            const totalGas = gasLimits_js_1.computeGasLimit(options.founders.length);
            this.logContractFunctionCall("DaoCreator.forgeOrg", options);
            return this.wrapTransactionInvocation("DaoCreator.forgeOrg", options, () => {
                return this.contract.forgeOrg(options.name, options.tokenName, options.tokenSymbol, options.founders.map((founder) => web3.toBigNumber(founder.address)), options.founders.map((founder) => web3.toBigNumber(founder.tokens)), options.founders.map((founder) => web3.toBigNumber(founder.reputation)), controllerAddress, options.tokenCap, { gas: totalGas });
            });
        });
    }
    /**
     * Register schemes with newly-created DAO.
     * Can only be invoked by the agent that created the DAO
     * via forgeOrg, and at that, can only be called one time.
     * @param {SetSchemesConfig} options
     */
    setSchemes(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            /**
             * See SetSchemesConfig
             */
            const defaults = {
                /**
                 * avatar address
                 */
                schemes: [],
                votingMachineParams: {},
            };
            options = Object.assign({}, defaults, options);
            if (!options.avatar) {
                throw new Error("avatar address is not defined");
            }
            const avatarService = new avatarService_1.AvatarService(options.avatar);
            const reputationAddress = yield avatarService.getNativeReputationAddress();
            const configuredVotingMachineName = configService_1.ConfigService.get("defaultVotingMachine");
            const defaultVotingMachineParams = Object.assign({
                // voting machines can't supply reputation as a default -- they don't know what it is
                reputation: reputationAddress,
                votingMachineName: configuredVotingMachineName,
            }, options.votingMachineParams || {});
            const defaultVotingMachine = yield wrapperService_1.WrapperService.getContractWrapper(defaultVotingMachineParams.votingMachineName, defaultVotingMachineParams.votingMachineAddress);
            // in case it wasn't supplied in order to get the default
            defaultVotingMachineParams.votingMachineAddress = defaultVotingMachine.address;
            const eventTopic = "txReceipts.DaoCreator.setSchemes";
            const txReceiptEventPayload = transactionService_1.TransactionService.publishKickoffEvent(eventTopic, options, this.setSchemesTransactionsCount(options));
            /**
             * subscribe to all votingMachine setParameter and scheme "txReceipts" and
             * republish as eventTopic with txReceiptEventPayload
             */
            const eventsSubscription = transactionService_1.TransactionService.resendTxEvents(["txReceipts.ContractWrapperBase.setParameters"], eventTopic, txReceiptEventPayload);
            let tx;
            try {
                /**
                 * each voting machine applies its own default values in setParameters
                 */
                let txResult = yield defaultVotingMachine.setParameters(defaultVotingMachineParams);
                const defaultVoteParametersHash = txResult.result;
                const initialSchemesSchemes = [];
                const initialSchemesParams = [];
                const initialSchemesPermissions = [];
                for (const schemeOptions of options.schemes) {
                    if (!schemeOptions.name) {
                        throw new Error("options.schemes[n].name is not defined");
                    }
                    const wrapperFactory = wrapperService_1.WrapperService.factories[schemeOptions.name];
                    if (!wrapperFactory) {
                        /* tslint:disable-next-line:max-line-length */
                        throw new Error("Non-arc schemes are not currently supported here.  You can add them later in your workflow.");
                    }
                    /**
                     * scheme will be a contract wrapper
                     */
                    const scheme = schemeOptions.address ?
                        yield wrapperFactory.at(schemeOptions.address) : wrapperService_1.WrapperService.wrappers[schemeOptions.name];
                    let schemeVotingMachineParams = schemeOptions.votingMachineParams;
                    let schemeVoteParametersHash;
                    let schemeVotingMachine;
                    if (schemeVotingMachineParams) {
                        const schemeVotingMachineName = schemeVotingMachineParams.votingMachineName;
                        const schemeVotingMachineAddress = schemeVotingMachineParams.votingMachineAddress;
                        /**
                         * get the voting machine contract
                         */
                        if (!schemeVotingMachineAddress &&
                            (!schemeVotingMachineName ||
                                (schemeVotingMachineName === defaultVotingMachineParams.votingMachineName))) {
                            /**
                             *  scheme is using the default voting machine
                             */
                            schemeVotingMachine = defaultVotingMachine;
                        }
                        else {
                            /**
                             * scheme has its own voting machine. Go get it.
                             */
                            if (!schemeVotingMachineName) {
                                schemeVotingMachineParams.votingMachineName = defaultVotingMachineParams.votingMachineName;
                            }
                            /**
                             * Note we are not supporting non-Arc voting machines here, and it must have a wrapper class.
                             */
                            schemeVotingMachine = yield wrapperService_1.WrapperService.getContractWrapper(schemeVotingMachineParams.votingMachineName, schemeVotingMachineParams.votingMachineAddress);
                            // in case it wasn't supplied in order to get the default
                            schemeVotingMachineParams.votingMachineAddress = schemeVotingMachine.address;
                        }
                        schemeVotingMachineParams = Object.assign(defaultVotingMachineParams, schemeVotingMachineParams);
                        /**
                         * get the voting machine parameters
                         */
                        txResult = yield schemeVotingMachine.setParameters(schemeVotingMachineParams);
                        schemeVoteParametersHash = txResult.result;
                    }
                    else {
                        // using the defaults
                        schemeVotingMachineParams = defaultVotingMachineParams;
                        schemeVoteParametersHash = defaultVoteParametersHash;
                    }
                    /**
                     * This is the set of all possible parameters from which the current scheme
                     * will choose just the ones it requires
                     */
                    txResult = yield scheme.setParameters(Object.assign({
                        voteParametersHash: schemeVoteParametersHash,
                        votingMachineAddress: schemeVotingMachineParams.votingMachineAddress,
                    }, schemeOptions.additionalParams || {}));
                    const schemeParamsHash = txResult.result;
                    initialSchemesSchemes.push(scheme.address);
                    initialSchemesParams.push(schemeParamsHash);
                    /**
                     * Make sure the scheme has at least its required permissions, regardless of what the caller
                     * passes in.
                     */
                    const requiredPermissions = scheme.getDefaultPermissions();
                    const additionalPermissions = schemeOptions.permissions;
                    /* tslint:disable-next-line:no-bitwise */
                    initialSchemesPermissions.push(commonTypes_1.SchemePermissions.toString(requiredPermissions | additionalPermissions));
                }
                this.logContractFunctionCall("DaoCreator.setSchemes", options);
                // register the schemes with the dao
                tx = yield this.contract.setSchemes(options.avatar, initialSchemesSchemes, initialSchemesParams, initialSchemesPermissions);
            }
            finally {
                eventsSubscription.unsubscribe();
                if (tx) {
                    transactionService_1.TransactionService.publishTxEvent(eventTopic, txReceiptEventPayload, tx);
                }
            }
            return new contractWrapperBase_1.ArcTransactionResult(tx);
        });
    }
    forgeOrgTransactionsCount(options) {
        return 1;
    }
    setSchemesTransactionsCount(options) {
        /**
         * one for setSchemes, one for the default votingMachine params,
         * one for each scheme's params, and one for each scheme that is not using the default votingMachine
         */
        const schemes = options.schemes || [];
        const numSchemes = schemes.length;
        const numSchemesWithDefaultParams = schemes.filter((s) => !!s.votingMachineParams).length;
        return 2 + numSchemes + numSchemesWithDefaultParams;
    }
}
exports.DaoCreatorWrapper = DaoCreatorWrapper;
/**
 * defined just to add good type checking
 */
class DaoCreatorFactoryType extends contractWrapperFactory_1.ContractWrapperFactory {
    /**
     *
     * @param controllerCreatorAddress The ControllerCreator that Arc will use when migrating
     * a new non-universal controller in `forgeOrg`.
     * Typically is `ControllerCreator` from Arc.
     */
    new(controllerCreatorAddress) {
        const _super = name => super[name];
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!controllerCreatorAddress) {
                controllerCreatorAddress = (yield (yield utils_1.Utils.requireContract("ControllerCreator")).deployed()).address;
            }
            return _super("new").call(this, controllerCreatorAddress);
        });
    }
}
exports.DaoCreatorFactoryType = DaoCreatorFactoryType;
exports.DaoCreatorFactory = new DaoCreatorFactoryType("DaoCreator", DaoCreatorWrapper);
//# sourceMappingURL=daoCreator.js.map