"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.eventToLog = void 0;
exports.spreadEvent = spreadEvent;
exports.paginatedEventQuery = paginatedEventQuery;
exports.getPaginatedBlockRanges = getPaginatedBlockRanges;
exports.logToSortableEvent = logToSortableEvent;
exports.spreadEventWithBlockNumber = spreadEventWithBlockNumber;
exports.sortEventsAscending = sortEventsAscending;
exports.sortEventsAscendingInPlace = sortEventsAscendingInPlace;
exports.sortEventsDescending = sortEventsDescending;
exports.sortEventsDescendingInPlace = sortEventsDescendingInPlace;
exports.isEventOlder = isEventOlder;
exports.getTransactionRefs = getTransactionRefs;
exports.duplicateEvent = duplicateEvent;
const common_1 = require("./common");
const _1 = require("./");
const maxRetries = 3;
const retrySleepTime = 10;
function spreadEvent(args) {
    const keys = Object.keys(args).filter((key) => isNaN(+key));
    const returnedObject = {};
    keys.forEach((key) => {
        switch (typeof args[key]) {
            case "boolean":
            case "number":
            case "bigint":
            case "string":
                returnedObject[key] = args[key];
                break;
            case "object":
                if (Array.isArray(args[key])) {
                    returnedObject[key] =
                        Object.keys(args[key]).filter((key) => isNaN(+key)).length > 0
                            ? spreadEvent(args[key])
                            : args[key];
                }
                else {
                    returnedObject[key] = args[key];
                }
                break;
        }
    });
    if (returnedObject.groupIndex) {
        returnedObject.groupIndex = Number(returnedObject.groupIndex);
    }
    if (returnedObject.leafId) {
        returnedObject.leafId = Number(returnedObject.leafId);
    }
    if (returnedObject.chainId) {
        returnedObject.chainId = Number(returnedObject.chainId);
    }
    if (returnedObject.destinationChainId) {
        returnedObject.destinationChainId = Number(returnedObject.destinationChainId);
    }
    if (returnedObject.originChainId) {
        returnedObject.originChainId = Number(returnedObject.originChainId);
    }
    if (returnedObject.repaymentChainId) {
        returnedObject.repaymentChainId = Number(returnedObject.repaymentChainId);
    }
    if (returnedObject.l2ChainId) {
        returnedObject.l2ChainId = Number(returnedObject.l2ChainId);
    }
    if (returnedObject.rootBundleId) {
        returnedObject.rootBundleId = Number(returnedObject.rootBundleId);
    }
    if ((0, _1.isDefined)(returnedObject.depositId)) {
        returnedObject.depositId = (0, _1.toBN)(returnedObject.depositId);
    }
    return returnedObject;
}
const eventToLog = (event) => ({ ...event, event: event.event, args: spreadEvent(event.args) });
exports.eventToLog = eventToLog;
async function paginatedEventQuery(contract, filter, searchConfig, retryCount = 0) {
    if (searchConfig.maxLookBack === 0) {
        const events = await contract.queryFilter(filter, searchConfig.from, searchConfig.to);
        return events.map(exports.eventToLog);
    }
    const paginatedRanges = getPaginatedBlockRanges(searchConfig);
    try {
        return ((await Promise.all(paginatedRanges.map(([fromBlock, toBlock]) => contract.queryFilter(filter, fromBlock, toBlock))))
            .flat()
            .filter((event) => event.blockNumber >= searchConfig.from && event.blockNumber <= searchConfig.to)
            .map(exports.eventToLog));
    }
    catch (error) {
        if (retryCount < maxRetries) {
            await (0, common_1.delay)(retrySleepTime);
            return await paginatedEventQuery(contract, filter, searchConfig, retryCount + 1);
        }
        else {
            throw error;
        }
    }
}
function getPaginatedBlockRanges({ from, to, maxLookBack }) {
    const fromBlock = from;
    const toBlock = to;
    const maxBlockLookBack = maxLookBack;
    if (maxBlockLookBack === undefined) {
        return [[fromBlock, toBlock]];
    }
    if (fromBlock > toBlock) {
        return [];
    }
    if (maxBlockLookBack <= 0) {
        throw new Error("Cannot set maxBlockLookBack <= 0");
    }
    const flooredStartBlock = Math.floor(fromBlock / maxBlockLookBack) * maxBlockLookBack;
    const iterations = Math.ceil((toBlock + 1 - flooredStartBlock) / maxBlockLookBack);
    const ranges = [];
    for (let i = 0; i < iterations; i++) {
        const innerFromBlock = flooredStartBlock + maxBlockLookBack * i;
        const innerToBlock = Math.min(innerFromBlock + maxBlockLookBack - 1, toBlock);
        ranges.push([innerFromBlock, innerToBlock]);
    }
    return ranges;
}
function logToSortableEvent(log) {
    return {
        txnIndex: log.transactionIndex,
        txnRef: log.transactionHash,
        logIndex: log.logIndex,
        blockNumber: log.blockNumber,
    };
}
function spreadEventWithBlockNumber(event) {
    return {
        ...spreadEvent(event.args),
        ...logToSortableEvent(event),
    };
}
function sortEventsAscending(events) {
    return sortEventsAscendingInPlace([...events]);
}
function sortEventsAscendingInPlace(events) {
    return events.sort((ex, ey) => ex.blockNumber === ey.blockNumber ? ex.logIndex - ey.logIndex : ex.blockNumber - ey.blockNumber);
}
function sortEventsDescending(events) {
    return sortEventsDescendingInPlace([...events]);
}
function sortEventsDescendingInPlace(events) {
    return events.sort((ex, ey) => ex.blockNumber === ey.blockNumber ? ey.logIndex - ex.logIndex : ey.blockNumber - ex.blockNumber);
}
function isEventOlder(ex, ey) {
    return ex.blockNumber === ey.blockNumber ? ex.logIndex < ey.logIndex : ex.blockNumber < ey.blockNumber;
}
function getTransactionRefs(events) {
    return [...Array.from(new Set(events.map((e) => e.txnRef)))];
}
function duplicateEvent(a, b) {
    return a.txnRef === b.txnRef && a.logIndex === b.logIndex;
}
//# sourceMappingURL=EventUtils.js.map