import { WindowCoveringCCGet, WindowCoveringCCReport, WindowCoveringCCSet, WindowCoveringCCStartLevelChange, WindowCoveringCCStopLevelChange, WindowCoveringCCSupportedGet, WindowCoveringCCSupportedReport, } from "@zwave-js/cc/WindowCoveringCC";
import { CommandClasses, Duration } from "@zwave-js/core";
import { setTimer } from "@zwave-js/shared";
import { getTransitionCurrentValue, getTransitionRemainingDuration, startTransition, stopTransition, } from "./MockTransition.js";
const defaultCapabilities = {
    supportedParameters: [],
    travelTime: 5000,
};
const STATE_KEY_PREFIX = "WindowCovering_";
function currentValueKey(param) {
    return `${STATE_KEY_PREFIX}currentValue_${param}`;
}
function transitionKey(param) {
    return `${STATE_KEY_PREFIX}transition_${param}`;
}
/** Stops any running transition for the given parameter and snaps state. */
function stopParameterTransition(self, param) {
    const existing = self.state.get(transitionKey(param));
    if (existing) {
        const value = stopTransition(existing);
        self.state.set(currentValueKey(param), value);
        self.state.delete(transitionKey(param));
        return { wasSupervised: existing.supervised };
    }
}
/**
 * Stops any existing transition for a parameter, then starts a new one.
 * Manages all state reads/writes.
 */
