'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const bignumber_js_1 = require("bignumber.js");
const contractWrapperFactory_1 = require("../contractWrapperFactory");
const schemeWrapperBase_1 = require("../schemeWrapperBase");
const utils_1 = require("../utils");
const utilsInternal_1 = require("../utilsInternal");
const web3EventService_1 = require("../web3EventService");
const wrapperService_1 = require("../wrapperService");
const daoToken_1 = require("./daoToken");
class Auction4ReputationWrapper extends schemeWrapperBase_1.SchemeWrapperBase {
    constructor() {
        super(...arguments);
        this.name = 'Auction4Reputation';
        this.friendlyName = 'Auction For Reputation';
        this.factory = exports.Auction4ReputationFactory;
    }
    initialize(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const avatar = yield this.getAvatar();
            if (!utilsInternal_1.UtilsInternal.isNullAddress(avatar)) {
                throw new Error('you can only call initialize once');
            }
            if (!options.avatarAddress) {
                throw new Error('avatarAddress is not defined');
            }
            if (!options.tokenAddress) {
                throw new Error('tokenAddress is not defined');
            }
            if (!options.walletAddress) {
                throw new Error('walletAddress is not defined');
            }
            if (!Number.isInteger(options.auctionPeriod) || (options.auctionPeriod <= 15)) {
                throw new Error('auctionPeriod must be a number > 15');
            }
            if (!Number.isInteger(options.numberOfAuctions)) {
                throw new Error('numberOfAuctions is not an integer');
            }
            if (options.numberOfAuctions <= 0) {
                throw new Error('maxLockingPeriod must be greater than zero');
            }
            if (!options.reputationReward) {
                throw new Error('reputationReward is not defined');
            }
            if (!options.redeemEnableTime) {
                throw new Error('redeemEnableTime is not defined');
            }
            const auctionsEndTime = (options.auctionsStartTime.getTime() / 1000) +
                (options.auctionPeriod * options.numberOfAuctions);
            if ((options.redeemEnableTime.getTime() / 1000) < auctionsEndTime) {
                throw new Error('redeemEnableTime cannot occur before the last auction has completed');
            }
            if (!options.auctionsStartTime) {
                throw new Error('auctionsStartTime is not defined');
            }
            const reputationReward = new bignumber_js_1.default(options.reputationReward);
            if (reputationReward.lte(0)) {
                throw new Error('reputationReward must be greater than zero');
            }
            this.logContractFunctionCall('Auction4Reputation.initialize', options);
            return this.wrapTransactionInvocation('Auction4Reputation.initialize', options, this.contract.initialize, [
                options.avatarAddress,
                options.reputationReward,
                options.auctionsStartTime.getTime() / 1000,
                options.auctionPeriod,
                options.numberOfAuctions,
                options.redeemEnableTime.getTime() / 1000,
                options.tokenAddress,
                options.walletAddress,
            ]);
        });
    }
    redeem(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.beneficiaryAddress) {
                throw new Error('beneficiaryAddress is not defined');
            }
            if (!Number.isInteger(options.auctionId)) {
                throw new Error('auctionId is not an integer');
            }
            if (options.auctionId < 0) {
                throw new Error('auctionId must be greater than or equal to zero');
            }
            const bid = yield this.getBid(options.beneficiaryAddress, options.auctionId);
            if (bid.lte(0)) {
                // because the Arc contract will revert in this case
                return Promise.resolve(null);
            }
            const errMsg = yield this.getRedeemBlocker(options);
            if (errMsg) {
                throw new Error(errMsg);
            }
            this.logContractFunctionCall('Auction4Reputation.redeem', options);
            return this.wrapTransactionInvocation('Auction4Reputation.redeem', options, this.contract.redeem, [options.beneficiaryAddress, options.auctionId]);
        });
    }
    /**
     * Returns reason why can't redeem, or else null if can redeem
     * @param lockerAddress
     */
    getRedeemBlocker(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const redeemEnableTime = yield this.getRedeemEnableTime();
            const now = yield utilsInternal_1.UtilsInternal.lastBlockDate();
            if (now <= redeemEnableTime) {
                throw new Error(`nothing can be redeemed until after ${redeemEnableTime}`);
            }
            const endTime = yield this.getAuctionsEndTime();
            if (now <= endTime) {
                throw new Error('the auction period has not passed');
            }
            return null;
        });
    }
    /**
     * Returns reason why can't bid, else null if can bid
     */
    getBidBlocker(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const amount = new bignumber_js_1.default(options.amount);
            if (amount.isNaN()) {
                return 'amount does not represent a number';
            }
            if (amount.lte(0)) {
                return 'amount to bid must be greater than zero';
            }
            const startTime = yield this.getAuctionsStartTime();
            const endTime = yield this.getAuctionsEndTime();
            const now = yield utilsInternal_1.UtilsInternal.lastBlockDate();
            if ((now > endTime) || (now < startTime)) {
                return 'bidding is not within the allowed bidding period';
            }
            const balance = yield daoToken_1.DaoTokenWrapper.getGenTokenBalance(yield utils_1.Utils.getDefaultAccount());
            if (balance.lt(amount)) {
                return 'the account has insufficient GEN to bid the requested amount';
            }
            return null;
        });
    }
    /**
     * Bid on behalf of the current account
     * @param options
     */
    bid(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const msg = yield this.getBidBlocker(options);
            if (msg) {
                throw new Error(msg);
            }
            this.logContractFunctionCall('Auction4Reputation.bid', options);
            return this.wrapTransactionInvocation('Auction4Reputation.bid', options, this.contract.bid, [options.amount]);
        });
    }
    /**
     * transfer bidded tokens to the wallet
     * @param options
     */
    transferToWallet(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const endTime = yield this.getAuctionsEndTime();
            const now = yield utilsInternal_1.UtilsInternal.lastBlockDate();
            if (now <= endTime) {
                throw new Error('the bidding period has not yet expired');
            }
            this.logContractFunctionCall('Auction4Reputation.transferToWallet', options);
            return this.wrapTransactionInvocation('Auction4Reputation.transferToWallet', options, this.contract.transferToWallet, []);
        });
    }
    /**
     * get promise of the amount the account has bid on the given auction.
     * @param bidderAddress
     * @param auctionId
     */
    getBid(bidderAddress, auctionId) {
        if (!bidderAddress) {
            throw new Error('bidderAddress is not defined');
        }
        if (!Number.isInteger(auctionId)) {
            throw new Error('auctionId is not an integer');
        }
        if (auctionId < 0) {
            throw new Error('auctionId must be greater than or equal to zero');
        }
        this.validateAuctionId(auctionId);
        this.logContractFunctionCall('Auction4Reputation.getBid', { bidderAddress, auctionId });
        return this.contract.getBid(bidderAddress, auctionId);
    }
    getUserEarnedReputation(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.beneficiaryAddress) {
                throw new Error('beneficiaryAddress is not defined');
            }
            if (!Number.isInteger(options.auctionId)) {
                throw new Error('auctionId is not an integer');
            }
            if (options.auctionId < 0) {
                throw new Error('auctionId must be greater than or equal to zero');
            }
            let rep = new bignumber_js_1.default(0);
            const bid = yield this.getBid(options.beneficiaryAddress, options.auctionId);
            // because the Arc contract will revert in this case
            if (!bid.eq(0)) {
                const errMsg = yield this.getRedeemBlocker(options);
                if (errMsg) {
                    throw new Error(errMsg);
                }
                this.logContractFunctionCall('Auction4Reputation.redeem.call', options);
                rep = yield this.contract.redeem.call(options.beneficiaryAddress, options.auctionId);
            }
            else {
                /**
                 * see if it is 0 by result of the reputation being redeemed
                 */
                const events = yield this.Redeem({ _beneficiary: options.beneficiaryAddress, _auctionId: options.auctionId }, { fromBlock: 0 }).get();
                if (events.length) {
                    if (events.length > 1) {
                        throw new Error('unexpectedly received more than one Redeem event for the account');
                    }
                    rep = events[0].args._amount;
                }
            }
            return rep;
        });
    }
    /**
     * get a promise of an array of auction ids and amounts in which the given account has placed a bid,
     * or all auctions if beneficiaryAddressis not supplied
     * @param beneficiaryAddress optional
     */
    getBids(beneficiaryAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const filter = beneficiaryAddress ? { _bidder: beneficiaryAddress } : {};
            const bids = yield this.Bid(filter, { fromBlock: 0 }).get();
            return bids.map((bid) => {
                return {
                    amount: bid.args._amount,
                    auctionId: bid.args._auctionId.toNumber(),
                };
            });
        });
    }
    /**
     * Get a promise of the first date/time when anything can be redeemed
     */
    getRedeemEnableTime() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.redeemEnableTime');
            const seconds = yield this.contract.redeemEnableTime();
            return new Date(seconds.toNumber() * 1000);
        });
    }
    /**
     * Get a promise of the total reputation rewardable across all the auctions
     */
    getReputationReward() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.reputationReward');
            const auctionReputationReward = yield this.contract.auctionReputationReward();
            const numAuctions = yield this.getNumberOfAuctions();
            return auctionReputationReward.mul(numAuctions);
        });
    }
    getReputationRewardLeft() {
        this.logContractFunctionCall('Auction4Reputation.reputationRewardLeft');
        return this.contract.reputationRewardLeft();
    }
    getAuctionsEndTime() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.auctionsEndTime');
            const dt = yield this.contract.auctionsEndTime();
            return new Date(dt.toNumber() * 1000);
        });
    }
    getAuctionsStartTime() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.auctionsStartTime');
            const dt = yield this.contract.auctionsStartTime();
            return new Date(dt.toNumber() * 1000);
        });
    }
    getNumberOfAuctions() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.numberOfAuctions');
            return (yield this.contract.numberOfAuctions()).toNumber();
        });
    }
    /**
     * Get a promise of  the reputation reward of a single auction
     */
    auctionReputationReward() {
        this.logContractFunctionCall('Auction4Reputation.auctionReputationReward');
        return this.contract.auctionReputationReward();
    }
    /**
     * Return promise of 0-based id of the current auction.  Returns a negative number if the auction
     * has not yet begun.  If auction is over then may return an id for an auction that doesn't exist.
     */
    getCurrentAuctionId() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const auctionPeriod = (yield this.getAuctionPeriod()) * 1000;
            const auctionsStartTime = (yield this.getAuctionsStartTime()).getTime();
            return Math.floor((Date.now() - auctionsStartTime) / auctionPeriod);
        });
    }
    /**
     * Get a promise of the number of seconds in a single auction
     */
    getAuctionPeriod() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.auctionPeriod');
            return (yield this.contract.auctionPeriod()).toNumber();
        });
    }
    getToken() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.token');
            const address = yield this.contract.token();
            return wrapperService_1.WrapperService.factories.Erc20.at(address);
        });
    }
    getWallet() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logContractFunctionCall('Auction4Reputation.wallet');
            return yield this.contract.wallet();
        });
    }
    getAvatar() {
        this.logContractFunctionCall('Auction4Reputation.avatar');
        return this.contract.avatar();
    }
    /**
     * Get a promise of the total amount bid for a given auction.
     */
    getAuctionTotalBid(auctionId) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.validateAuctionId(auctionId);
            this.logContractFunctionCall('Auction4Reputation.auctions', { auctionId });
            return this.contract.auctions(new bignumber_js_1.default(auctionId));
        });
    }
    hydrated() {
        super.hydrated();
        /* tslint:disable:max-line-length */
        this.Bid = this.createEventFetcherFactory(this.contract.Bid);
        this.Redeem = this.createEventFetcherFactory(this.contract.Redeem);
        /* tslint:enable:max-line-length */
    }
    validateAuctionId(auctionId) {
        if (typeof (auctionId) === 'undefined') {
            throw new Error('auctionId is not defined');
        }
        if (auctionId < 0) {
            throw new Error('auctionId must be greater or equal to 0');
        }
        if (!Number.isInteger(auctionId)) {
            throw new Error('auctionId must be an integer number');
        }
    }
}
exports.Auction4ReputationWrapper = Auction4ReputationWrapper;
class Auction4ReputationType extends contractWrapperFactory_1.ContractWrapperFactory {
    deployed() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            throw new Error('Auction4Reputation has not been deployed');
        });
    }
}
exports.Auction4ReputationType = Auction4ReputationType;
exports.Auction4ReputationFactory = new Auction4ReputationType('Auction4Reputation', Auction4ReputationWrapper, new web3EventService_1.Web3EventService());
//# sourceMappingURL=auction4Reputation.js.map