import { BasicCCReport, BasicCCSet, BasicCCValues, } from "@zwave-js/cc";
import { CommandClasses, EncapsulationFlags, ZWaveError, ZWaveErrorCodes, } from "@zwave-js/core";
/**
 * Returns the CC that Basic Set/Report commands should be mapped to
 * based on the endpoint's device class, or undefined if no mapping exists.
 */
export function getBasicMappingTarget(deviceClass) {
    switch (deviceClass?.generic.key) {
        case 0x20: // Binary Sensor
            return CommandClasses["Binary Sensor"];
        case 0x10: // Binary Switch
            return CommandClasses["Binary Switch"];
        case 0x11: // Multilevel Switch
            return CommandClasses["Multilevel Switch"];
        case 0x12: // Remote Switch
            switch (deviceClass.specific.key) {
                case 0x01: // Binary Remote Switch
                    return CommandClasses["Binary Switch"];
                case 0x02: // Multilevel Remote Switch
                    return CommandClasses["Multilevel Switch"];
            }
    }
}
/** Handles the receipt of a BasicCC Set or Report */
export function handleBasicCommand(ctx, node, command) {
    // Retrieve the endpoint the command is coming from
    const sourceEndpoint = node.getEndpoint(command.endpointIndex ?? 0)
        ?? node;
    // Depending on the generic device class, we may need to map the basic command to other CCs
    const mappedTargetCCId = getBasicMappingTarget(sourceEndpoint.deviceClass);
    let mappedTargetCC;
    if (mappedTargetCCId != undefined) {
        mappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(mappedTargetCCId);
    }
    if (command instanceof BasicCCReport) {
        // By default, map Basic CC Reports to a more appropriate CC, unless stated otherwise in a config file
        const basicReportMapping = node.deviceConfig?.compat?.mapBasicReport
            ?? "auto";
        if (basicReportMapping === "Binary Sensor") {
            // Treat the command as a BinarySensorCC Report, regardless of the device class
            mappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(CommandClasses["Binary Sensor"]);
            if (typeof command.currentValue === "number") {
                if (mappedTargetCC) {
                    ctx.logNode(node.id, {
                        endpoint: command.endpointIndex,
                        message: "treating BasicCC::Report as a BinarySensorCC::Report",
                    });
                    mappedTargetCC.setMappedBasicValue(ctx, command.currentValue);
                }
                else {
                    ctx.logNode(node.id, {
                        endpoint: command.endpointIndex,
                        message: "cannot treat BasicCC::Report as a BinarySensorCC::Report, because the Binary Sensor CC is not supported",
                        level: "warn",
                    });
                }
            }
            else {
                ctx.logNode(node.id, {
                    endpoint: command.endpointIndex,
                    message: "cannot map BasicCC::Report to a different CC, because the current value is unknown",
                    level: "warn",
                });
            }
        }
        else if (basicReportMapping === "auto" || basicReportMapping === false) {
            // Try to set the mapped value on the target CC
            const didSetMappedValue = typeof command.currentValue === "number"
                // ... unless forbidden
                && basicReportMapping === "auto"
                && mappedTargetCC?.setMappedBasicValue(ctx, command.currentValue);
            // Otherwise fall back to setting it ourselves
            if (!didSetMappedValue) {
                // Store the value in the value DB now
                command.persistValues(ctx);
            }
        }
    }
    else if (command instanceof BasicCCSet) {
        // By default, map Basic CC Set to Basic CC Report, unless stated otherwise in a config file
        const basicSetMapping = node.deviceConfig?.compat?.mapBasicSet
            ?? "report";
        if (basicSetMapping === "event") {
            // Treat BasicCCSet as value events if desired
            ctx.logNode(node.id, {
                endpoint: command.endpointIndex,
                message: "treating BasicCC::Set as a value event",
            });
            node.valueDB.setValue(BasicCCValues.compatEvent.endpoint(command.endpointIndex), command.targetValue, {
                stateful: false,
            });
        }
        else if (basicSetMapping === "Binary Sensor") {
            // Treat the Set command as a BinarySensorCC Report, regardless of the device class
            mappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(CommandClasses["Binary Sensor"]);
            if (mappedTargetCC) {
                ctx.logNode(node.id, {
                    endpoint: command.endpointIndex,
                    message: "treating BasicCC::Set as a BinarySensorCC::Report",
                });
                mappedTargetCC.setMappedBasicValue(ctx, command.targetValue);
            }
            else {
                ctx.logNode(node.id, {
                    endpoint: command.endpointIndex,
                    message: "cannot treat BasicCC::Set as a BinarySensorCC::Report, because the Binary Sensor CC is not supported",
                    level: "warn",
                });
            }
        }
        else if (!node.deviceConfig?.compat?.mapBasicSet
            && !!(command.encapsulationFlags
                & EncapsulationFlags.Supervision)) {
            // A controller MUST not support Basic CC per the specifications. While we can interpret its contents,
            // we MUST respond to supervised Basic CC Set with "no support".
            // All known devices that use BasicCCSet for reporting send it unsupervised, so this should be safe to do.
            if (command.encapsulationFlags & EncapsulationFlags.Supervision) {
                throw new ZWaveError("Basic CC is not supported", ZWaveErrorCodes.CC_NotSupported);
            }
        }
        else if (basicSetMapping === "auto" || basicSetMapping === "report") {
            // Some devices send their current state using BasicCCSet to their associations
            // instead of using reports. We still interpret them like reports
            ctx.logNode(node.id, {
                endpoint: command.endpointIndex,
                message: "treating BasicCC::Set as a report",
            });
            // In "auto" mode, try to set the mapped value on the target CC first
            const didSetMappedValue = basicSetMapping === "auto"
                && !!mappedTargetCC?.setMappedBasicValue(ctx, command.targetValue);
            // Otherwise handle the command ourselves
            if (!didSetMappedValue) {
                // Basic Set commands cannot store their value automatically, so store the values manually
                node.valueDB.setValue(BasicCCValues.currentValue.endpoint(command.endpointIndex), command.targetValue);
                // Since the node sent us a Basic Set, we are sure that it is at least controlled
                // Add it to the support list, so the information lands in the network cache
                if (!sourceEndpoint.controlsCC(CommandClasses.Basic)) {
                    sourceEndpoint.addCC(CommandClasses.Basic, {
                        isControlled: true,
                    });
                }
            }
        }
    }
}
//# sourceMappingURL=BasicCC.js.map