"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 avatarService_1 = require("./avatarService");
const utils_1 = require("./utils");
const daocreator_1 = require("./wrappers/daocreator");
const wrapperService_js_1 = require("./wrapperService.js");
/**
 * 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 __awaiter(this, void 0, void 0, function* () {
            let daoCreator;
            if (options.daoCreatorAddress) {
                daoCreator = yield daocreator_1.DaoCreatorFactory.at(options.daoCreatorAddress);
            }
            else {
                daoCreator = wrapperService_js_1.WrapperService.wrappers.DaoCreator;
            }
            const result = yield daoCreator.forgeOrg(options);
            const avatarAddress = result.getValueFromTx("_avatar", "NewOrg");
            yield daoCreator.setSchemes(Object.assign({ avatar: avatarAddress }, options));
            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 __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 __awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
                try {
                    const daoCreator = daoCreatorAddress ?
                        yield wrapperService_js_1.WrapperService.factories.DaoCreator.at(daoCreatorAddress) : wrapperService_js_1.WrapperService.wrappers.DaoCreator;
                    let avatarAddress;
                    const event = daoCreator.InitialSchemesSet({}, { fromBlock: 0 });
                    /**
                     * this first DAO returned will be DAOstack
                     */
                    event.get((err, log) => {
                        avatarAddress = log[0].args._avatar;
                        resolve(avatarAddress);
                    });
                }
                catch (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 __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 __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 __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 __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 __awaiter(this, void 0, void 0, function* () {
            return utils_1.Utils.getWeb3().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 __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 __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 __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 __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_js_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 __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 __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_js_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