"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 SteemOperationNumber_1 = require("./blockchain/SteemOperationNumber");
const Protocol_1 = require("./protocol/Protocol");
const V1Handler_1 = require("./protocol/versions/v1/V1Handler");
const Validator_1 = require("./validation/Validator");
const V2Handler_1 = require("./protocol/versions/v2/V2Handler");
const RulesUpdater_1 = require("./RulesUpdater");
const log_1 = require("./log/log");
const SingleDaemon_1 = require("./synchronizer/SingleDaemon");
/**
 * Wise is a vote delegation system for steem blockchain. Wise allows you to securely grant other steemians your voting
 * power under conditions specified by you. For usage examples and more information please refer README. A complete cli
 * tool that enables performing all wise operations is available in package steem-wise-cli. A manual is available at
 * https://noisy-witness.github.io/steem-wise-manual/introduction.
 *
 * This is the main class of Wise.
 */
class Wise {
    /**
     * Constructs wise object.
     *
     * @constructor
     * @param {string} account - steem account name for performing operations (a voter if sending voteorder, a delegator
     *      if setting rules).
     * @param {Api} api - an Api (Direct blockchain api, our REST database, or any custom api that
     *      implements Api abstract class)
     * @param {Protocol} [protocol=(default protocol)] -  a Protocol object with Protocol version handlers specified.
     *      If not specified a default protocol object is created. We recommend you to use the default protocol
     *      (leave this option) as it is always up-to-date.
     */
    constructor(account, api, protocol) {
        /**
         * Uploads rulesets for this voter to the blockchain.
         *
         * @param {string} voter - voter for whom these rulesets are set.
         * @param {Ruleset []} rules - an array of rulesets for this voter
         * @param {(msg: string, proggress: number) => void} [proggressCallback] proggress callback that will receive
         *      proggress notifications (useful for UI)
         *
         * @returns {Promise<SteemOperationNumber>} - a Promise that resolves with SteemOperationNumber which indicates
         *      the blockchain moment of this wiseoperation.
         */
        this.uploadRulesetsForVoter = (voter, rulesets, proggressCallback = () => { }) => {
            return RulesUpdater_1.RulesUpdater.uploadRulesetsForVoter(this.api, this.protocol, this.account, voter, rulesets, proggressCallback);
        };
        /**
         * Download rulesets set by the delegator for a specified voter.
         *
         * @param {string} delegator - the delegator account name
         * @param {string} voter - the voter account name
         * @param {SteemOperationNumber} [atMoment = SteemOperationNumber.NOW] - a moment in blockchain (block, transaction,
         *      and operation number). Default is SteemOperationNumber.NOW.
         *
         * @returns {Promise<Ruleset []>} - a Promise that resolves with rulesets that are in effect at the specified moment
         *      or now.
         */
        this.downloadRulesetsForVoter = (delegator, voter, atMoment = SteemOperationNumber_1.SteemOperationNumber.NOW) => __awaiter(this, void 0, void 0, function* () {
            const setRules = yield this.api.loadRulesets({ delegator: delegator, voter: voter }, atMoment);
            if (setRules.length > 0)
                return setRules[0].rulesets;
            else
                return [];
        });
        /**
         * Uploads all rulesets for this delegator account for all voters. First it reads the account history and looks
         * for all rulesets thet were set and are currently in power. Then it compares it with provided new rulesets.
         * It then:
         *  - sends new rulesets if no rules were set previously for particular voter
         *  - sends new rulesets if rules for particular voter changed
         *  - clears out the rules for voters that previously had rules, but are not listed in current rules (this is done
         *    by sending empty array of rulesets).
         *
         * @param {SetRulesForVoter} rules - new rules for all voters of this delegator
         * @param {(error: Error, result: SteemOperationNumber | true) => void} [callback=undefined] -
         *      callback function that will be called when rules are uploaded or an error occurs. Can be ommited or set
         *      to undefined if you want to only use the returned promise.
         * @param {(msg: string, proggress: number) => void} [proggressCallback] proggress callback that will receive
         *      proggress notifications (useful for UI)
         *
         * @returns {Promise<SteemOperationNumber>} - a Promise that resolves with SteemOperationNumber or true.
         *      SteemOperationNumber indicates that rules were updated and contains the blockchain moment of last update
         *      operation. True indicates that rules were already up to date and no operations were sent to blockchain.
         */
        this.uploadAllRulesets = (rules, proggressCallback = () => { }) => {
            return RulesUpdater_1.RulesUpdater.uploadAllRulesets(this.api, this.protocol, this.account, rules, proggressCallback);
        };
        /**
         * Downloads from blockchain all rules set by specified delegator (default is provided accound) at specified moment
         * (default moment is NOW).
         *
         * @param {string} [delegator] - account name of the delegator (default is account name set in constructor of this
         *      Wise instance)
         * @param {SteemOperationNumber} [atMoment = SteemOperationNumber.NOW] - a moment in blockchain (block, transaction,
         *      and operation number). Default is SteemOperationNumber.NOW.
         * @param {(msg: string, proggress: number) => void} [proggressCallback] proggress callback that will receive
         *      proggress notifications (useful for UI)
         *
         * @returns {Promise<EffectuatedSetRules []>} - a Promise that resolves with SteemOperationNumber or true.
         *      SteemOperationNumber indicates that rules were updated and contains the blockchain moment of last update
         *      operation. True indicates that rules were already up to date and no operations were sent to blockchain.
         */
        this.downloadAllRulesets = (delegator = this.account, atMoment = SteemOperationNumber_1.SteemOperationNumber.NOW, proggressCallback = () => { }) => {
            proggressCallback("Downloading all rulesets set by " + delegator, 0.0);
            const result = RulesUpdater_1.RulesUpdater.downloadAllRulesets(this.api, delegator, atMoment);
            proggressCallback("Downloaded all rulesets set by " + delegator, 1.0);
            return result;
        };
        /**
         * This method generates an array of steem operations that have to be sent to emit a Wise voteorder. This method
         * allows e.g. sending the voteorder through SteemConnect or generating a Vessel link.
         *
         * @param {string} delegator - account name of the delegator
         * @param {string} voter - account name of the voter
         * @param {SendVoteorder} voteorder - voteorder object
         * @param {(msg: string, proggress: number) => void} [proggressCallback] proggress callback that will receive
         *      proggress notifications (useful for UI)
         * @param {boolean} [skipValidation=false] allows to skip validation (send is immediate and allows sending
         *      invalid voteorder).
         *
         * @returns {Promise<steem.OperationWithDescriptor[]>} - a Promise that resolves with an array of steem operations that can
         * be directly sent to the blockchain using broadcast send in RPC/WS, or using external tool such as vessel
         * or steemconnect.
         */
        this.generateVoteorderOperations = (delegator, voter, voteorder, proggressCallback = () => { }, skipValidation = false) => __awaiter(this, void 0, void 0, function* () {
            proggressCallback("Validating voteorder...", 0.0);
            const smOp = {
                voter: voter,
                delegator: delegator,
                command: voteorder
            };
            const steemOps = this.protocol.serializeToBlockchain(smOp);
            if (steemOps.length !== 1)
                throw new Error("A voteorder should be a single blockchain operation");
            if (!skipValidation && !this.protocol.validateOperation(steemOps[0]))
                throw new Error("Operation object has invalid structure");
            if (!skipValidation) {
                const validationResult = yield this.validateVoteorder(delegator, voter, voteorder, SteemOperationNumber_1.SteemOperationNumber.FUTURE, proggressCallback);
                if (validationResult !== true)
                    throw new Error("Validation error: " + validationResult.message);
                proggressCallback("Voteorder validation done", 1.0);
            }
            return steemOps;
        });
        /**
         * Sends a voteorder to the blockchain.
         *
         * @param {string} delegator - account name of the delegator
         * @param {SendVoteorder} voteorder - voteorder object
         * @param {(msg: string, proggress: number) => void} [proggressCallback] proggress callback that will receive
         *      proggress notifications (useful for UI)
         * @param {boolean} [skipValidation=false] allows to skip validation (send is immediate and allows sending
         *      invalid voteorder).
         *
         * @returns {Promise<SteemOperationNumber>} - a Promise that resolves with SteemOperationNumber when the voteorder
         *      is sent successfully.
         */
        this.sendVoteorder = (delegator, voteorder, proggressCallback = () => { }, skipValidation = false) => __awaiter(this, void 0, void 0, function* () {
            const steemOps = yield this.generateVoteorderOperations(delegator, this.account, voteorder, proggressCallback, skipValidation);
            proggressCallback("Sending voteorder...", 0.5);
            const sentMoment = yield this.api.sendToBlockchain(steemOps);
            proggressCallback("Sending voteorder done", 1.0);
            return sentMoment;
        });
        /**
         * Validates a voteorder. Both structure and rules.
         *
         * @param {string} delegator - delegator account name
         * @param {string} voter - voter account name
         * @param {SendVoteorder} voteorder - a voteorder object
         * @param {SteemOperationNumber} atMoment - a moment in blockchain at which we are testing the voteorder validity
         *      (usually this is a moment at which the voteorder appeared on the blockchain). Set atMoment to
         *      SteemOperationNumber.FUTURE, which will indicate a potential (not sent) voteorder.
         * @param {(msg: string, proggress: number) => void} [proggressCallback] proggress callback that will receive
         *      proggress notifications (useful for UI)
         *
         * @returns {Promise<ValidationException | true>} - a Promise that resolves with ValidationException or true.
         *      If the validation was successful result === true. If it was not, result is an instance of
         *      ValidationException. Note that ValidationException is not thrown, nor returned as rejection. Other errors
         *      are thrown/rejected.
         */
        this.validateVoteorder = (delegator, voter, voteorder, atMoment, proggressCallback = () => { }) => {
            const v = new Validator_1.Validator(this.api);
            if (proggressCallback)
                v.withProggressCallback(proggressCallback);
            return v.validate(delegator, voter, voteorder, atMoment);
        };
        /**
         * Returns the moment in the blockchain of the newest confirmation of a specified delegator.
         *
         * @param {string} delegator - the delegator account name
         * @param {(error: Error, result: undefined | SteemOperationNumber)} [callback=undefined] -
         *      callback function that will be called when the result is ready. Can be ommited or set
         *      to undefined if you want to only use the returned promise. Result value is same as in returned Promise.
         *
         * @returns {Promise<undefined | SteemOperationNumber>} - a Promise that resolves with undefined or
         * SteemOperationNumber. It resolves with undefined if this account has not sent any confirmations yet. Otherwise
         * it resolves with SteemOperationNumber pointing at the moment of the newest confirmation.
         */
        this.getLastConfirmationMoment = (delegator) => {
            return this.api.getLastConfirmationMoment(delegator);
        };
        /**
         * Starts the Wise daemon. It reads blockchain transactions looking for Wise operations. When a new voteorder
         * is found, it is processed, validated and the daemon sends to the blockchain [vote + confirmation] on a valid
         * voteorder or [rejection message] on invalid one. It loops through blockchain operations starting from *since*
         * param. When it reaches the HEAD it loops through newcoming operations. It stops when notifierCallback returns
         * false.
         *
         * @param {SteemOperationNumber} since - moment in the blockchain to start synchronization from
         * @param {SingleDaemon.NotifierCallback} notifierCallback - a callback which is notified every time an event
         *      occurs. It should return true to continue synchronization, or false if to stop it.
         *
         * @returns - this method returns the Synchronizer object.
         */
        this.startDaemon = (since, notifierCallback) => {
            const daemon = new SingleDaemon_1.SingleDaemon(this.api, this.protocol, this.account, notifierCallback);
            (() => __awaiter(this, void 0, void 0, function* () {
                yield daemon.start(since);
            }))();
            return daemon;
        };
        /**
         * Returns current protocol of this Wise object.
         *
         * @returns {Protocol} - the current Protocol of this Wise object.
         */
        this.getProtocol = () => {
            return this.protocol;
        };
        this.account = account;
        this.api = api;
        if (protocol) {
            this.protocol = protocol;
        }
        else {
            this.protocol = Wise.constructDefaultProtocol();
        }
    }
    static constructDefaultProtocol() {
        return new Protocol_1.Protocol([
            new V2Handler_1.V2Handler(),
            new V1Handler_1.V1Handler()
        ]);
    }
    static getVersion() {
        return /*§ §*/ "3.1.0" /*§ ' "' + data.config.wise.version + '" ' §.*/;
    }
    /**
     * Returns instance of Log singleton that is used inside steem-wise-core. This may look insane as Log is a singleton.
     * In fact. This is an antipattern in Typescript, although a very useful one. Static class members has a scope in
     * typescript. The scope is limited to a single node project. If we want to configure wise core log in another project
     * we have to get the singleton instance from inside of steem-wise-core package.
     */
    static getLog() {
        return log_1.Log.log();
    }
}
exports.Wise = Wise;
/**
 * Prayer:
 *  Gloria Patri, et Filio, et Spiritui Sancto, sicut erat in principio et nunc et semper et in saecula
 *  saeculorum. Amen. In te, Domine, speravi: non confundar in aeternum.
 */
