import { MessagePriority, normalizeValueID, valueEquals, } from "@zwave-js/core";
import { ObjectKeyMap, setTimer } from "@zwave-js/shared";
import { EndpointsMixin } from "./50_Endpoints.js";
export class SchedulePollMixin extends EndpointsMixin {
    constructor(nodeId, driver, endpointIndex, deviceClass, supportedCCs, valueDB) {
        super(nodeId, driver, endpointIndex, deviceClass, supportedCCs, valueDB);
        // Avoid verifying a value change for which we recently received an update
        for (const event of ["value updated", "value removed"]) {
            this.valueDB.on(event, (args) => {
                // Value updates caused by the driver should never cancel a scheduled poll
                if ("source" in args && args.source === "driver")
                    return;
                if (this.cancelScheduledPoll(args, args.newValue)) {
                    this.driver.controllerLog.logNode(this.id, "Scheduled poll canceled because expected value was received", "verbose");
                }
            });
        }
    }
    /**
     * All polls that are currently scheduled for this node
     */
    _scheduledPolls = new ObjectKeyMap();
    hasScheduledPolls() {
        return this._scheduledPolls.size > 0;
    }
    schedulePoll(valueId, options = {}) {
        const { timeoutMs = this.driver.options.timeouts.refreshValue, expectedValue, } = options;
        // Avoid false positives or false negatives due to a mis-formatted value ID
        valueId = normalizeValueID(valueId);
        // Try to retrieve the corresponding CC API
        const endpointInstance = this.getEndpoint(valueId.endpoint || 0);
        if (!endpointInstance)
            return false;
        const api = endpointInstance.commandClasses[valueId.commandClass].withOptions({
            // Verifying a value change after a user-initiated command is required for UI
            // feedback and should not be subject to the poll time requirements for background polling.
            // However we do not want to delay more important communication by polling, so reduce the
            // priority to NodeQuery - the same as for device interviews.
            maxSendAttempts: 1,
            priority: MessagePriority.NodeQuery,
        });
        // Check if the pollValue method is implemented
        if (!api.pollValue)
            return false;
        // make sure there is only one timeout instance per poll
        this.cancelScheduledPoll(valueId);
        const timeout = setTimer(async () => {
            // clean up after the timeout
            this.cancelScheduledPoll(valueId);
            try {
                await api.pollValue.call(api, valueId);
            }
            catch {
                /* ignore */
            }
        }, timeoutMs).unref();
        this._scheduledPolls.set(valueId, { timeout, expectedValue });
        return true;
    }
    cancelScheduledPoll(valueId, actualValue) {
        // Avoid false positives or false negatives due to a mis-formatted value ID
        valueId = normalizeValueID(valueId);
        const poll = this._scheduledPolls.get(valueId);
        if (!poll)
            return false;
        if (actualValue !== undefined
            && poll.expectedValue !== undefined
            && !valueEquals(poll.expectedValue, actualValue)) {
            return false;
        }
        poll.timeout.clear();
        this._scheduledPolls.delete(valueId);
        return true;
    }
    cancelAllScheduledPolls() {
        // Remove queued polls that would interfere with the interview
        for (const valueId of this._scheduledPolls.keys()) {
            this.cancelScheduledPoll(valueId);
        }
    }
}
//# sourceMappingURL=60_ScheduledPoll.js.map