"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const dopts = require("default-options");
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 utils_1 = require("../utils");
const wrapperService_js_1 = require("../wrapperService.js");
class DaoCreatorWrapper extends contractWrapperBase_1.ContractWrapperBase {
    constructor() {
        super(...arguments);
        this.name = "DaoCreator";
        this.frendlyName = "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} opts
     */
    forgeOrg(opts = {}) {
        return __awaiter(this, void 0, void 0, function* () {
            /**
             * See these properties in ForgeOrgConfig
             */
            const defaults = {
                founders: [],
                name: undefined,
                tokenCap: 0,
                tokenName: undefined,
                tokenSymbol: undefined,
                universalController: true,
            };
            const options = dopts(opts, defaults, { allowUnknown: true });
            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");
            }
            const web3 = utils_1.Utils.getWeb3();
            let controllerAddress;
            if (options.universalController) {
                const contract = yield utils_1.Utils.requireContract("UController");
                controllerAddress = (yield contract.deployed()).address;
            }
            else {
                controllerAddress = 0;
            }
            const tx = yield 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, 
            /**
             * We need to increase the gas limit when creating for a non-universal controller,
             * or it will revert.  MetaMask will probably complain that our gas exceeds the block limit,
             * but there is no choice (except the TODO below).
             *
             * But the universal controller requires less gas and requires no change in the gas
             * limit. So to make things easier with MetaMask, we will not set the gas in this case.
             *
             * TODO:  Dynamically compute the gas requirement for both cases.
             */
            controllerAddress ? undefined : { gas: configService_1.ConfigService.get("gasLimit_deployment") });
            return new contractWrapperBase_1.ArcTransactionResult(tx);
        });
    }
    /**
     * 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} opts
     */
    setSchemes(opts = {}) {
        return __awaiter(this, void 0, void 0, function* () {
            /**
             * See SetSchemesConfig
             */
            const defaults = {
                /**
                 * avatar address
                 */
                avatar: undefined,
                schemes: [],
                votingMachineParams: {},
            };
            const options = dopts(opts, defaults, { allowUnknown: true });
            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_js_1.WrapperService.getContractWrapper(defaultVotingMachineParams.votingMachineName, defaultVotingMachineParams.votingMachineAddress);
            // in case it wasn't supplied in order to get the default
            defaultVotingMachineParams.votingMachineAddress = defaultVotingMachine.address;
            /**
             * each voting machine applies its own default values in setParameters
             */
            const defaultVoteParametersHash = (yield defaultVotingMachine.setParameters(defaultVotingMachineParams)).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_js_1.WrapperService.factories[schemeOptions.name];
                if (!wrapperFactory) {
                    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_js_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;
                        }
                        schemeVotingMachine = yield wrapperService_js_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
                     */
                    schemeVoteParametersHash = (yield schemeVotingMachine.setParameters(schemeVotingMachineParams)).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
                 */
                const schemeParamsHash = (yield scheme.setParameters(Object.assign({
                    voteParametersHash: schemeVoteParametersHash,
                    votingMachineAddress: schemeVotingMachineParams.votingMachineAddress,
                }, schemeOptions.additionalParams || {}))).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));
            }
            // register the schemes with the dao
            const tx = yield this.contract.setSchemes(options.avatar, initialSchemesSchemes, initialSchemesParams, initialSchemesPermissions);
            return new contractWrapperBase_1.ArcTransactionResult(tx);
        });
    }
}
exports.DaoCreatorWrapper = DaoCreatorWrapper;
exports.DaoCreatorFactory = new contractWrapperFactory_1.default("DaoCreator", DaoCreatorWrapper);
//# sourceMappingURL=daocreator.js.map