/**
 * Exports
 */
var Api_1 = require("./api/Api");
exports.Api = Api_1.Api;
var DirectBlockchainApi_1 = require("./api/directblockchain/DirectBlockchainApi");
exports.DirectBlockchainApi = DirectBlockchainApi_1.DirectBlockchainApi;
var WiseSQLApi_1 = require("./api/sql/WiseSQLApi");
exports.WiseSQLApi = WiseSQLApi_1.WiseSQLApi;
var WiseSQLProtocol_1 = require("./api/sql/WiseSQLProtocol");
exports.WiseSQLProtocol = WiseSQLProtocol_1.WiseSQLProtocol;
var DisabledApi_1 = require("./api/DisabledApi");
exports.DisabledApi = DisabledApi_1.DisabledApi;
var SteemOperationNumber_2 = require("./blockchain/SteemOperationNumber");
exports.SteemOperationNumber = SteemOperationNumber_2.SteemOperationNumber;
var Protocol_2 = require("./protocol/Protocol");
exports.Protocol = Protocol_2.Protocol;
var ConfirmVote_1 = require("./protocol/ConfirmVote");
exports.ConfirmVote = ConfirmVote_1.ConfirmVote;
var ConfirmVoteBoundWithVote_1 = require("./protocol/ConfirmVoteBoundWithVote");
exports.ConfirmVoteBoundWithVote = ConfirmVoteBoundWithVote_1.ConfirmVoteBoundWithVote;
var SendVoteorder_1 = require("./protocol/SendVoteorder");
exports.SendVoteorder = SendVoteorder_1.SendVoteorder;
var Ruleset_1 = require("./protocol/Ruleset");
exports.Ruleset = Ruleset_1.Ruleset;
var SetRules_1 = require("./protocol/SetRules");
exports.SetRules = SetRules_1.SetRules;
var SetRulesForVoter_1 = require("./protocol/SetRulesForVoter");
exports.SetRulesForVoter = SetRulesForVoter_1.SetRulesForVoter;
var EffectuatedSetRules_1 = require("./protocol/EffectuatedSetRules");
exports.EffectuatedSetRules = EffectuatedSetRules_1.EffectuatedSetRules;
var Rule_1 = require("./rules/Rule");
exports.Rule = Rule_1.Rule;
var AuthorsRule_1 = require("./rules/AuthorsRule");
exports.AuthorsRule = AuthorsRule_1.AuthorsRule;
var TagsRule_1 = require("./rules/TagsRule");
exports.TagsRule = TagsRule_1.TagsRule;
var CustomRPCRule_1 = require("./rules/CustomRPCRule");
exports.CustomRPCRule = CustomRPCRule_1.CustomRPCRule;
var WeightRule_1 = require("./rules/WeightRule");
exports.WeightRule = WeightRule_1.WeightRule;
var VotingPowerRule_1 = require("./rules/VotingPowerRule");
exports.VotingPowerRule = VotingPowerRule_1.VotingPowerRule;
var AgeOfPostRule_1 = require("./rules/AgeOfPostRule");
exports.AgeOfPostRule = AgeOfPostRule_1.AgeOfPostRule;
var FirstPostRule_1 = require("./rules/FirstPostRule");
exports.FirstPostRule = FirstPostRule_1.FirstPostRule;
var PayoutRule_1 = require("./rules/PayoutRule");
exports.PayoutRule = PayoutRule_1.PayoutRule;
var VotersRule_1 = require("./rules/VotersRule");
exports.VotersRule = VotersRule_1.VotersRule;
var VotesCountRule_1 = require("./rules/VotesCountRule");
exports.VotesCountRule = VotesCountRule_1.VotesCountRule;
var WeightForPeriodRule_1 = require("./rules/WeightForPeriodRule");
exports.WeightForPeriodRule = WeightForPeriodRule_1.WeightForPeriodRule;
var ExpirationDateRule_1 = require("./rules/ExpirationDateRule");
exports.ExpirationDateRule = ExpirationDateRule_1.ExpirationDateRule;
var RulePrototyper_1 = require("./rules/RulePrototyper");
exports.RulePrototyper = RulePrototyper_1.RulePrototyper;
var RulesUpdater_2 = require("./RulesUpdater");
exports.RulesUpdater = RulesUpdater_2.RulesUpdater;
var Validator_2 = require("./validation/Validator");
exports.Validator = Validator_2.Validator;
var ValidationException_1 = require("./validation/ValidationException");
exports.ValidationException = ValidationException_1.ValidationException;
var ValidationContext_1 = require("./validation/ValidationContext");
exports.ValidationContext = ValidationContext_1.ValidationContext;
var NotFoundException_1 = require("./util/NotFoundException");
exports.NotFoundException = NotFoundException_1.NotFoundException;
var SingleDaemon_2 = require("./synchronizer/SingleDaemon");
exports.SingleDaemon = SingleDaemon_2.SingleDaemon;
var LegacySynchronizer_1 = require("./synchronizer/LegacySynchronizer");
exports.LegacySynchronizer = LegacySynchronizer_1.LegacySynchronizer;
var UniversalSynchronizer_1 = require("./synchronizer/UniversalSynchronizer");
exports.UniversalSynchronizer = UniversalSynchronizer_1.UniversalSynchronizer;
var Abstractlog_1 = require("./log/Abstractlog");
exports.AbstractLog = Abstractlog_1.AbstractLog;
exports.default = Wise;
//# sourceMappingURL=wise.js.map