"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 TlvObject_exports = {};
__export(TlvObject_exports, {
  ObjectSchema: () => ObjectSchema,
  ObjectSchemaWithMaxSize: () => ObjectSchemaWithMaxSize,
  TlvField: () => TlvField,
  TlvObject: () => TlvObject,
  TlvObjectWithMaxSize: () => TlvObjectWithMaxSize,
  TlvOptionalField: () => TlvOptionalField,
  TlvOptionalRepeatedField: () => TlvOptionalRepeatedField,
  TlvRepeatedField: () => TlvRepeatedField,
  TlvTaggedList: () => TlvTaggedList
});
module.exports = __toCommonJS(TlvObject_exports);
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_ValidationError = require("../common/ValidationError.js");
var import_TlvAny = require("./TlvAny.js");
var import_TlvCodec = require("./TlvCodec.js");
var import_TlvSchema = require("./TlvSchema.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
class ObjectSchema extends import_TlvSchema.TlvSchema {
  constructor(fieldDefinitions, type = import_TlvCodec.TlvType.Structure, allowProtocolSpecificTags = false) {
    super();
    this.fieldDefinitions = fieldDefinitions;
    this.type = type;
    this.allowProtocolSpecificTags = allowProtocolSpecificTags;
    let isFabricScoped = false;
    for (const name in this.fieldDefinitions) {
      const field = this.fieldDefinitions[name];
      if (field.repeated && type !== import_TlvCodec.TlvType.List) {
        throw new Error("Repeated fields are only allowed in TLV List.");
      }
      this.fieldById[field.id] = { name, field };
      if (field.id === import_model.FabricIndex.id) {
        isFabricScoped = true;
      }
    }
    this.isFabricScoped = isFabricScoped;
  }
  isFabricScoped;
  fieldById = new Array();
  #encodeEntryToTlv(writer, name, value, options) {
    const { id, schema, optional: isOptional, repeated: isRepeated } = this.fieldDefinitions[name];
    const { forWriteInteraction = false, allowMissingFieldsForNonFabricFilteredRead = false } = options ?? {};
    if (forWriteInteraction && allowMissingFieldsForNonFabricFilteredRead) {
      throw new import_general.InternalError(
        "Encode options cannot indicate a write interaction and a fabric filtered read interaction at the same time."
      );
    }
    const fieldValue = value[name];
    if (fieldValue === void 0) {
      if (!isOptional && !allowMissingFieldsForNonFabricFilteredRead) {
        if (forWriteInteraction && id === import_model.FabricIndex.id) {
          return;
        }
        throw new import_ValidationError.ValidationMandatoryFieldMissingError(`Missing mandatory field ${name}`, name);
      }
      return;
    }
    if (isRepeated) {
      if (!Array.isArray(fieldValue)) {
        throw new import_ValidationError.ValidationDatatypeMismatchError(`Repeated field ${name} should be an array.`, name);
      }
      for (const element of fieldValue) {
        schema.encodeTlvInternal(writer, element, { id }, options);
      }
    } else {
      schema.encodeTlvInternal(writer, fieldValue, { id }, options);
    }
  }
  /**
   * Encode the object as Structure, by the order of field definitions.
   */
  #encodeStructure(writer, value, options) {
    for (const name in this.fieldDefinitions) {
      this.#encodeEntryToTlv(writer, name, value, options);
    }
  }
  /**
   * Encode the object as List, by the order of the fields in the object.
   */
  #encodeList(writer, value, options) {
    const encodedFields = /* @__PURE__ */ new Set();
    for (const name of Object.keys(value)) {
      this.#encodeEntryToTlv(writer, name, value, options);
      encodedFields.add(name);
    }
    for (const name in this.fieldDefinitions) {
      if (encodedFields.has(name)) continue;
      this.#encodeEntryToTlv(writer, name, value, options);
    }
  }
  encodeTlvInternal(writer, value, tag, options) {
    writer.writeTag({ type: this.type }, tag);
    if (this.type === import_TlvCodec.TlvType.Structure) {
      this.#encodeStructure(writer, value, options);
    } else {
      this.#encodeList(writer, value, options);
    }
    writer.writeTag({ type: import_TlvCodec.TlvType.EndOfContainer });
  }
  decodeTlvInternalValue(reader, typeLength) {
    if (typeLength.type !== this.type)
      throw new import_general.UnexpectedDataError(`Unexpected type ${typeLength.type} (expected ${this.type}).`);
    const result = {};
    while (true) {
      const { tag: { profile, id } = {}, typeLength: elementTypeLength } = reader.readTagType();
      if (elementTypeLength.type === import_TlvCodec.TlvType.EndOfContainer) break;
      if (profile !== void 0 && !this.allowProtocolSpecificTags)
        throw new import_general.UnexpectedDataError("Structure element tags should be context-specific.");
      if (id === void 0) throw new import_general.UnexpectedDataError("Structure element tags should have an id.");
      const fieldName = this.fieldById[id];
      if (fieldName === void 0) {
        import_TlvAny.TlvAny.decodeTlvInternalValue(reader, elementTypeLength);
        continue;
      }
      const { field, name } = fieldName;
      const decoded = field.schema.decodeTlvInternalValue(reader, elementTypeLength);
      if (field.repeated) {
        if (result[name] === void 0) {
          result[name] = [decoded];
        } else {
          result[name].push(decoded);
        }
      } else {
        result[name] = decoded;
      }
    }
    for (const name in this.fieldDefinitions) {
      const { optional, fallback, repeated } = this.fieldDefinitions[name];
      if (optional) continue;
      const value = result[name];
      if (value !== void 0) continue;
      if (fallback !== void 0) {
        if (repeated) {
          result[name] = [fallback];
        } else {
          result[name] = fallback;
        }
      }
    }
    return result;
  }
  validate(value) {
    for (const name in this.fieldDefinitions) {
      const { optional, schema, repeated: isRepeated } = this.fieldDefinitions[name];
      const data = value[name];
      if (data === void 0) {
        if (optional) {
          continue;
        }
        throw new import_ValidationError.ValidationMandatoryFieldMissingError(`Missing mandatory field ${name}`, name);
      }
      if (isRepeated) {
        const { minLength = 2, maxLength = 65535 } = this.fieldDefinitions[name];
        if (!Array.isArray(data)) {
          throw new import_ValidationError.ValidationDatatypeMismatchError(`Repeated field ${name} should be an array.`, name);
        }
        if (data.length > maxLength)
          throw new import_ValidationError.ValidationOutOfBoundsError(
            `Repeated field list for ${name} is too long: ${data.length}, max ${maxLength}.`,
            name
          );
        if (data.length < minLength)
          throw new import_ValidationError.ValidationOutOfBoundsError(
            `Repeated field list for ${name} is too short: ${data.length}, min ${minLength}.`,
            name
          );
        for (const element of data) {
          try {
            schema.validate(element);
          } catch (e) {
            import_ValidationError.ValidationError.accept(e);
            e.fieldName = `${name}${e.fieldName !== void 0 ? `.${e.fieldName}` : ""}`;
            throw e;
          }
        }
      } else {
        try {
          schema.validate(data);
        } catch (e) {
          import_ValidationError.ValidationError.accept(e);
          e.fieldName = `${name}${e.fieldName !== void 0 ? `.${e.fieldName}` : ""}`;
          throw e;
        }
      }
    }
  }
  injectField(value, fieldId, fieldValue, injectChecker) {
    for (const k in this.fieldDefinitions) {
      const field = this.fieldDefinitions[k];
      if (field.id === fieldId) {
        if (injectChecker(value[k])) {
          field.schema.validate(fieldValue);
          value[k] = fieldValue;
        }
      } else {
        value[k] = field.schema.injectField(value[k], fieldId, fieldValue, injectChecker);
      }
    }
    return value;
  }
  removeField(value, fieldId, removeChecker) {
    for (const k in this.fieldDefinitions) {
      const field = this.fieldDefinitions[k];
      if (field.id === fieldId) {
        if (value[k] !== void 0 && removeChecker(value[k])) {
          delete value[k];
        }
      } else {
        value[k] = field.schema.removeField(value[k], fieldId, removeChecker);
      }
    }
    return value;
  }
}
const TlvObject = (fields) => new ObjectSchema(fields, import_TlvCodec.TlvType.Structure);
class ObjectSchemaWithMaxSize extends ObjectSchema {
  constructor(fieldDefinitions, maxSize, type = import_TlvCodec.TlvType.Structure, allowProtocolSpecificTags = false) {
    super(fieldDefinitions, type, allowProtocolSpecificTags);
    this.maxSize = maxSize;
  }
  encode(value) {
    const encoded = super.encode(value);
    if (encoded.byteLength > this.maxSize) {
      throw new import_general.ImplementationError(
        `Encoded TLV object with ${encoded.byteLength} bytes exceeds maximum size of ${this.maxSize} bytes.`
      );
    }
    return encoded;
  }
}
const TlvObjectWithMaxSize = (fields, maxSize) => new ObjectSchemaWithMaxSize(fields, maxSize, import_TlvCodec.TlvType.Structure);
const TlvTaggedList = (fields, allowProtocolSpecificTags = false) => new ObjectSchema(fields, import_TlvCodec.TlvType.List, allowProtocolSpecificTags);
const TlvField = (id, schema, fallback) => ({ id, schema, fallback, optional: false });
const TlvOptionalField = (id, schema) => ({ id, schema, optional: true });
const TlvRepeatedField = (id, schema, lengthOptions) => {
  const { minLength, maxLength, length } = lengthOptions ?? {};
  return {
    id,
    schema,
    optional: false,
    repeated: true,
    minLength: length ?? minLength,
    maxLength: length ?? maxLength
  };
};
const TlvOptionalRepeatedField = (id, schema, lengthOptions) => {
  const { maxLength } = lengthOptions ?? {};
  return {
    id,
    schema,
    optional: true,
    repeated: true,
    minLength: 0,
    maxLength
  };
};
//# sourceMappingURL=TlvObject.js.map
