"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const avatarService_1 = require("./avatarService");
const transactionService_1 = require("./transactionService");
const utils_1 = require("./utils");
const web3EventService_1 = require("./web3EventService");
const daoCreator_1 = require("./wrappers/daoCreator");
const wrapperService_1 = require("./wrapperService");
/**
 * Helper class and factory for DAOs.
 */
class DAO {
    /**
     * Returns the promise of a new DAO
     * @param {NewDaoConfig} options Configuration of the new DAO
     */
    static new(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let daoCreator;
            if (options.daoCreatorAddress) {
                daoCreator = yield daoCreator_1.DaoCreatorFactory.at(options.daoCreatorAddress);
            }
            else {
                daoCreator = wrapperService_1.WrapperService.wrappers.DaoCreator;
            }
            const payload = transactionService_1.TransactionService.publishKickoffEvent("DAO.new", options, daoCreator.forgeOrgTransactionsCount(options) + daoCreator.setSchemesTransactionsCount(options));
            /**
             * resend sub-events as Dao.new
             */
            const eventContext = transactionService_1.TransactionService.newTxEventContext("DAO.new", payload, options);
            options.txEventContext = eventContext;
            const result = yield (yield daoCreator.forgeOrg(options)).watchForTxMined();
            const avatarAddress = transactionService_1.TransactionService.getValueFromLogs(result, "_avatar", "NewOrg");
            if (!avatarAddress) {
                throw new Error("avatar address is not defined");
            }
            // In case forgeOrg ever may decide to alter options.txEventContext, reset it here
            options.txEventContext = eventContext;
            yield (yield daoCreator.setSchemes(Object.assign({ avatar: avatarAddress }, options))).watchForTxMined();
            return DAO.at(avatarAddress);
        });
    }
    /**
     * Returns the promise of a DAO at the given address.  Returns undefined if not found.
     * @param avatarAddress The DAO avatar's address
     */
    static at(avatarAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let dao;
            const avatarService = new avatarService_1.AvatarService(avatarAddress);
            const avatar = yield avatarService.getAvatar();
            if (avatar) {
                dao = new DAO();
                dao.avatar = yield avatarService.getAvatar();
                dao.controller = yield avatarService.getController();
                dao.hasUController = yield avatarService.getIsUController();
                dao.token = yield avatarService.getNativeToken();
                dao.reputation = yield avatarService.getNativeReputation();
                dao.name = yield dao.avatar.orgName();
                return dao;
            }
        });
    }
    /**
     * Return a promise of an array of avatar addresses for all of the DAOs created by the optionally-given
     * DaoCreator contract.  The default DaoCreator is the one deployed by
     * the running version of Arc.js.
     *
     * An alternative DaoCreator must implement an InitialSchemesSet event just like the
     * Arc DaoCreater.
     * @param options
     */
    static getDaos(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const daoEventFetcherFactory = yield DAO.getDaoCreationEvents(options);
            return daoEventFetcherFactory({}, { fromBlock: 0 }).get();
        });
    }
    /**
     * Return a promise of an EntityFetcherFactory to get/watch avatar addresses
     * for all of the DAOs created by the optionally-given DaoCreator contract.
     * The default DaoCreator is the one deployed by the running version of Arc.js.
     *
     * An alternative DaoCreator must implement an InitialSchemesSet event just like the Arc DaoCreater.
     * @param options Optional, default is `{}`.
     */
    static getDaoCreationEvents(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const web3EventService = new web3EventService_1.Web3EventService();
            const daoCreator = options.daoCreatorAddress ?
                yield wrapperService_1.WrapperService.factories.DaoCreator
                    .at(options.daoCreatorAddress) : wrapperService_1.WrapperService.wrappers.DaoCreator;
            return web3EventService.createEntityFetcherFactory(daoCreator.InitialSchemesSet, (event) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                return Promise.resolve(event.args._avatar);
            }));
        });
    }
    /**
     * Returns the promise of all of the schemes registered into this DAO, as Array<DaoSchemeInfo>
     * @param name Optionally filter by the name of a scheme, like "SchemeRegistrar"
     */
    getSchemes(name, filterParams) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const schemes = yield this._getSchemes(filterParams);
            if (name) {
                return schemes.filter((s) => s.wrapper && s.wrapper.name && (s.wrapper.name === name));
            }
            else {
                return schemes;
            }
        });
    }
    /**
     * Returns the promise of all os the global constraints currently registered into this DAO,
     * as Array<DaoGlobalConstraintInfo>
     * @param name Optionally filter by the name of a global constraint, like "TokenCapGC"
     */
    getGlobalConstraints(name) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // return the global constraints registered on this controller satisfying the contract spec
            // return all global constraints if name is not given
            const constraints = yield this._getConstraints();
            if (name) {
                return constraints.filter((s) => s.wrapper.name && (s.wrapper.name === name));
            }
            else {
                return constraints;
            }
        });
    }
    /**
     * Returns promise of an array of `Participant` representing accounts that currently have
     * greater-than-zero reputation with this DAO.  Optionally returns the current reputation
     * of each participant.
     * @param options
     */
    getParticipants(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const addresses = new Set();
            let participants;
            const fetcher = this.reputation.Mint(options.participantAddress ? { _to: options.participantAddress } : undefined, Object.assign({ fromBlock: 0 }, options));
            const events = yield fetcher.get();
            events.forEach((event) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                addresses.add(event.args._to);
            }));
            if (options.returnReputations) {
                participants = new Array();
                for (const account of addresses) {
                    const balance = yield this.reputation.getBalanceOf(account);
                    if (balance.gt(0)) {
                        participants.push({ address: account, reputation: balance });
                    }
                }
            }
            else {
                participants = Array.from(addresses.values()).map((address) => ({ address }));
            }
            return participants;
        });
    }
    /**
     * Returns whether the scheme with the given address is registered to this DAO's controller
     */
    isSchemeRegistered(schemeAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return yield this.controller.isSchemeRegistered(schemeAddress, this.avatar.address);
        });
    }
    /**
     * Returns whether the global constraint with the given address is registered to this DAO's controller
     */
    isGlobalConstraintRegistered(gc) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return yield this.controller.isGlobalConstraintRegistered(gc, this.avatar.address);
        });
    }
    /**
     * Returns a promise of the given account's native token balance.
     * @param agentAddress
     */
    getTokenBalance(agentAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return utils_1.Utils.getTokenBalance(agentAddress, this.token.address);
        });
    }
    /**
     * Returns the promise of the  token name for the DAO as stored in the native token
     * @return {Promise<string>}
     */
    getTokenName() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return yield this.token.getTokenName();
        });
    }
    /**
     * Returns  the promise of the token symbol for the DAO as stored in the native token
     * @return {Promise<string>}
     */
    getTokenSymbol() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return yield this.token.getTokenSymbol();
        });
    }
    /**
     * Returns promise of schemes currently in this DAO as Array<DaoSchemeInfo>
     */
    _getSchemes(filterParams = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const foundSchemes = new Map();
            const controller = this.controller;
            const avatar = this.avatar;
            const registerSchemeEvent = controller.RegisterScheme(Object.assign({ _avatar: this.avatar.address }, filterParams), // _avatar only matters with Universal controller
            { fromBlock: 0, toBlock: 'latest' });
            yield new Promise((resolve, reject) => {
                registerSchemeEvent.get((err, log) => {
                    if (err) {
                        return reject(err);
                    }
                    this._handleSchemeEvent(log, foundSchemes);
                    return resolve();
                });
            });
            const registeredSchemes = [];
            for (const scheme of foundSchemes.values()) {
                if (yield controller.isSchemeRegistered(scheme.address, avatar.address)) {
                    registeredSchemes.push(scheme);
                }
            }
            return registeredSchemes;
        });
    }
    _handleSchemeEvent(log, schemesMap) {
        if (!Array.isArray(log)) {
            log = [log];
        }
        const count = log.length;
        for (let i = 0; i < count; i++) {
            const address = log[i].args._scheme;
            const wrapper = wrapperService_1.WrapperService.wrappersByAddress.get(address);
            const schemeInfo = {
                address,
                blockNumber: log[i].blockNumber,
                transactionHash: log[i].transactionHash,
                // will be undefined if not an Arc scheme deployed by the running version of Arc.js
                // TODO: this should be aware of previously-deployed schemes
                wrapper,
            };
            // dedup
            schemesMap.set(address, schemeInfo);
        }
    }
    /**
     * Returns promise of global constraints currently in this DAO, as DaoGlobalConstraintInfo
     */
    _getConstraints() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const foundConstraints = new Map(); // <string, DaoGlobalConstraintInfo>
            const controller = this.controller;
            const event = controller.AddGlobalConstraint({}, { fromBlock: 0, toBlock: "latest" });
            yield new Promise((resolve, reject) => {
                event.get((err, log) => {
                    if (err) {
                        return reject(err);
                    }
                    this._handleConstraintEvent(log, foundConstraints).then(() => {
                        resolve();
                    });
                });
            });
            const registeredConstraints = [];
            for (const gc of foundConstraints.values()) {
                if (yield this.isGlobalConstraintRegistered(gc.address)) {
                    registeredConstraints.push(gc);
                }
            }
            return registeredConstraints;
        });
    }
    _handleConstraintEvent(log, constraintsMap) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!Array.isArray(log)) {
                log = [log];
            }
            const count = log.length;
            for (let i = 0; i < count; i++) {
                const address = log[i].args._globalConstraint;
                const paramsHash = log[i].args._params;
                const wrapper = wrapperService_1.WrapperService.wrappersByAddress.get(address);
                const info = {
                    address,
                    paramsHash,
                    // will be undefined if not an Arc GC deployed by the running version of Arc.js
                    // TODO: this should be aware of previously-deployed GCs
                    wrapper,
                };
                // dedup
                constraintsMap.set(address, info);
            }
        });
    }
}
exports.DAO = DAO;
//# sourceMappingURL=dao.js.map