"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.BuffaloZcl = void 0;
const buffalo_1 = require("../../buffalo");
const utils_1 = require("../../utils/utils");
const datatypes_1 = require("./definition/datatypes");
const enums_1 = require("./definition/enums");
const Utils = __importStar(require("./utils"));
const utils_2 = require("./utils");
const UINT8_NON_VALUE = datatypes_1.ZCL_TYPE_INVALID_BY_TYPE[datatypes_1.ZclType.Uint8];
const UINT16_NON_VALUE = datatypes_1.ZCL_TYPE_INVALID_BY_TYPE[datatypes_1.ZclType.Uint16];
const SEC_KEY_LENGTH = 16;
const EXTENSION_FIELD_SETS_DATA_TYPE = {
    6: [enums_1.DataType.UINT8],
    8: [enums_1.DataType.UINT8],
    258: [enums_1.DataType.UINT8, enums_1.DataType.UINT8],
    768: [enums_1.DataType.UINT16, enums_1.DataType.UINT16, enums_1.DataType.UINT16, enums_1.DataType.UINT8, enums_1.DataType.UINT8, enums_1.DataType.UINT8, enums_1.DataType.UINT16, enums_1.DataType.UINT16],
};
class BuffaloZcl extends buffalo_1.Buffalo {
    readLengthUInt8() {
        const value = this.readUInt8();
        return value === UINT8_NON_VALUE ? Number.NaN : value;
    }
    readLengthUInt16() {
        const value = this.readUInt16();
        return value === UINT16_NON_VALUE ? Number.NaN : value;
    }
    writeOctetStr(value) {
        if (value) {
            this.writeUInt8(value.length);
            this.writeBuffer(value, value.length);
        }
        else {
            this.writeUInt8(UINT8_NON_VALUE);
        }
    }
    readOctetStr() {
        const length = this.readLengthUInt8();
        return Number.isNaN(length) ? Buffer.from([]) : this.readBuffer(length);
    }
    // TODO: support read/write with specific `length` from attribute metadata (CHAR_STR & LONG_CHAR_STR)
    writeCharStr(value) {
        // In case of an empty string, send 0 length, from the spec:
        // "Setting this sub-field to 0x00 represents a character string with no character data (an “empty string”). Setting
        // this sub-field to 0xff represents the non-value. In both cases the character data sub-field has zero length."
        if (value != null) {
            if (typeof value === "string") {
                this.writeUInt8(Buffer.byteLength(value, "utf8"));
                this.writeUtf8String(value);
            }
            else {
                // XXX: value.length not written?
                this.writeBuffer(value, value.length);
            }
        }
        else {
            this.writeUInt8(UINT8_NON_VALUE);
        }
    }
    readCharStr() {
        const length = this.readLengthUInt8();
        return Number.isNaN(length) ? "" : this.readUtf8String(length);
    }
    writeLongOctetStr(value) {
        if (value) {
            this.writeUInt16(value.length);
            this.writeBuffer(value, value.length);
        }
        else {
            this.writeUInt16(UINT16_NON_VALUE);
        }
    }
    readLongOctetStr() {
        const length = this.readLengthUInt16();
        return Number.isNaN(length) ? Buffer.from([]) : this.readBuffer(length);
    }
    writeLongCharStr(value) {
        // In case of an empty string, send 0 length, from the spec:
        // "Setting this sub-field to 0x0000 represents a long character string with no character data (an “empty string”).
        // Setting this sub-field to 0xffff represents an non-value long character string value. In both cases the character
        // data sub-field has zero length"
        if (value != null) {
            this.writeUInt16(Buffer.byteLength(value, "utf8"));
            this.writeUtf8String(value);
        }
        else {
            this.writeUInt16(UINT16_NON_VALUE);
        }
    }
    readLongCharStr() {
        const length = this.readLengthUInt16();
        return Number.isNaN(length) ? "" : this.readUtf8String(length);
    }
    writeArray(value) {
        if (value) {
            const elTypeNumeric = typeof value.elementType === "number" ? value.elementType : enums_1.DataType[value.elementType];
            this.writeUInt8(elTypeNumeric);
            this.writeUInt16(value.elements.length);
            for (const element of value.elements) {
                this.write(elTypeNumeric, element, {});
            }
        }
        else {
            this.writeUInt8(enums_1.DataType.NO_DATA); // XXX: correct value?
            this.writeUInt16(UINT16_NON_VALUE);
        }
    }
    // TODO: support read [] with specific length (`arrayLengthSize` & `arrayLengthField`) from parameter metadata
    readArray() {
        const values = [];
        const elementType = this.readUInt8();
        const numberOfElements = this.readLengthUInt16();
        if (!Number.isNaN(numberOfElements)) {
            // not non-value
            for (let i = 0; i < numberOfElements; i++) {
                const value = this.read(elementType, {});
                values.push(value);
            }
        }
        return values;
    }
    writeStruct(value) {
        if (value) {
            this.writeUInt16(value.length);
            for (const v of value) {
                this.writeUInt8(v.elmType);
                this.write(v.elmType, v.elmVal, {});
            }
        }
        else {
            this.writeUInt16(UINT16_NON_VALUE);
        }
    }
    readStruct() {
        const values = [];
        const numberOfElements = this.readLengthUInt16();
        if (!Number.isNaN(numberOfElements)) {
            // not non-value
            for (let i = 0; i < numberOfElements; i++) {
                const elementType = this.readUInt8();
                const value = this.read(elementType, {});
                values.push({ elmType: elementType, elmVal: value });
            }
        }
        return values;
    }
    writeToD(value) {
        this.writeUInt8(value.hours == null || Number.isNaN(value.hours) ? UINT8_NON_VALUE : value.hours);
        this.writeUInt8(value.minutes == null || Number.isNaN(value.minutes) ? UINT8_NON_VALUE : value.minutes);
        this.writeUInt8(value.seconds == null || Number.isNaN(value.seconds) ? UINT8_NON_VALUE : value.seconds);
        this.writeUInt8(value.hundredths == null || Number.isNaN(value.hundredths) ? UINT8_NON_VALUE : value.hundredths);
    }
    readToD() {
        const hours = this.readLengthUInt8();
        const minutes = this.readLengthUInt8();
        const seconds = this.readLengthUInt8();
        const hundredths = this.readLengthUInt8();
        return {
            hours,
            minutes,
            seconds,
            hundredths,
        };
    }
    writeDate(value) {
        this.writeUInt8(value.year == null || Number.isNaN(value.year) ? UINT8_NON_VALUE : value.year - 1900);
        this.writeUInt8(value.month == null || Number.isNaN(value.month) ? UINT8_NON_VALUE : value.month);
        this.writeUInt8(value.dayOfMonth == null || Number.isNaN(value.dayOfMonth) ? UINT8_NON_VALUE : value.dayOfMonth);
        this.writeUInt8(value.dayOfWeek == null || Number.isNaN(value.dayOfWeek) ? UINT8_NON_VALUE : value.dayOfWeek);
    }
    readDate() {
        const year = this.readLengthUInt8();
        const month = this.readLengthUInt8();
        const dayOfMonth = this.readLengthUInt8();
        const dayOfWeek = this.readLengthUInt8();
        return {
            year: year + 1900, // remains NaN if year is NaN
            month,
            dayOfMonth,
            dayOfWeek,
        };
    }
    //--- BuffaloZclDataType
    writeListZoneInfo(values) {
        for (const value of values) {
            this.writeUInt8(value.zoneID);
            this.writeUInt16(value.zoneStatus);
        }
    }
    readListZoneInfo(length) {
        const value = [];
        for (let i = 0; i < length; i++) {
            value.push({
                zoneID: this.readUInt8(),
                zoneStatus: this.readUInt16(),
            });
        }
        return value;
    }
    writeExtensionFieldSets(values) {
        for (const value of values) {
            this.writeUInt16(value.clstId);
            this.writeUInt8(value.len);
            let index = 0;
            for (const entry of value.extField) {
                this.write(EXTENSION_FIELD_SETS_DATA_TYPE[value.clstId][index], entry, {});
                index++;
            }
        }
    }
    readExtensionFieldSets() {
        const value = [];
        // XXX: doesn't work if buffer has more unrelated fields after this one
        while (this.isMore()) {
            const clstId = this.readUInt16();
            const len = this.readUInt8();
            const end = this.getPosition() + len;
            let index = 0;
            const extField = [];
            while (this.getPosition() < end) {
                extField.push(this.read(EXTENSION_FIELD_SETS_DATA_TYPE[clstId][index], {}));
                index++;
            }
            value.push({ extField, clstId, len });
        }
        return value;
    }
    writeListThermoTransitions(value) {
        for (const entry of value) {
            this.writeUInt16(entry.transitionTime);
            if (entry.heatSetpoint != null) {
                this.writeInt16(entry.heatSetpoint);
            }
            if (entry.coolSetpoint != null) {
                this.writeInt16(entry.coolSetpoint);
            }
        }
    }
    readListThermoTransitions(options) {
        if (options.payload == null || options.payload.mode == null || options.payload.numoftrans == null) {
            throw new Error("Cannot read LIST_THERMO_TRANSITIONS without required payload options specified");
        }
        const heat = options.payload.mode & 1;
        const cool = options.payload.mode & 2;
        const result = [];
        for (let i = 0; i < options.payload.numoftrans; i++) {
            const entry = {
                transitionTime: this.readUInt16(),
            };
            if (heat) {
                entry.heatSetpoint = this.readInt16();
            }
            if (cool) {
                entry.coolSetpoint = this.readInt16();
            }
            result.push(entry);
        }
        return result;
    }
    writeGpdFrame(value) {
        if (value.commandID === 0xf0) {
            // Commissioning Reply
            const v = value;
            const panIDPresent = Boolean(v.options & 0x1);
            const gpdSecurityKeyPresent = Boolean(v.options & 0x2);
            const gpdKeyEncryption = Boolean((v.options >> 2) & 0x1);
            const securityLevel = (v.options >> 3) & 0x3;
            const hasGPDKeyMIC = gpdKeyEncryption && gpdSecurityKeyPresent;
            const hasFrameCounter = gpdSecurityKeyPresent && gpdKeyEncryption && (securityLevel === 0b10 || securityLevel === 0b11);
            this.writeUInt8(1 + (panIDPresent ? 2 : 0) + (gpdSecurityKeyPresent ? 16 : 0) + (hasGPDKeyMIC ? 4 : 0) + (hasFrameCounter ? 4 : 0)); // Length
            this.writeUInt8(v.options);
            if (panIDPresent) {
                // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
                this.writeUInt16(v.panID);
            }
            if (gpdSecurityKeyPresent) {
                // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
                this.writeBuffer(v.securityKey, 16);
            }
            if (hasGPDKeyMIC) {
                // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
                this.writeUInt32(v.keyMic);
            }
            if (hasFrameCounter) {
                // biome-ignore lint/style/noNonNullAssertion: ignored using `--suppress`
                this.writeUInt32(v.frameCounter);
            }
        }
        else if (value.commandID === 0xf3) {
            // Channel configuration
            const v = value;
            this.writeUInt8(1);
            this.writeUInt8((v.operationalChannel & 0xf) | ((v.basic ? 1 : 0) << 4));
        }
        else if (value.commandID === 0xf4 || value.commandID === 0xf5 || (value.commandID >= 0xf7 && value.commandID <= 0xff)) {
            // Other commands sent to GPD
            const v = value;
            this.writeUInt8(v.buffer.length);
            this.writeBuffer(v.buffer, v.buffer.length);
        }
        // 0xf1: Write Attributes
        // 0xf2: Read Attributes
        // 0xf6: ZCL Tunneling
    }
    readGpdFrame(options) {
        if (options.payload?.payloadSize === undefined) {
            throw new Error("Cannot read GPD_FRAME without required payload options specified");
        }
        if (Number.isNaN(options.payload.payloadSize) || options.payload.payloadSize === 0) {
            return {}; // non-value, don't move position
        }
        // ensure offset by options.payload.payloadSize (if any) at end of parsing to not cause issues with spec changes (until supported)
        const startPosition = this.position;
        switch (options.payload.commandID) {
            case 0xe0: {
                // Commisioning
                const frame = {
                    deviceID: this.readUInt8(),
                    options: this.readUInt8(),
                    extendedOptions: 0,
                    securityKey: Buffer.alloc(16),
                    keyMic: 0,
                    outgoingCounter: 0,
                    applicationInfo: 0,
                    manufacturerID: 0,
                    modelID: 0,
                    numGpdCommands: 0,
                    gpdCommandIdList: Buffer.alloc(0),
                    numServerClusters: 0,
                    numClientClusters: 0,
                    gpdServerClusters: Buffer.alloc(0),
                    gpdClientClusters: Buffer.alloc(0),
                    genericSwitchConfig: 0,
                    currentContactStatus: 0,
                };
                if (frame.options & 0x80) {
                    frame.extendedOptions = this.readUInt8();
                }
                if (frame.extendedOptions & 0x20) {
                    frame.securityKey = this.readBuffer(16);
                }
                if (frame.extendedOptions & 0x40) {
                    frame.keyMic = this.readUInt32();
                }
                if (frame.extendedOptions & 0x80) {
                    frame.outgoingCounter = this.readUInt32();
                }
                if (frame.options & 0x04) {
                    frame.applicationInfo = this.readUInt8();
                }
                if (frame.applicationInfo & 0x01) {
                    frame.manufacturerID = this.readUInt16();
                }
                if (frame.applicationInfo & 0x02) {
                    frame.modelID = this.readUInt16();
                }
                if (frame.applicationInfo & 0x04) {
                    frame.numGpdCommands = this.readUInt8();
                    frame.gpdCommandIdList = this.readBuffer(frame.numGpdCommands);
                }
                if (frame.applicationInfo & 0x08) {
                    const len = this.readUInt8();
                    frame.numServerClusters = len & 0xf;
                    frame.numClientClusters = (len >> 4) & 0xf;
                    frame.gpdServerClusters = this.readBuffer(2 * frame.numServerClusters);
                    frame.gpdClientClusters = this.readBuffer(2 * frame.numClientClusters);
                }
                if (frame.applicationInfo & 0x10) {
                    const len = this.readUInt8();
                    if (len >= 1) {
                        frame.genericSwitchConfig = this.readUInt8();
                    }
                    if (len >= 2) {
                        frame.currentContactStatus = this.readUInt8();
                    }
                }
                this.setPosition(startPosition + options.payload.payloadSize);
                return frame;
            }
            case 0xe3: {
                // Channel Request
                const channelOpts = this.readUInt8();
                this.setPosition(startPosition + options.payload.payloadSize);
                return {
                    nextChannel: channelOpts & 0xf,
                    nextNextChannel: channelOpts >> 4,
                };
            }
            case 0xa0: {
                // Attribute Reporting
                const clusterID = this.readUInt16();
                const cluster = Utils.getCluster(clusterID, undefined, {});
                const frame = {
                    clusterID,
                    clusterName: cluster.name,
                    attributes: {},
                };
                while (this.position - startPosition < options.payload.payloadSize) {
                    const attributeId = this.readUInt16();
                    const dataType = this.readUInt8();
                    const attributeNameOrId = (0, utils_2.getClusterAttribute)(cluster, attributeId, undefined)?.name ?? attributeId;
                    frame.attributes[attributeNameOrId] = this.read(dataType, options);
                }
                this.setPosition(startPosition + options.payload.payloadSize);
                return frame;
            }
            case 0xa1: {
                // Manufacturer-specific Attribute Reporting
                const manufacturerCode = this.readUInt16();
                const clusterID = this.readUInt16();
                const cluster = Utils.getCluster(clusterID, manufacturerCode, {});
                const frame = {
                    manufacturerCode,
                    clusterID,
                    clusterName: cluster.name,
                    attributes: {},
                };
                while (this.position - startPosition < options.payload.payloadSize) {
                    const attributeId = this.readUInt16();
                    const dataType = this.readUInt8();
                    // many times will fallback to ID since lots of unknown manu-specific attributes (handled in ZHC)
                    const attributeNameOrId = (0, utils_2.getClusterAttribute)(cluster, attributeId, manufacturerCode)?.name ?? attributeId;
                    frame.attributes[attributeNameOrId] = this.read(dataType, options);
                }
                this.setPosition(startPosition + options.payload.payloadSize);
                return frame;
            }
            case 0xa2: {
                // Multi-Cluster Reporting
                const frame = { reports: [] };
                while (this.position - startPosition < options.payload.payloadSize) {
                    const clusterID = this.readUInt16();
                    const cluster = Utils.getCluster(clusterID, undefined, {});
                    const attributeId = this.readUInt16();
                    const dataType = this.readUInt8();
                    const attributeNameOrId = (0, utils_2.getClusterAttribute)(cluster, attributeId, undefined)?.name ?? attributeId;
                    const attrData = this.read(dataType, options);
                    const report = {
                        clusterID,
                        clusterName: cluster.name,
                        attribute: attributeNameOrId,
                        attrData,
                    };
                    frame.reports.push(report);
                }
                this.setPosition(startPosition + options.payload.payloadSize);
                return frame;
            }
            case 0xa3: {
                // Manufacturer-specific Multi-Cluster Reporting
                const manufacturerCode = this.readUInt16();
                const frame = { manufacturerCode, reports: [] };
                while (this.position - startPosition < options.payload.payloadSize) {
                    const clusterID = this.readUInt16();
                    const cluster = Utils.getCluster(clusterID, manufacturerCode, {});
                    const attributeId = this.readUInt16();
                    const dataType = this.readUInt8();
                    // many times will fallback to ID since lots of unknown manu-specific attributes (handled in ZHC)
                    const attributeNameOrId = (0, utils_2.getClusterAttribute)(cluster, attributeId, manufacturerCode)?.name ?? attributeId;
                    const attrData = this.read(dataType, options);
                    const report = {
                        clusterID,
                        clusterName: cluster.name,
                        attribute: attributeNameOrId,
                        attrData,
                    };
                    frame.reports.push(report);
                }
                this.setPosition(startPosition + options.payload.payloadSize);
                return frame;
            }
            case 0xa4: {
                // Request Attributes
                const frame = { options: this.readUInt8(), manufacturerID: undefined, records: [] };
                // ignore multi-record bit, while loop automatically handles it
                if (frame.options & 0x02) {
                    frame.manufacturerID = this.readUInt16();
                }
                while (this.position - startPosition < options.payload.payloadSize) {
                    const clusterID = this.readUInt16();
                    // payload contains size in octets
                    const recordsLength = this.readUInt8() / 2;
                    const attributes = this.readListUInt16(recordsLength);
                    frame.records.push({ clusterID, attributes });
                }
                this.setPosition(startPosition + options.payload.payloadSize);
                return frame;
            }
            case 0xa5: {
                // Read Attributes Response
                const frame = { options: this.readUInt8(), manufacturerID: undefined, records: [] };
                // ignore multi-record bit, while loop automatically handles it
                if (frame.options & 0x02) {
                    frame.manufacturerID = this.readUInt16();
                }
                while (this.position - startPosition < options.payload.payloadSize) {
                    const clusterID = this.readUInt16();
                    const cluster = Utils.getCluster(clusterID, frame.manufacturerID, {});
                    // payload contains size in octets
                    const recordsLength = this.readUInt8();
                    const record = { clusterID, clusterName: cluster.name, attributes: {} };
                    let readLength = 0;
                    while (readLength < recordsLength) {
                        const startPos = this.getPosition();
                        const attributeId = this.readUInt16();
                        const status = this.readUInt8();
                        const dataType = this.readUInt8();
                        const attributeNameOrId = (0, utils_2.getClusterAttribute)(cluster, attributeId, frame.manufacturerID)?.name ?? attributeId;
                        record.attributes[attributeNameOrId] = {
                            status,
                            attrData: this.read(dataType, options),
                        };
                        readLength += this.getPosition() - startPos;
                    }
                    frame.records.push(record);
                }
                this.setPosition(startPosition + options.payload.payloadSize);
                return frame;
            }
        }
        // might contain `gppNwkAddr`, `gppGpdLink` & `mic` from ZCL cluster after this, so limit by `payloadSize`
        return { raw: this.readBuffer(options.payload.payloadSize) };
    }
    writeStructuredSelector(value) {
        if (value != null) {
            const indexes = value.indexes || [];
            const indicatorType = value.indicatorType || enums_1.StructuredIndicatorType.Whole;
            const indicator = indexes.length + indicatorType;
            this.writeUInt8(indicator);
            for (const index of indexes) {
                this.writeUInt16(index);
            }
        }
    }
    readStructuredSelector() {
        /** [0-15] range */
        const indicator = this.readUInt8();
        if (indicator === 0) {
            // no indexes, whole attribute value is to be read
            return { indicatorType: enums_1.StructuredIndicatorType.Whole };
        }
        if (indicator < enums_1.StructuredIndicatorType.WriteAdd) {
            const indexes = [];
            for (let i = 0; i < indicator; i++) {
                const index = this.readUInt16();
                indexes.push(index);
            }
            return { indexes };
        }
        throw new Error("Read structured selector was outside [0-15] range.");
    }
    writeListTuyaDataPointValues(dpValues) {
        for (const dpValue of dpValues) {
            this.writeUInt8(dpValue.dp);
            this.writeUInt8(dpValue.datatype);
            const dataLen = dpValue.data.length;
            // UInt16BE
            this.writeUInt8((dataLen >> 8) & 0xff);
            this.writeUInt8(dataLen & 0xff);
            this.writeBuffer(dpValue.data, dataLen);
        }
    }
    readListTuyaDataPointValues() {
        const value = [];
        // XXX: doesn't work if buffer has more unrelated fields after this one
        while (this.isMore()) {
            try {
                const dp = this.readUInt8();
                const datatype = this.readUInt8();
                const len_hi = this.readUInt8();
                const len_lo = this.readUInt8();
                const data = this.readBuffer(len_lo + (len_hi << 8));
                value.push({ dp, datatype, data });
            }
            catch {
                break;
            }
        }
        return value;
    }
    writeListMiboxerZones(values) {
        this.writeUInt8(values.length);
        for (const value of values) {
            this.writeUInt16(value.groupId);
            this.writeUInt8(value.zoneNum);
        }
    }
    readListMiboxerZones() {
        const value = [];
        const len = this.readUInt8();
        for (let i = 0; i < len; i++) {
            const groupId = this.readUInt16();
            const zoneNum = this.readUInt8();
            value.push({ groupId, zoneNum });
        }
        return value;
    }
    writeBigEndianUInt24(value) {
        this.position = this.buffer.writeUIntBE(value, this.position, 3);
    }
    readBigEndianUInt24() {
        const value = this.buffer.readUIntBE(this.position, 3);
        this.position += 3;
        return value;
    }
    // NOTE: writeMiStruct is not supported.
    readMiStruct() {
        const length = this.readUInt8();
        const value = {};
        if (length === UINT8_NON_VALUE) {
            return value;
        }
        for (let i = 0; i < length; i++) {
            const index = this.readUInt8();
            const dataType = this.readUInt8();
            value[index] = this.read(dataType, {});
            const remaining = this.buffer.length - this.position;
            if (remaining <= 1) {
                if (remaining === 1) {
                    // Some Xiaomi structs have a trailing byte, skip it.
                    this.position += 1;
                }
                break;
            }
        }
        return value;
    }
    // biome-ignore lint/suspicious/noExplicitAny: API
    write(type, value, options) {
        switch (type) {
            case enums_1.DataType.NO_DATA:
            case enums_1.DataType.UNKNOWN: {
                return; // nothing to write
            }
            case enums_1.DataType.BOOLEAN:
            case enums_1.DataType.DATA8:
            case enums_1.DataType.BITMAP8:
            case enums_1.DataType.UINT8:
            case enums_1.DataType.ENUM8: {
                this.writeUInt8(value);
                break;
            }
            case enums_1.DataType.DATA16:
            case enums_1.DataType.BITMAP16:
            case enums_1.DataType.UINT16:
            case enums_1.DataType.ENUM16:
            case enums_1.DataType.CLUSTER_ID:
            case enums_1.DataType.ATTR_ID: {
                this.writeUInt16(value);
                break;
            }
            case enums_1.DataType.DATA24:
            case enums_1.DataType.BITMAP24:
            case enums_1.DataType.UINT24: {
                this.writeUInt24(value);
                break;
            }
            case enums_1.DataType.DATA32:
            case enums_1.DataType.BITMAP32:
            case enums_1.DataType.UINT32:
            case enums_1.DataType.UTC:
            case enums_1.DataType.BAC_OID: {
                this.writeUInt32(value);
                break;
            }
            case enums_1.DataType.DATA40:
            case enums_1.DataType.BITMAP40:
            case enums_1.DataType.UINT40: {
                this.writeUInt40(value);
                break;
            }
            case enums_1.DataType.DATA48:
            case enums_1.DataType.BITMAP48:
            case enums_1.DataType.UINT48: {
                this.writeUInt48(value);
                break;
            }
            case enums_1.DataType.DATA56:
            case enums_1.DataType.BITMAP56:
            case enums_1.DataType.UINT56: {
                this.writeUInt56(value);
                break;
            }
            case enums_1.DataType.DATA64:
            case enums_1.DataType.BITMAP64:
            case enums_1.DataType.UINT64: {
                this.writeUInt64(value);
                break;
            }
            case enums_1.DataType.INT8: {
                this.writeInt8(value);
                break;
            }
            case enums_1.DataType.INT16: {
                this.writeInt16(value);
                break;
            }
            case enums_1.DataType.INT24: {
                this.writeInt24(value);
                break;
            }
            case enums_1.DataType.INT32: {
                this.writeInt32(value);
                break;
            }
            case enums_1.DataType.INT40: {
                this.writeInt40(value);
                break;
            }
            case enums_1.DataType.INT48: {
                this.writeInt48(value);
                break;
            }
            case enums_1.DataType.INT56: {
                this.writeInt56(value);
                break;
            }
            case enums_1.DataType.INT64: {
                this.writeInt64(value);
                break;
            }
            // case DataType.SEMI_PREC: {
            //     // https://tc39.es/proposal-float16array/
            //     // not currently used
            //     this.writeSemiFloatLE(value);
            //     break;
            // }
            case enums_1.DataType.SINGLE_PREC: {
                this.writeFloatLE(value);
                break;
            }
            case enums_1.DataType.DOUBLE_PREC: {
                this.writeDoubleLE(value);
                break;
            }
            case enums_1.DataType.OCTET_STR: {
                this.writeOctetStr(value);
                break;
            }
            case enums_1.DataType.CHAR_STR: {
                this.writeCharStr(value);
                break;
            }
            case enums_1.DataType.LONG_OCTET_STR: {
                this.writeLongOctetStr(value);
                break;
            }
            case enums_1.DataType.LONG_CHAR_STR: {
                this.writeLongCharStr(value);
                break;
            }
            case enums_1.DataType.ARRAY:
            case enums_1.DataType.SET:
            case enums_1.DataType.BAG: {
                this.writeArray(value);
                break;
            }
            case enums_1.DataType.STRUCT: {
                this.writeStruct(value);
                break;
            }
            case enums_1.DataType.TOD: {
                this.writeToD(value);
                break;
            }
            case enums_1.DataType.DATE: {
                this.writeDate(value);
                break;
            }
            case enums_1.DataType.IEEE_ADDR: {
                this.writeIeeeAddr(value);
                break;
            }
            case enums_1.DataType.SEC_KEY: {
                this.writeBuffer(value, SEC_KEY_LENGTH);
                break;
            }
            case enums_1.BuffaloZclDataType.USE_DATA_TYPE: {
                if (options.dataType == null) {
                    if (Buffer.isBuffer(value) || (0, utils_1.isNumberArray)(value)) {
                        this.writeBuffer(value, value.length);
                        break;
                    }
                    throw new Error("Cannot write USE_DATA_TYPE without dataType option specified");
                }
                this.write(options.dataType, value, options);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_UINT8: {
                this.writeListUInt8(value);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_UINT16: {
                this.writeListUInt16(value);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_UINT24: {
                this.writeListUInt24(value);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_UINT32: {
                this.writeListUInt32(value);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_ZONEINFO: {
                this.writeListZoneInfo(value);
                break;
            }
            case enums_1.BuffaloZclDataType.EXTENSION_FIELD_SETS: {
                this.writeExtensionFieldSets(value);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_THERMO_TRANSITIONS: {
                this.writeListThermoTransitions(value);
                break;
            }
            case enums_1.BuffaloZclDataType.BUFFER: {
                // XXX: inconsistent with read that allows partial with options.length, here always "whole"
                this.writeBuffer(value, value.length);
                break;
            }
            case enums_1.BuffaloZclDataType.GPD_FRAME: {
                this.writeGpdFrame(value);
                break;
            }
            case enums_1.BuffaloZclDataType.STRUCTURED_SELECTOR: {
                this.writeStructuredSelector(value);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_TUYA_DATAPOINT_VALUES: {
                this.writeListTuyaDataPointValues(value);
                break;
            }
            case enums_1.BuffaloZclDataType.LIST_MIBOXER_ZONES: {
                this.writeListMiboxerZones(value);
                break;
            }
            case enums_1.BuffaloZclDataType.BIG_ENDIAN_UINT24: {
                this.writeBigEndianUInt24(value);
                break;
            }
            default: {
                // In case the type is undefined, write it as a buffer to easily allow for custom types
                // e.g. for https://github.com/Koenkk/zigbee-herdsman/issues/127
                if (Buffer.isBuffer(value) || (0, utils_1.isNumberArray)(value)) {
                    this.writeBuffer(value, value.length);
                    break;
                }
                throw new Error(`Write for '${type}' not available`);
            }
        }
    }
    // biome-ignore lint/suspicious/noExplicitAny: API
    read(type, options) {
        switch (type) {
            case enums_1.DataType.NO_DATA:
            case enums_1.DataType.UNKNOWN: {
                return; // nothing to write
            }
            case enums_1.DataType.BOOLEAN:
            case enums_1.DataType.DATA8:
            case enums_1.DataType.BITMAP8:
            case enums_1.DataType.UINT8:
            case enums_1.DataType.ENUM8: {
                return this.readUInt8();
            }
            case enums_1.DataType.DATA16:
            case enums_1.DataType.BITMAP16:
            case enums_1.DataType.UINT16:
            case enums_1.DataType.ENUM16:
            case enums_1.DataType.CLUSTER_ID:
            case enums_1.DataType.ATTR_ID: {
                return this.readUInt16();
            }
            case enums_1.DataType.DATA24:
            case enums_1.DataType.BITMAP24:
            case enums_1.DataType.UINT24: {
                return this.readUInt24();
            }
            case enums_1.DataType.DATA32:
            case enums_1.DataType.BITMAP32:
            case enums_1.DataType.UINT32:
            case enums_1.DataType.UTC:
            case enums_1.DataType.BAC_OID: {
                return this.readUInt32();
            }
            case enums_1.DataType.DATA40:
            case enums_1.DataType.BITMAP40:
            case enums_1.DataType.UINT40: {
                return this.readUInt40();
            }
            case enums_1.DataType.DATA48:
            case enums_1.DataType.BITMAP48:
            case enums_1.DataType.UINT48: {
                return this.readUInt48();
            }
            case enums_1.DataType.DATA56:
            case enums_1.DataType.BITMAP56:
            case enums_1.DataType.UINT56: {
                return this.readUInt56();
            }
            case enums_1.DataType.DATA64:
            case enums_1.DataType.BITMAP64:
            case enums_1.DataType.UINT64: {
                return this.readUInt64();
            }
            case enums_1.DataType.INT8: {
                return this.readInt8();
            }
            case enums_1.DataType.INT16: {
                return this.readInt16();
            }
            case enums_1.DataType.INT24: {
                return this.readInt24();
            }
            case enums_1.DataType.INT32: {
                return this.readInt32();
            }
            case enums_1.DataType.INT40: {
                return this.readInt40();
            }
            case enums_1.DataType.INT48: {
                return this.readInt48();
            }
            case enums_1.DataType.INT56: {
                return this.readInt56();
            }
            case enums_1.DataType.INT64: {
                return this.readInt64();
            }
            // case DataType.SEMI_PREC: {
            //     // https://tc39.es/proposal-float16array/
            //     // not currently used
            //     return this.readSemiFloatLE();
            // }
            case enums_1.DataType.SINGLE_PREC: {
                return this.readFloatLE();
            }
            case enums_1.DataType.DOUBLE_PREC: {
                return this.readDoubleLE();
            }
            case enums_1.DataType.OCTET_STR: {
                return this.readOctetStr();
            }
            case enums_1.DataType.CHAR_STR: {
                return this.readCharStr();
            }
            case enums_1.DataType.LONG_OCTET_STR: {
                return this.readLongOctetStr();
            }
            case enums_1.DataType.LONG_CHAR_STR: {
                return this.readLongCharStr();
            }
            case enums_1.DataType.ARRAY:
            case enums_1.DataType.SET:
            case enums_1.DataType.BAG: {
                return this.readArray();
            }
            case enums_1.DataType.STRUCT: {
                return this.readStruct();
            }
            case enums_1.DataType.TOD: {
                return this.readToD();
            }
            case enums_1.DataType.DATE: {
                return this.readDate();
            }
            case enums_1.DataType.IEEE_ADDR: {
                return this.readIeeeAddr();
            }
            case enums_1.DataType.SEC_KEY: {
                return this.readBuffer(SEC_KEY_LENGTH);
            }
            case enums_1.BuffaloZclDataType.USE_DATA_TYPE: {
                if (options.dataType == null) {
                    return this.readBuffer(options.length ?? this.buffer.length);
                }
                return this.read(options.dataType, options);
            }
            case enums_1.BuffaloZclDataType.LIST_UINT8: {
                if (options.length == null) {
                    throw new Error("Cannot read LIST_UINT8 without length option specified");
                }
                return this.readListUInt8(options.length);
            }
            case enums_1.BuffaloZclDataType.LIST_UINT16: {
                if (options.length == null) {
                    throw new Error("Cannot read LIST_UINT16 without length option specified");
                }
                return this.readListUInt16(options.length);
            }
            case enums_1.BuffaloZclDataType.LIST_UINT24: {
                if (options.length == null) {
                    throw new Error("Cannot read LIST_UINT24 without length option specified");
                }
                return this.readListUInt24(options.length);
            }
            case enums_1.BuffaloZclDataType.LIST_UINT32: {
                if (options.length == null) {
                    throw new Error("Cannot read LIST_UINT32 without length option specified");
                }
                return this.readListUInt32(options.length);
            }
            case enums_1.BuffaloZclDataType.LIST_ZONEINFO: {
                if (options.length == null) {
                    throw new Error("Cannot read LIST_ZONEINFO without length option specified");
                }
                return this.readListZoneInfo(options.length);
            }
            case enums_1.BuffaloZclDataType.EXTENSION_FIELD_SETS: {
                return this.readExtensionFieldSets();
            }
            case enums_1.BuffaloZclDataType.LIST_THERMO_TRANSITIONS: {
                return this.readListThermoTransitions(options);
            }
            case enums_1.BuffaloZclDataType.BUFFER: {
                // if length option not specified, read the whole buffer
                return this.readBuffer(options.length ?? this.buffer.length);
            }
            case enums_1.BuffaloZclDataType.GPD_FRAME: {
                return this.readGpdFrame(options);
            }
            case enums_1.BuffaloZclDataType.STRUCTURED_SELECTOR: {
                return this.readStructuredSelector();
            }
            case enums_1.BuffaloZclDataType.LIST_TUYA_DATAPOINT_VALUES: {
                return this.readListTuyaDataPointValues();
            }
            case enums_1.BuffaloZclDataType.LIST_MIBOXER_ZONES: {
                return this.readListMiboxerZones();
            }
            case enums_1.BuffaloZclDataType.BIG_ENDIAN_UINT24: {
                return this.readBigEndianUInt24();
            }
            case enums_1.BuffaloZclDataType.MI_STRUCT: {
                return this.readMiStruct();
            }
        }
        throw new Error(`Read for '${type}' not available`);
    }
}
exports.BuffaloZcl = BuffaloZcl;
//# sourceMappingURL=buffaloZcl.js.map