function beginTransition(controller, self, receivedCC, param, targetValue, travelTime, supervised, onCompleteSupervised, onAbortSupervised) {
    stopParameterTransition(self, param);
    const currentValue = self.state.get(currentValueKey(param)) ?? 0;
    const transition = startTransition({
        currentValue,
        targetValue,
        duration: travelTime,
        supervised,
        onComplete: async () => {
            self.state.set(currentValueKey(param), targetValue);
            self.state.delete(transitionKey(param));
            if (supervised) {
                await onCompleteSupervised?.();
                return;
            }
            const cc = new WindowCoveringCCReport({
                nodeId: controller.ownNodeId,
                parameter: param,
                currentValue: targetValue,
                targetValue,
                duration: new Duration(0, "seconds"),
            });
            await self.sendResponse(receivedCC, {
                action: "sendCC",
                cc,
            });
        },
        onAbort: async () => {
            if (!supervised)
                return;
            await onAbortSupervised?.();
        },
    });
    if (transition) {
        self.state.set(transitionKey(param), transition);
        return transition.durationMs;
    }
    else {
        self.state.set(currentValueKey(param), targetValue);
        return 0;
    }
}
const respondToWindowCoveringSupportedGet = {
    handleCC(controller, self, receivedCC) {
        if (receivedCC instanceof WindowCoveringCCSupportedGet) {
            const capabilities = {
                ...defaultCapabilities,
                ...self.getCCCapabilities(CommandClasses["Window Covering"], receivedCC.endpointIndex),
            };
            const cc = new WindowCoveringCCSupportedReport({
                nodeId: controller.ownNodeId,
                supportedParameters: capabilities.supportedParameters,
            });
            return { action: "sendCC", cc };
        }
    },
};
const respondToWindowCoveringGet = {
    handleCC(controller, self, receivedCC) {
        if (receivedCC instanceof WindowCoveringCCGet) {
            const param = receivedCC.parameter;
            const transition = self.state.get(transitionKey(param));
            let currentValue;
            let targetValue;
            let duration;
            if (transition) {
                currentValue = getTransitionCurrentValue(transition);
                targetValue = transition.targetValue;
                duration = getTransitionRemainingDuration(transition);
            }
            else {
                currentValue = self.state.get(currentValueKey(param)) ?? 0;
                targetValue = currentValue;
                duration = new Duration(0, "seconds");
            }
            const cc = new WindowCoveringCCReport({
                nodeId: controller.ownNodeId,
                parameter: param,
                currentValue,
                targetValue,
                duration,
            });
            return { action: "sendCC", cc };
        }
    },
};
const respondToWindowCoveringSet = {
    handleCC(controller, self, receivedCC) {
        if (receivedCC instanceof WindowCoveringCCSet) {
            const capabilities = {
                ...defaultCapabilities,
                ...self.getCCCapabilities(CommandClasses["Window Covering"], receivedCC.endpointIndex),
            };
            const supervised = receivedCC.isEncapsulatedWith(CommandClasses.Supervision);
            const supervisedState = supervised
                ? {
                    activeParams: new Set(),
                    finished: false,
                }
                : undefined;
            let durationMs = 0;
            for (const { parameter, value } of receivedCC.targetValues) {
                const completeSupervised = async () => {
                    if (!supervisedState || supervisedState.finished)
                        return;
                    supervisedState.finished = true;
                    await self.sendResponse(receivedCC, {
                        action: "ok",
                    });
                };
                const abortSupervised = async () => {
                    if (!supervisedState || supervisedState.finished)
                        return;
                    supervisedState.finished = true;
                    supervisedState.activeParams.delete(parameter);
                    for (const otherParam of supervisedState.activeParams) {
                        stopParameterTransition(self, otherParam);
                    }
                    supervisedState.activeParams.clear();
                    await self.sendResponse(receivedCC, {
                        action: "fail",
                    });
                };
                const transitionDurationMs = beginTransition(controller, self, receivedCC, parameter, value, capabilities.travelTime, supervised, completeSupervised, abortSupervised);
                durationMs = Math.max(durationMs, transitionDurationMs);
                if (transitionDurationMs > 0 && supervisedState) {
                    supervisedState.activeParams.add(parameter);
                }
            }
            return {
                action: "ok",
                durationMs,
            };
        }
    },
};
const respondToWindowCoveringStartLevelChange = {
    handleCC(controller, self, receivedCC) {
        if (receivedCC instanceof WindowCoveringCCStartLevelChange) {
            const capabilities = {
                ...defaultCapabilities,
                ...self.getCCCapabilities(CommandClasses["Window Covering"], receivedCC.endpointIndex),
            };
            const targetValue = receivedCC.direction === "up" ? 99 : 0;
            const supervised = receivedCC.isEncapsulatedWith(CommandClasses.Supervision);
            const durationMs = beginTransition(controller, self, receivedCC, receivedCC.parameter, targetValue, capabilities.travelTime, supervised, async () => self.sendResponse(receivedCC, {
                action: "ok",
            }), async () => self.sendResponse(receivedCC, {
                action: "fail",
            }));
            return {
                action: "ok",
                durationMs,
            };
        }
    },
};
const respondToWindowCoveringStopLevelChange = {
    handleCC(controller, self, receivedCC) {
        if (receivedCC instanceof WindowCoveringCCStopLevelChange) {
            const param = receivedCC.parameter;
            const stoppedTransition = stopParameterTransition(self, param);
            const currentValue = (self.state.get(currentValueKey(param)) ?? 0);
            if (!stoppedTransition?.wasSupervised) {
                // Send a delayed report with the final state
                setTimer(async () => {
                    const cc = new WindowCoveringCCReport({
                        nodeId: controller.ownNodeId,
                        parameter: param,
                        currentValue,
                        targetValue: currentValue,
                        duration: new Duration(0, "seconds"),
                    });
                    await self.sendResponse(receivedCC, {
                        action: "sendCC",
                        cc,
                    });
                }, 100).unref();
            }
            return { action: "ok" };
        }
    },
};
export const WindowCoveringCCBehaviors = [
    respondToWindowCoveringSupportedGet,
    respondToWindowCoveringGet,
    respondToWindowCoveringSet,
    respondToWindowCoveringStartLevelChange,
    respondToWindowCoveringStopLevelChange,
];
//# sourceMappingURL=WindowCovering.js.map