"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const avatarService_1 = require("./avatarService");
const loggingService_1 = require("./loggingService");
const transactionService_1 = require("./transactionService");
const utils_1 = require("./utils");
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 eventTopic = "txReceipts.DAO.new";
            const txReceiptEventPayload = transactionService_1.TransactionService.publishKickoffEvent(eventTopic, options, daoCreator.forgeOrgTransactionsCount(options) + daoCreator.setSchemesTransactionsCount(options));
            /**
             * subscribe to all "txReceipts.DaoCreator" and republish as eventTopic with txReceiptEventPayload
             */
            const eventSubscription = transactionService_1.TransactionService.resendTxEvents("txReceipts.DaoCreator", eventTopic, txReceiptEventPayload);
            let avatarAddress;
            try {
                const result = yield daoCreator.forgeOrg(options);
                avatarAddress = result.getValueFromTx("_avatar", "NewOrg");
                if (!avatarAddress) {
                    throw new Error("avatar address is not defined");
                }
                yield daoCreator.setSchemes(Object.assign({ avatar: avatarAddress }, options));
            }
            finally {
                eventSubscription.unsubscribe();
            }
            return DAO.at(avatarAddress);
        });
    }
    /**
     * Returns the promise of a DAO at the given address.  Throws an exception if not found.
     * @param avatarAddress The DAO's address
     */
    static at(avatarAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const dao = new DAO();
            const avatarService = new avatarService_1.AvatarService(avatarAddress);
            dao.avatar = yield avatarService.getAvatar();
            dao.controller = yield avatarService.getController();
            dao.hasUController = avatarService.isUController;
            dao.token = yield avatarService.getNativeToken();
            dao.reputation = yield avatarService.getNativeReputation();
            return dao;
        });
    }
    /**
     * Returns the promise of the DAOstack Genesis avatar address, or undefined if not found
     */
    static getGenesisDao(daoCreatorAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    const daoCreator = daoCreatorAddress ?
                        yield wrapperService_1.WrapperService.factories.DaoCreator.at(daoCreatorAddress) : wrapperService_1.WrapperService.wrappers.DaoCreator;
                    let avatarAddress;
                    const event = daoCreator.InitialSchemesSet({}, { fromBlock: 0 });
                    /**
                     * this first DAO returned will be DAOstack
                     */
                    event.get((err, log) => {
                        if (err) {
                            loggingService_1.LoggingService.error(`getGenesisDao: Error obtaining Genesis Dao: ${err}`);
                            reject(err);
                        }
                        avatarAddress = log[0].args._avatar;
                        resolve(avatarAddress);
                    });
                }
                catch (ex) {
                    reject(ex);
                }
            }));
        });
    }
    /**
     * 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* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    const daos = new Array();
                    const daoCreator = options.daoCreatorAddress ?
                        yield wrapperService_1.WrapperService.factories.DaoCreator
                            .at(options.daoCreatorAddress) : wrapperService_1.WrapperService.wrappers.DaoCreator;
                    const initSchemesEvent = daoCreator.InitialSchemesSet({}, { fromBlock: 0 });
                    initSchemesEvent.get((err, log) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                        if (err) {
                            loggingService_1.LoggingService.error(`getDaos: Error obtaining DAOs: ${err}`);
                            reject(err);
                        }
                        for (const event of log) {
                            const avatarAddress = event.args._avatar;
                            loggingService_1.LoggingService.debug(`getDaos: loaded dao: ${avatarAddress}`);
                            daos.push(avatarAddress);
                            if (options.perDaoCallback) {
                                const promiseOfStopSign = options.perDaoCallback(avatarAddress);
                                if (promiseOfStopSign) {
                                    const stop = yield promiseOfStopSign;
                                    if (stop) {
                                        break;
                                    }
                                }
                            }
                        }
                        loggingService_1.LoggingService.debug("Finished loading daos");
                        resolve(daos);
                    }));
                }
                catch (ex) {
                    loggingService_1.LoggingService.error(`getDaos: Error obtaining DAOs: ${ex}`);
                    reject(ex);
                }
            }));
        });
    }
    /**
     * 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) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const schemes = yield this._getSchemes();
            if (name) {
                return schemes.filter((s) => 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 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 the promise of the name of the DAO as stored in the Avatar
     * @return {Promise<string>}
     */
    getName() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const web3 = yield utils_1.Utils.getWeb3();
            return web3.toUtf8(yield this.avatar.orgName());
        });
    }
    /**
     * 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.name();
        });
    }
    /**
     * 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.symbol();
        });
    }
    /**
     * Returns promise of schemes currently in this DAO as Array<DaoSchemeInfo>
     */
    _getSchemes() {
        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({}, { fromBlock: 0, toBlock: "latest" });
            yield new Promise((resolve) => {
                registerSchemeEvent.get((err, log) => this._handleSchemeEvent(log, foundSchemes)
                    .then(() => {
                    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) {
        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._scheme;
                const wrapper = wrapperService_1.WrapperService.wrappersByAddress.get(address);
                const schemeInfo = {
                    address,
                    // 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) => {
                event.get((err, log) => 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