"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getToPreferredDelta = void 0;
const debug_1 = require("./debug");
const debug = (0, debug_1.createDebug)('signalk-server:sourcepriorities');
const toPrecedences = (sourcePrioritiesMap) => Object.keys(sourcePrioritiesMap).reduce((acc, path) => {
    const priorityIndices = sourcePrioritiesMap[path].reduce((acc2, { sourceRef, timeout }, i) => {
        acc2.set(sourceRef, {
            precedence: i,
            timeout
        });
        return acc2;
    }, new Map());
    acc.set(path, priorityIndices);
    return acc;
}, new Map());
const getToPreferredDelta = (sourcePrioritiesData, unknownSourceTimeout = 10000) => {
    if (!sourcePrioritiesData) {
        debug('No priorities data');
        return (delta, _now, _selfContext) => delta;
    }
    const precedences = toPrecedences(sourcePrioritiesData);
    const contextPathTimestamps = new Map();
    const setLatest = (context, path, sourceRef, millis) => {
        let pathLatestTimestamps = contextPathTimestamps.get(context);
        if (!pathLatestTimestamps) {
            pathLatestTimestamps = new Map();
            contextPathTimestamps.set(context, pathLatestTimestamps);
        }
        pathLatestTimestamps.set(path, { sourceRef, timestamp: millis });
    };
    const getLatest = (context, path) => {
        const pathLatestTimestamps = contextPathTimestamps.get(context);
        if (!pathLatestTimestamps) {
            return {
                sourceRef: '',
                timestamp: 0
            };
        }
        const latestTimestamp = pathLatestTimestamps.get(path);
        if (!latestTimestamp) {
            return {
                sourceRef: '',
                timestamp: 0
            };
        }
        return latestTimestamp;
    };
    const HIGHESTPRECEDENCE = {
        precedence: 0,
        timeout: 0
    };
    const LOWESTPRECEDENCE = {
        precedence: Number.POSITIVE_INFINITY,
        timeout: unknownSourceTimeout
    };
    const isPreferredValue = (path, latest, sourceRef, millis) => {
        const pathPrecedences = precedences.get(path);
        if (!pathPrecedences) {
            return true;
        }
        const latestPrecedence = pathPrecedences.get(latest.sourceRef) || HIGHESTPRECEDENCE;
        const incomingPrecedence = pathPrecedences.get(sourceRef) || LOWESTPRECEDENCE;
        const latestIsFromHigherPrecedence = latestPrecedence.precedence < incomingPrecedence.precedence;
        const isPreferred = !latestIsFromHigherPrecedence ||
            millis - latest.timestamp > incomingPrecedence.timeout;
        if (debug.enabled) {
            debug(`${path}:${sourceRef}:${isPreferred}:${millis - latest.timestamp}`);
        }
        return isPreferred;
    };
    return (delta, now, selfContext) => {
        if (delta.context === selfContext) {
            const millis = now.getTime();
            delta.updates &&
                delta.updates.forEach((update) => {
                    if ('values' in update) {
                        update.values = update.values.reduce((acc, pathValue) => {
                            const latest = getLatest(delta.context, pathValue.path);
                            const isPreferred = isPreferredValue(pathValue.path, latest, update.$source, millis);
                            if (isPreferred) {
                                setLatest(delta.context, pathValue.path, update.$source, millis);
                                acc.push(pathValue);
                                return acc;
                            }
                            return acc;
                        }, []);
                    }
                });
        }
        return delta;
    };
};
exports.getToPreferredDelta = getToPreferredDelta;
//# sourceMappingURL=deltaPriority.js.map