"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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isApprovedForAll = exports.approveOperator = exports.calculateClaims = exports.calculateOwnership = exports.getAllCertificateEvents = exports.decodeEvent = exports.getAllOwnedCertificates = exports.getAllCertificates = exports.decodeData = exports.encodeData = exports.decodeClaimData = exports.encodeClaimData = void 0;
const ethers_1 = require("ethers");
const Certificate_1 = require("./Certificate");
const events_1 = require("../utils/events");
const encodeClaimData = (claimData) => {
    const { beneficiary, location, countryCode, periodStartDate, periodEndDate, purpose } = claimData;
    return ethers_1.utils.defaultAbiCoder.encode(['string', 'string', 'string', 'string', 'string', 'string'], [beneficiary, location, countryCode, periodStartDate, periodEndDate, purpose]);
};
exports.encodeClaimData = encodeClaimData;
const decodeClaimData = (encodedClaimData) => {
    const [beneficiary, location, countryCode, periodStartDate, periodEndDate, purpose] = ethers_1.utils.defaultAbiCoder.decode(['string', 'string', 'string', 'string', 'string', 'string'], encodedClaimData);
    return {
        beneficiary,
        location,
        countryCode,
        periodStartDate,
        periodEndDate,
        purpose
    };
};
exports.decodeClaimData = decodeClaimData;
const encodeData = (data) => {
    const { generationStartTime, generationEndTime, deviceId, metadata } = data;
    return ethers_1.utils.defaultAbiCoder.encode(['uint256', 'uint256', 'string', 'string'], [generationStartTime, generationEndTime, deviceId, metadata !== null && metadata !== void 0 ? metadata : '']);
};
exports.encodeData = encodeData;
const decodeData = (encodedData) => {
    const [generationStartTime, generationEndTime, deviceId, metadata] = ethers_1.utils.defaultAbiCoder.decode(['uint256', 'uint256', 'string', 'string'], encodedData);
    return {
        generationStartTime: generationStartTime.toNumber(),
        generationEndTime: generationEndTime.toNumber(),
        deviceId,
        metadata
    };
};
exports.decodeData = decodeData;
function getAllCertificates(blockchainProperties) {
    return __awaiter(this, void 0, void 0, function* () {
        const { registry } = blockchainProperties;
        const issuanceEvents = yield (0, events_1.getEventsFromContract)(registry, registry.filters.IssuanceSingle(null, null, null));
        const certificatePromises = issuanceEvents.map((event) => new Certificate_1.Certificate(event._id.toNumber(), blockchainProperties).sync());
        return Promise.all(certificatePromises);
    });
}
exports.getAllCertificates = getAllCertificates;
function getAllOwnedCertificates(blockchainProperties) {
    return __awaiter(this, void 0, void 0, function* () {
        const { registry, activeUser } = blockchainProperties;
        const owner = yield activeUser.getAddress();
        const transfers = yield (0, events_1.getEventsFromContract)(registry, registry.filters.TransferSingle(null, null, owner, null, null));
        const certificateIds = [
            ...new Set(transfers.map((transfer) => transfer.id.toNumber()))
        ];
        const balances = yield registry.balanceOfBatch(Array(certificateIds.length).fill(owner), certificateIds);
        const available = certificateIds.filter((id, index) => !balances[index].isZero());
        const certificatePromises = available.map((id) => new Certificate_1.Certificate(id, blockchainProperties).sync());
        return Promise.all(certificatePromises);
    });
}
exports.getAllOwnedCertificates = getAllOwnedCertificates;
function decodeEvent(eventName, event, contract) {
    return __awaiter(this, void 0, void 0, function* () {
        const eventBlock = yield contract.provider.getBlock(event.blockHash);
        return Object.assign({ name: eventName, transactionHash: event.transactionHash, blockHash: event.blockHash, timestamp: eventBlock.timestamp }, contract.interface.decodeEventLog(eventName, event.data, event.topics));
    });
}
exports.decodeEvent = decodeEvent;
const getAllCertificateEvents = (certId, blockchainProperties) => __awaiter(void 0, void 0, void 0, function* () {
    const { registry } = blockchainProperties;
    const getEvent = (filter, eventName) => __awaiter(void 0, void 0, void 0, function* () {
        const logs = yield registry.provider.getLogs(Object.assign(Object.assign({}, filter), { fromBlock: 0, toBlock: 'latest' }));
        const parsedLogs = yield Promise.all(logs.map((event) => __awaiter(void 0, void 0, void 0, function* () { return decodeEvent(eventName, event, registry); })));
        return parsedLogs.filter((event) => { var _a, _b; return ((_a = event._id) === null || _a === void 0 ? void 0 : _a.toNumber()) === certId || ((_b = event.id) === null || _b === void 0 ? void 0 : _b.toNumber()) === certId; });
    });
    const issuanceSingleEvents = yield getEvent(registry.filters.IssuanceSingle(null, null, null), 'IssuanceSingle');
    const issuanceBatchEvents = yield getEvent(registry.filters.IssuanceBatch(null, null, null), 'IssuanceBatch');
    const transferSingleEvents = yield getEvent(registry.filters.TransferSingle(null, null, null, null, null), 'TransferSingle');
    const transferBatchEvents = yield getEvent(registry.filters.TransferBatch(null, null, null, null, null), 'TransferBatch');
    const transferBatchMultipleEvents = yield getEvent(registry.filters.TransferBatchMultiple(null, null, null, null, null), 'TransferBatchMultiple');
    const claimSingleEvents = yield getEvent(registry.filters.ClaimSingle(null, null, null, null, null, null), 'ClaimSingle');
    const claimBatchEvents = yield getEvent(registry.filters.ClaimBatch(null, null, null, null, null, null), 'ClaimBatch');
    const claimBatchMultipleEvents = yield getEvent(registry.filters.ClaimBatchMultiple(null, null, null, null, null, null), 'ClaimBatchMultiple');
    return [
        ...issuanceSingleEvents,
        ...issuanceBatchEvents,
        ...transferSingleEvents,
        ...transferBatchEvents,
        ...transferBatchMultipleEvents,
        ...claimSingleEvents,
        ...claimBatchEvents,
        ...claimBatchMultipleEvents
    ];
});
exports.getAllCertificateEvents = getAllCertificateEvents;
const calculateOwnership = (certificateId, blockchainProperties, creationBlockNumber = 0) => __awaiter(void 0, void 0, void 0, function* () {
    const ownedShares = {};
    const { registry } = blockchainProperties;
    const transferSingleEvents = (yield (0, events_1.getEventsFromContract)(registry, registry.filters.TransferSingle(null, null, null, null, null), creationBlockNumber)).filter((event) => event.id.eq(certificateId));
    const transferBatchEvents = (yield (0, events_1.getEventsFromContract)(registry, registry.filters.TransferBatch(null, null, null, null, null), creationBlockNumber)).filter((e) => e.ids.some((id) => id.eq(certificateId)));
    const transferBatchMultipleEvents = (yield (0, events_1.getEventsFromContract)(registry, registry.filters.TransferBatchMultiple(null, null, null, null, null), creationBlockNumber)).filter((e) => e.ids.some((id) => id.eq(certificateId)));
    const allHistoricOwners = [
        ...new Set([
            ...[...transferSingleEvents, ...transferBatchEvents].map((event) => event.to),
            ...transferBatchMultipleEvents
                .map((event) => event.to)
                .reduce((a, b) => a.concat(b), [])
        ])
    ].filter((address) => address !== ethers_1.constants.AddressZero);
    const allHistoricOwnersBalances = yield registry.balanceOfBatch(allHistoricOwners, Array(allHistoricOwners.length).fill(certificateId.toString()));
    allHistoricOwners.forEach((owner, index) => {
        ownedShares[owner] = allHistoricOwnersBalances[index].toString();
    });
    return ownedShares;
});
exports.calculateOwnership = calculateOwnership;
const calculateClaims = (certificateId, blockchainProperties, creationBlockNumber = 0) => __awaiter(void 0, void 0, void 0, function* () {
    const claimedShares = {};
    const { registry } = blockchainProperties;
    const claimSingleEvents = (yield (0, events_1.getEventsFromContract)(registry, registry.filters.ClaimSingle(null, null, null, null, null, null), creationBlockNumber)).filter((event) => event._id.eq(certificateId));
    const claimBatchEvents = (yield (0, events_1.getEventsFromContract)(registry, registry.filters.ClaimBatch(null, null, null, null, null, null), creationBlockNumber)).filter((e) => e._ids.some((id) => id.eq(certificateId)));
    const claimBatchMultipleEvents = (yield (0, events_1.getEventsFromContract)(registry, registry.filters.ClaimBatchMultiple(null, null, null, null, null, null), creationBlockNumber)).filter((e) => e._ids.some((id) => id.eq(certificateId)));
    const allHistoricClaimers = [
        ...new Set([
            ...[...claimSingleEvents, ...claimBatchEvents].map((event) => event._claimSubject),
            ...claimBatchMultipleEvents
                .map((event) => event._claimSubject)
                .reduce((a, b) => a.concat(b), [])
        ])
    ].filter((address) => address !== ethers_1.constants.AddressZero);
    const allHistoricClaimersBalances = yield registry.claimedBalanceOfBatch(allHistoricClaimers, Array(allHistoricClaimers.length).fill(certificateId.toString()));
    allHistoricClaimers.forEach((owner, index) => {
        claimedShares[owner] = allHistoricClaimersBalances[index].toString();
    });
    return claimedShares;
});
exports.calculateClaims = calculateClaims;
function approveOperator(operator, blockchainProperties) {
    return __awaiter(this, void 0, void 0, function* () {
        const { activeUser, registry } = blockchainProperties;
        const registryWithSigner = registry.connect(activeUser);
        return registryWithSigner.setApprovalForAll(operator, true);
    });
}
exports.approveOperator = approveOperator;
function isApprovedForAll(operator, blockchainProperties) {
    return __awaiter(this, void 0, void 0, function* () {
        const { activeUser, registry } = blockchainProperties;
        const registryWithSigner = registry.connect(activeUser);
        const address = yield activeUser.getAddress();
        return registryWithSigner.isApprovedForAll(address, operator);
    });
}
exports.isApprovedForAll = isApprovedForAll;
//# sourceMappingURL=CertificateUtils.js.map