"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Asset = void 0;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const internal_1 = require("../../../internal");
const queries_1 = require("../../../middleware/queries");
const types_1 = require("../../../middleware/types");
const constants_1 = require("../../../utils/constants");
const conversion_1 = require("../../../utils/conversion");
const internal_2 = require("../../../utils/internal");
const AssetHolders_1 = require("./AssetHolders");
const Checkpoints_1 = require("./Checkpoints");
const Compliance_1 = require("./Compliance");
const CorporateActions_1 = require("./CorporateActions");
const Documents_1 = require("./Documents");
const Issuance_1 = require("./Issuance");
const Offerings_1 = require("./Offerings");
const Permissions_1 = require("./Permissions");
const Settlements_1 = require("./Settlements");
const TransferRestrictions_1 = require("./TransferRestrictions");
/**
 * Class used to manage all Asset functionality
 */
class Asset extends internal_1.Entity {
    /**
     * @hidden
     */
    constructor(identifiers, context) {
        super(identifiers, context);
        const { ticker } = identifiers;
        this.ticker = ticker;
        this.did = conversion_1.tickerToDid(ticker);
        this.documents = new Documents_1.Documents(this, context);
        this.settlements = new Settlements_1.Settlements(this, context);
        this.assetHolders = new AssetHolders_1.AssetHolders(this, context);
        this.issuance = new Issuance_1.Issuance(this, context);
        this.compliance = new Compliance_1.Compliance(this, context);
        this.transferRestrictions = new TransferRestrictions_1.TransferRestrictions(this, context);
        this.offerings = new Offerings_1.Offerings(this, context);
        this.checkpoints = new Checkpoints_1.Checkpoints(this, context);
        this.corporateActions = new CorporateActions_1.CorporateActions(this, context);
        this.permissions = new Permissions_1.Permissions(this, context);
        this.transferOwnership = internal_2.createProcedureMethod({ getProcedureAndArgs: args => [internal_1.transferAssetOwnership, Object.assign({ ticker }, args)] }, context);
        this.modify = internal_2.createProcedureMethod({ getProcedureAndArgs: args => [internal_1.modifyAsset, Object.assign({ ticker }, args)] }, context);
        this.freeze = internal_2.createProcedureMethod({
            getProcedureAndArgs: () => [internal_1.toggleFreezeTransfers, { ticker, freeze: true }],
            voidArgs: true,
        }, context);
        this.unfreeze = internal_2.createProcedureMethod({
            getProcedureAndArgs: () => [internal_1.toggleFreezeTransfers, { ticker, freeze: false }],
            voidArgs: true,
        }, context);
        this.modifyPrimaryIssuanceAgent = internal_2.createProcedureMethod({ getProcedureAndArgs: args => [internal_1.modifyPrimaryIssuanceAgent, Object.assign({ ticker }, args)] }, context);
        this.removePrimaryIssuanceAgent = internal_2.createProcedureMethod({ getProcedureAndArgs: () => [internal_1.removePrimaryIssuanceAgent, { ticker }], voidArgs: true }, context);
        this.redeem = internal_2.createProcedureMethod({ getProcedureAndArgs: args => [internal_1.redeemTokens, Object.assign({ ticker }, args)] }, context);
        this.controllerTransfer = internal_2.createProcedureMethod({ getProcedureAndArgs: args => [internal_1.controllerTransfer, Object.assign({ ticker }, args)] }, context);
    }
    /**
     * @hidden
     * Check if a value is of type [[UniqueIdentifiers]]
     */
    static isUniqueIdentifiers(identifier) {
        const { ticker } = identifier;
        return typeof ticker === 'string';
    }
    // eslint-disable-next-line require-jsdoc
    details(callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { context: { polymeshApi: { query: { asset, externalAgents }, }, }, ticker, context, } = this;
            /* eslint-disable @typescript-eslint/naming-convention */
            const assembleResult = ({ total_supply, divisible, owner_did, asset_type }, agentGroups, assetName, iuDisabled) => __awaiter(this, void 0, void 0, function* () {
                const primaryIssuanceAgents = [];
                const fullAgents = [];
                agentGroups.forEach(([storageKey, agentGroup]) => {
                    const rawAgentGroup = agentGroup.unwrap();
                    if (rawAgentGroup.isPolymeshV1Pia) {
                        primaryIssuanceAgents.push(new internal_1.Identity({ did: conversion_1.identityIdToString(storageKey.args[1]) }, context));
                    }
                    else if (rawAgentGroup.isFull) {
                        fullAgents.push(new internal_1.Identity({ did: conversion_1.identityIdToString(storageKey.args[1]) }, context));
                    }
                });
                const owner = new internal_1.Identity({ did: conversion_1.identityIdToString(owner_did) }, context);
                const type = conversion_1.assetTypeToKnownOrId(asset_type);
                let assetType;
                if (typeof type === 'string') {
                    assetType = type;
                }
                else {
                    const customType = yield asset.customTypes(conversion_1.bigNumberToU32(type, context));
                    assetType = conversion_1.bytesToString(customType);
                }
                return {
                    assetType,
                    isDivisible: conversion_1.boolToBoolean(divisible),
                    name: conversion_1.textToString(assetName),
                    owner,
                    totalSupply: conversion_1.balanceToBigNumber(total_supply),
                    primaryIssuanceAgents,
                    fullAgents,
                    requiresInvestorUniqueness: !conversion_1.boolToBoolean(iuDisabled),
                };
            });
            /* eslint-enable @typescript-eslint/naming-convention */
            const rawTicker = conversion_1.stringToTicker(ticker, context);
            const groupOfAgentPromise = externalAgents.groupOfAgent.entries(rawTicker);
            const namePromise = asset.assetNames(rawTicker);
            const disabledIuPromise = asset.disableInvestorUniqueness(rawTicker);
            if (callback) {
                const groupEntries = yield groupOfAgentPromise;
                const assetName = yield namePromise;
                const disabledInvestorUniqueness = yield disabledIuPromise;
                return asset.tokens(rawTicker, (securityToken) => __awaiter(this, void 0, void 0, function* () {
                    const result = yield assembleResult(securityToken, groupEntries, assetName, disabledInvestorUniqueness);
                    callback(result);
                }));
            }
            const [token, groups, name, disabledIu] = yield Promise.all([
                asset.tokens(rawTicker),
                groupOfAgentPromise,
                namePromise,
                disabledIuPromise,
            ]);
            return assembleResult(token, groups, name, disabledIu);
        });
    }
    // eslint-disable-next-line require-jsdoc
    currentFundingRound(callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { context: { polymeshApi: { query: { asset }, }, }, ticker, context, } = this;
            const rawTicker = conversion_1.stringToTicker(ticker, context);
            const assembleResult = (roundName) => conversion_1.fundingRoundNameToString(roundName) || null;
            if (callback) {
                return asset.fundingRound(rawTicker, round => {
                    callback(assembleResult(round));
                });
            }
            const fundingRound = yield asset.fundingRound(rawTicker);
            return assembleResult(fundingRound);
        });
    }
    // eslint-disable-next-line require-jsdoc
    getIdentifiers(callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { context: { polymeshApi: { query: { asset }, }, }, ticker, context, } = this;
            const rawTicker = conversion_1.stringToTicker(ticker, context);
            if (callback) {
                return asset.identifiers(rawTicker, identifiers => {
                    callback(identifiers.map(conversion_1.assetIdentifierToSecurityIdentifier));
                });
            }
            const assetIdentifiers = yield asset.identifiers(rawTicker);
            return assetIdentifiers.map(conversion_1.assetIdentifierToSecurityIdentifier);
        });
    }
    /**
     * Retrieve the identifier data (block number, date and event index) of the event that was emitted when the token was created
     *
     * @note uses the middleware
     * @note there is a possibility that the data is not ready by the time it is requested. In that case, `null` is returned
     */
    createdAt() {
        return __awaiter(this, void 0, void 0, function* () {
            const { ticker, context } = this;
            const { data: { eventByIndexedArgs: event }, } = yield context.queryMiddleware(queries_1.eventByIndexedArgs({
                moduleId: types_1.ModuleIdEnum.Asset,
                eventId: types_1.EventIdEnum.AssetCreated,
                eventArg1: internal_2.padString(ticker, constants_1.MAX_TICKER_LENGTH),
            }));
            return internal_2.optionize(conversion_1.middlewareEventToEventIdentifier)(event);
        });
    }
    // eslint-disable-next-line require-jsdoc
    isFrozen(callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { ticker, context: { polymeshApi: { query: { asset }, }, }, context, } = this;
            const rawTicker = conversion_1.stringToTicker(ticker, context);
            if (callback) {
                return asset.frozen(rawTicker, frozen => {
                    callback(conversion_1.boolToBoolean(frozen));
                });
            }
            const result = yield asset.frozen(rawTicker);
            return conversion_1.boolToBoolean(result);
        });
    }
    // eslint-disable-next-line require-jsdoc
    investorCount(callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { context: { polymeshApi: { query: { statistics }, }, }, context, ticker, } = this;
            const rawTicker = conversion_1.stringToTicker(ticker, context);
            if (callback) {
                return statistics.investorCountPerAsset(rawTicker, count => {
                    callback(conversion_1.u64ToBigNumber(count));
                });
            }
            const result = yield statistics.investorCountPerAsset(conversion_1.stringToTicker(ticker, context));
            return conversion_1.u64ToBigNumber(result);
        });
    }
    /**
     * Retrieve this Asset's Operation History
     *
     * @note Operations are grouped by the agent Identity who performed them
     *
     * @note uses the middleware
     */
    getOperationHistory() {
        return __awaiter(this, void 0, void 0, function* () {
            const { context: { polymeshApi: { query: { system }, }, }, context, ticker, } = this;
            const { data: { tickerExternalAgentHistory: tickerExternalAgentHistoryResult }, } = yield context.queryMiddleware(queries_1.tickerExternalAgentHistory({
                ticker,
            }));
            const multiParams = [];
            const results = [];
            tickerExternalAgentHistoryResult.forEach(({ did, history }) => {
                const historyResult = [];
                history.forEach(({ block_id: blockId, datetime, event_idx: eventIndex }) => {
                    const blockNumber = new bignumber_js_1.default(blockId);
                    multiParams.push(conversion_1.bigNumberToU32(blockNumber, context));
                    historyResult.push({
                        blockNumber,
                        blockDate: new Date(datetime),
                        eventIndex: new bignumber_js_1.default(eventIndex),
                    });
                });
                results.push({
                    identity: new internal_1.Identity({ did }, context),
                    history: historyResult,
                });
            });
            let hashes = [];
            if (multiParams.length) {
                hashes = yield system.blockHash.multi(multiParams);
            }
            const finalResults = [];
            results.forEach(({ identity, history }) => {
                const historyWithHashes = [];
                history.forEach(event => {
                    /*
                     * Since we filled the params array in the order in which the events appeared and this is being done
                     *   synchronously, the order and amount of results should be the same and the array should never be empty
                     *   until all the data is in place
                     */
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    const blockHash = conversion_1.hashToString(hashes.shift());
                    historyWithHashes.push(Object.assign(Object.assign({}, event), { blockHash }));
                });
                finalResults.push({
                    identity,
                    history: historyWithHashes,
                });
            });
            return finalResults;
        });
    }
    /**
     * Determine whether this Asset exists on chain
     */
    exists() {
        return __awaiter(this, void 0, void 0, function* () {
            const { ticker, context } = this;
            const tokenSize = yield context.polymeshApi.query.asset.tokens.size(conversion_1.stringToTicker(ticker, context));
            return !tokenSize.isZero();
        });
    }
    /**
     * Return the Asset's ticker
     */
    toJson() {
        return this.ticker;
    }
}
exports.Asset = Asset;
//# sourceMappingURL=index.js.map