"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var MessageCodec_exports = {};
__export(MessageCodec_exports, {
  Message: () => Message,
  MessageCodec: () => MessageCodec,
  SessionType: () => SessionType
});
module.exports = __toCommonJS(MessageCodec_exports);
var import_Mark = require("#common/Mark.js");
var import_general = require("#general");
var import_types = require("#types");
var import_InteractionMessenger = require("../interaction/InteractionMessenger.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
var Message;
((Message2) => {
  function via({ via: via2 }, { packetHeader: { messageId } }) {
    return import_general.Diagnostic.via(`${via2}${import_Mark.Mark.MESSAGE}${import_general.hex.fixed(messageId, 8)}`);
  }
  Message2.via = via;
  function diagnosticsOf(context, message, logContext) {
    const {
      payloadHeader: { messageType, protocolId, ackedMessageId, requiresAck },
      payload
    } = message;
    const duplicate = !!logContext?.duplicate;
    const forInfo = logContext?.for;
    const log = { ...logContext };
    delete log.duplicate;
    delete log.for;
    const { type, for: forType } = mapProtocolAndMessageType(protocolId, messageType);
    return import_general.Diagnostic.dict(
      {
        for: forInfo ?? forType,
        ...log,
        id: Message2.via(context, message),
        type,
        acked: ackedMessageId === void 0 ? void 0 : import_general.hex.fixed(ackedMessageId, 8),
        msgFlags: import_general.Diagnostic.asFlags({
          reqAck: requiresAck,
          dup: duplicate
        }),
        size: payload.byteLength ? payload.byteLength : void 0,
        payload: payload.byteLength ? payload : void 0
      },
      true
    );
  }
  Message2.diagnosticsOf = diagnosticsOf;
})(Message || (Message = {}));
const HEADER_VERSION = 0;
var SessionType = /* @__PURE__ */ ((SessionType2) => {
  SessionType2[SessionType2["Group"] = 1] = "Group";
  SessionType2[SessionType2["Unicast"] = 0] = "Unicast";
  return SessionType2;
})(SessionType || {});
const COMMON_VENDOR_ID = 0;
var PacketHeaderFlag = /* @__PURE__ */ ((PacketHeaderFlag2) => {
  PacketHeaderFlag2[PacketHeaderFlag2["HasDestNodeId"] = 1] = "HasDestNodeId";
  PacketHeaderFlag2[PacketHeaderFlag2["HasDestGroupId"] = 2] = "HasDestGroupId";
  PacketHeaderFlag2[PacketHeaderFlag2["HasSourceNodeId"] = 4] = "HasSourceNodeId";
  PacketHeaderFlag2[PacketHeaderFlag2["Reserved"] = 8] = "Reserved";
  PacketHeaderFlag2[PacketHeaderFlag2["VersionMask"] = 240] = "VersionMask";
  return PacketHeaderFlag2;
})(PacketHeaderFlag || {});
var PayloadHeaderFlag = /* @__PURE__ */ ((PayloadHeaderFlag2) => {
  PayloadHeaderFlag2[PayloadHeaderFlag2["IsInitiatorMessage"] = 1] = "IsInitiatorMessage";
  PayloadHeaderFlag2[PayloadHeaderFlag2["IsAckMessage"] = 2] = "IsAckMessage";
  PayloadHeaderFlag2[PayloadHeaderFlag2["RequiresAck"] = 4] = "RequiresAck";
  PayloadHeaderFlag2[PayloadHeaderFlag2["HasSecureExtension"] = 8] = "HasSecureExtension";
  PayloadHeaderFlag2[PayloadHeaderFlag2["HasVendorId"] = 16] = "HasVendorId";
  return PayloadHeaderFlag2;
})(PayloadHeaderFlag || {});
var SecurityFlag = /* @__PURE__ */ ((SecurityFlag2) => {
  SecurityFlag2[SecurityFlag2["HasPrivacyEnhancements"] = 128] = "HasPrivacyEnhancements";
  SecurityFlag2[SecurityFlag2["IsControlMessage"] = 64] = "IsControlMessage";
  SecurityFlag2[SecurityFlag2["HasMessageExtension"] = 32] = "HasMessageExtension";
  SecurityFlag2[SecurityFlag2["SessionTypeMask"] = 3] = "SessionTypeMask";
  return SecurityFlag2;
})(SecurityFlag || {});
function mapProtocolAndMessageType(protocolId, messageType) {
  const msgTypeHex = import_general.Diagnostic.hex(messageType);
  const type = `${import_general.Diagnostic.hex(protocolId)}/${msgTypeHex}`;
  switch (protocolId) {
    case import_types.SECURE_CHANNEL_PROTOCOL_ID: {
      return { type, for: `SC/${import_types.SecureMessageType[messageType] ?? msgTypeHex}` };
    }
    case import_types.INTERACTION_PROTOCOL_ID: {
      return { type, for: `I/${import_InteractionMessenger.MessageType[messageType] ?? msgTypeHex}` };
    }
    // TODO Add BDX and UDC once we support it
    default:
      return { type };
  }
}
class MessageCodec {
  static decodePacket(data) {
    const reader = new import_general.DataReader(data, import_general.Endian.Little);
    const header = this.decodePacketHeader(reader);
    let messageExtension = void 0;
    if (header.hasMessageExtensions) {
      const extensionLength = reader.readUInt16();
      messageExtension = reader.readByteArray(extensionLength);
    }
    const applicationPayload = reader.remainingBytes;
    return {
      header,
      messageExtension,
      applicationPayload
    };
  }
  static decodePayload({ header, applicationPayload }) {
    const reader = new import_general.DataReader(applicationPayload, import_general.Endian.Little);
    const payloadHeader = this.decodePayloadHeader(reader);
    let securityExtension = void 0;
    if (payloadHeader.hasSecuredExtension) {
      const extensionLength = reader.readUInt16();
      securityExtension = reader.readByteArray(extensionLength);
    }
    if (header.sessionType === 1 /* Group */ && !header.isControlMessage) {
      if (payloadHeader.requiresAck || payloadHeader.ackedMessageId) {
        throw new import_general.UnexpectedDataError(`Group data messages cannot have requiresAck or ackedMessageId set.`);
      }
    }
    return {
      packetHeader: header,
      payloadHeader,
      securityExtension,
      payload: reader.remainingBytes
    };
  }
  static encodePayload({ packetHeader, payloadHeader, payload, securityExtension }) {
    if (securityExtension !== void 0 || payloadHeader.hasSecuredExtension) {
      throw new import_general.NotImplementedError(`Security extensions not supported when encoding a payload.`);
    }
    return {
      header: packetHeader,
      applicationPayload: import_general.Bytes.concat(this.encodePayloadHeader(payloadHeader), payload)
    };
  }
  static encodePacket({ header, applicationPayload, messageExtension }) {
    if (messageExtension !== void 0 || header.hasMessageExtensions) {
      throw new import_general.NotImplementedError(`Message extensions not supported when encoding a packet.`);
    }
    return import_general.Bytes.concat(this.encodePacketHeader(header), applicationPayload);
  }
  static decodePacketHeader(reader) {
    const flags = reader.readUInt8();
    const version = (flags & 240 /* VersionMask */) >> 4;
    const hasDestNodeId = (flags & 1 /* HasDestNodeId */) !== 0;
    const hasDestGroupId = (flags & 2 /* HasDestGroupId */) !== 0;
    const hasSourceNodeId = (flags & 4 /* HasSourceNodeId */) !== 0;
    if (hasDestNodeId && hasDestGroupId)
      throw new import_general.UnexpectedDataError(
        "The header cannot contain destination group and node at the same time. Reserved for future use. Discard message."
      );
    if (version !== HEADER_VERSION) throw new import_general.NotImplementedError(`Unsupported header version ${version}.`);
    const sessionId = reader.readUInt16();
    const securityFlags = reader.readUInt8();
    const messageId = reader.readUInt32();
    const sourceNodeId = hasSourceNodeId ? (0, import_types.NodeId)(reader.readUInt64()) : void 0;
    const destNodeId = hasDestNodeId ? (0, import_types.NodeId)(reader.readUInt64()) : void 0;
    const destGroupId = hasDestGroupId ? (0, import_types.GroupId)(reader.readUInt16()) : void 0;
    const sessionType = securityFlags & 3 /* SessionTypeMask */;
    if (sessionType !== 1 /* Group */ && sessionType !== 0 /* Unicast */) {
      throw new import_general.UnexpectedDataError(`Unsupported session type ${sessionType}.`);
    }
    if (sessionType === 0 /* Unicast */ && hasDestGroupId) {
      throw new import_general.UnexpectedDataError(`Unicast session cannot have destination group id.`);
    }
    if (sessionType === 1 /* Group */) {
      if (!hasDestGroupId && !hasDestNodeId) {
        throw new import_general.UnexpectedDataError(`Group session must have destination group id or destination node id.`);
      }
      if (!hasSourceNodeId) {
        throw new import_general.UnexpectedDataError(`Group session must have source node id.`);
      }
    }
    const hasPrivacyEnhancements = (securityFlags & 128 /* HasPrivacyEnhancements */) !== 0;
    if (hasPrivacyEnhancements) {
      throw new import_general.NotImplementedError(`Privacy enhancements not supported.`);
    }
    const isControlMessage = (securityFlags & 64 /* IsControlMessage */) !== 0;
    if (isControlMessage) {
      throw new import_general.NotImplementedError(`Control Messages not supported.`);
    }
    const hasMessageExtensions = (securityFlags & 32 /* HasMessageExtension */) !== 0;
    return {
      securityFlags,
      sessionId,
      sourceNodeId,
      messageId,
      destGroupId,
      destNodeId,
      sessionType,
      hasPrivacyEnhancements,
      isControlMessage,
      hasMessageExtensions
    };
  }
  static decodePayloadHeader(reader) {
    const exchangeFlags = reader.readUInt8();
    const isInitiatorMessage = (exchangeFlags & 1 /* IsInitiatorMessage */) !== 0;
    const isAckMessage = (exchangeFlags & 2 /* IsAckMessage */) !== 0;
    const requiresAck = (exchangeFlags & 4 /* RequiresAck */) !== 0;
    const hasSecuredExtension = (exchangeFlags & 8 /* HasSecureExtension */) !== 0;
    const hasVendorId = (exchangeFlags & 16 /* HasVendorId */) !== 0;
    const messageType = reader.readUInt8();
    const exchangeId = reader.readUInt16();
    const vendorId = hasVendorId ? reader.readUInt16() : COMMON_VENDOR_ID;
    const protocolId = vendorId << 16 | reader.readUInt16();
    const ackedMessageId = isAckMessage ? reader.readUInt32() : void 0;
    return {
      protocolId,
      exchangeId,
      messageType,
      isInitiatorMessage,
      requiresAck,
      ackedMessageId,
      hasSecuredExtension
    };
  }
  static encodePacketHeader({
    messageId: messageCounter,
    sessionId,
    destGroupId,
    destNodeId,
    sourceNodeId,
    sessionType
  }) {
    if (sessionType === 1 /* Group */ && (destGroupId === void 0 || sourceNodeId === void 0 || destNodeId !== void 0)) {
      throw new import_general.InternalError(`Group session must have destination group id or source node id.`);
    }
    const writer = new import_general.DataWriter(import_general.Endian.Little);
    const flags = HEADER_VERSION << 4 | (destGroupId !== void 0 ? 2 /* HasDestGroupId */ : 0) | (destNodeId !== void 0 ? 1 /* HasDestNodeId */ : 0) | (sourceNodeId !== void 0 ? 4 /* HasSourceNodeId */ : 0);
    const securityFlags = sessionType;
    writer.writeUInt8(flags);
    writer.writeUInt16(sessionId);
    writer.writeUInt8(securityFlags);
    writer.writeUInt32(messageCounter);
    if (sourceNodeId !== void 0) writer.writeUInt64(sourceNodeId);
    if (destNodeId !== void 0) writer.writeUInt64(destNodeId);
    if (destGroupId !== void 0) writer.writeUInt16(destGroupId);
    return writer.toByteArray();
  }
  static encodePayloadHeader({
    exchangeId,
    isInitiatorMessage,
    messageType,
    protocolId,
    requiresAck,
    ackedMessageId: ackedMessageCounter
  }) {
    const writer = new import_general.DataWriter(import_general.Endian.Little);
    const vendorId = (protocolId & 4294901760) >> 16;
    const flags = (isInitiatorMessage ? 1 /* IsInitiatorMessage */ : 0) | (ackedMessageCounter !== void 0 ? 2 /* IsAckMessage */ : 0) | (requiresAck ? 4 /* RequiresAck */ : 0) | (vendorId !== COMMON_VENDOR_ID ? 16 /* HasVendorId */ : 0);
    writer.writeUInt8(flags);
    writer.writeUInt8(messageType);
    writer.writeUInt16(exchangeId);
    if (vendorId !== COMMON_VENDOR_ID) {
      writer.writeUInt32(protocolId);
    } else {
      writer.writeUInt16(protocolId);
    }
    if (ackedMessageCounter !== void 0) writer.writeUInt32(ackedMessageCounter);
    return writer.toByteArray();
  }
}
//# sourceMappingURL=MessageCodec.js.map
