"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 ClassSemantics_exports = {};
__export(ClassSemantics_exports, {
  ClassSemantics: () => ClassSemantics
});
module.exports = __toCommonJS(ClassSemantics_exports);
var import_models = require("#models/index.js");
var import_general = require("#general");
var import_Scope = require("#logic/Scope.js");
var import_models2 = require("#standard/elements/models.js");
var import_errors = require("../errors.js");
var import_FieldSemantics = require("./FieldSemantics.js");
var import_Semantics = require("./Semantics.js");
/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const matter = /* @__PURE__ */ Symbol("matter");
class ClassSemantics extends import_Semantics.Semantics {
  #new;
  #definedFields;
  /**
   * The model that represents the semantics for this class.
   *
   * This may be {@link localModel} or {@link prototypeBaseModel}
   */
  get semanticModel() {
    if (this.localModel) {
      return this.localModel;
    }
    if (this.#definedFields) {
      return this.mutableModel;
    }
    return this.prototypeBaseModel;
  }
  /**
   * Get the class constructor
   */
  get new() {
    return this.#new;
  }
  /**
   * Assign the constructor for the class.
   */
  set new(fn) {
    if (this.#new === fn) {
      return;
    }
    if (this.isFinal) {
      throw new import_errors.MetadataConflictError(
        `Cannot install semantic constructor ${fn.name} because semantics are final`
      );
    }
    this.#new = fn;
    if (this.localModel && !this.localModel.isFinal && this.localModel.name !== this.#new?.name) {
      this.localModel.name = this.#new?.name;
    }
    if (this.localModel) {
      this.#applyBaseSemantics();
    } else {
      const base = this.prototypeBaseModel;
      if (base !== void 0) {
        this.mutableModel = base;
      }
    }
  }
  /**
   * The class {@link Model} inherited from {@link new}'s prototype chain, if any.
   */
  get prototypeBaseModel() {
    let current = this.#new?.prototype;
    let base;
    while (current) {
      current = Object.getPrototypeOf(current);
      if (typeof current !== "object" || current === null) {
        return;
      }
      const constructor = current.constructor;
      if (constructor === Object) {
        return;
      }
      base = ClassSemantics.maybeOf(constructor);
      if (base?.localModel) {
        base.finalize();
        return base.localModel;
      }
    }
  }
  /**
   * Obtain a {@link FieldSemantics} for the named field.
   */
  fieldFor(name) {
    if (typeof name !== "string") {
      throw new import_errors.InvalidMetadataError(`Cannot decorate symbolic function ${String(name)}`);
    }
    if (this.#definedFields === void 0) {
      this.#definedFields = /* @__PURE__ */ new Map();
    }
    let field = this.#definedFields.get(name);
    if (field === void 0) {
      if (this.isFinal) {
        throw new import_errors.MetadataConflictError(`Cannot install field ${name} because semantics are final`);
      }
      this.#definedFields.set(name, field = new import_FieldSemantics.FieldSemantics(this, name));
    }
    return field;
  }
  /**
   * Add logical fields for unrecognized members of an object.
   *
   * This extends the model with untyped fields for properties that are not otherwise typed.
   */
  defineUnknownMembers(instance) {
    if (instance === void 0 || instance === null) {
      return;
    }
    const known = this.semanticModel ? new Set(
      (0, import_Scope.Scope)(this.semanticModel).membersOf(this.semanticModel).map((model) => (0, import_general.camelize)(model.name))
    ) : /* @__PURE__ */ new Set();
    const descriptors = Object.getOwnPropertyDescriptors(instance);
    for (const name in descriptors) {
      if (known.has((0, import_general.camelize)(name))) {
        continue;
      }
      if (typeof name !== "string") {
        continue;
      }
      const descriptor = descriptors[name];
      if (descriptor.set && !descriptor.get) {
        continue;
      }
      try {
        if (typeof instance[name] === "function") {
          continue;
        }
      } catch (e) {
        continue;
      }
      const model = this.fieldFor(name).mutableModel;
      model.operationalBase = import_models2.any;
      if (!descriptor.writable && !descriptor.set) {
        model.quality = { ...model.quality, fixed: true };
      }
      this.fieldFor(name).mutableModel.operationalBase = import_models2.any;
    }
  }
  /**
   * Obtain the {@link ClassSemantics} for {@link source}.
   */
  static of(source) {
    if (source instanceof ClassSemantics) {
      return source;
    }
    if (typeof source !== "function") {
      const metadata2 = source.metadata;
      if (Object.hasOwn(metadata2, matter)) {
        return metadata2[matter];
      }
      return metadata2[matter] = new ClassSemantics();
    }
    let metadata;
    if (!Object.hasOwn(source, Symbol.metadata)) {
      metadata = source[Symbol.metadata] = {};
    } else {
      metadata = source[Symbol.metadata];
    }
    let semantics;
    if (!Object.hasOwn(metadata, matter)) {
      semantics = metadata[matter] = new ClassSemantics();
    } else {
      semantics = metadata[matter];
    }
    if (!semantics.new) {
      semantics.new = source;
    }
    return semantics;
  }
  /**
   * Determine {@link new} has semantic decoration.
   */
  static hasOwnSemantics(source) {
    if (typeof source !== "function") {
      throw new import_general.InternalError(`Missing constructor for class semantic check`);
    }
    return Object.hasOwn(source, Symbol.metadata) && Object.hasOwn(source[Symbol.metadata], matter);
  }
  /**
   * Access the {@link ClassSemantics} of {@link new} if it is defined.
   */
  static maybeOf(source) {
    if (this.hasOwnSemantics(source)) {
      return ClassSemantics.of(source);
    }
  }
  static {
    import_Semantics.Semantics.classOf = this.of;
  }
  finalize() {
    if (this.isFinal) {
      return;
    }
    this.#new?.[ClassSemantics.extend]?.(this);
    super.finalize();
    if (this.#definedFields) {
      for (const field of this.#definedFields.values()) {
        field.finalize();
      }
    }
  }
  #applyBaseSemantics() {
    const base = this.prototypeBaseModel;
    if (base === void 0) {
      return;
    }
    const { type, base: currentBase } = this.mutableModel;
    if (type === void 0 && (currentBase === void 0 || currentBase === import_models2.struct)) {
      const operationalBase = this.prototypeBaseModel;
      if (operationalBase) {
        this.mutableModel.operationalBase = operationalBase;
      }
    }
    if (base && base.tag !== "datatype" && this.mutableModel.tag !== base.tag) {
      this.modelType = base.constructor;
    }
    if (this.mutableModel.id === void 0 && base.id !== void 0) {
      this.mutableModel.id = base.id;
    }
  }
  integrateModel(model) {
    if (this.localModel === void 0 || this.localModel.isFinal) {
      return model;
    }
    if (model.isFinal) {
      this.localModel.operationalBase = model;
      return this.localModel;
    }
    model.children.push(...this.localModel.children);
    return model;
  }
  createModel(type = import_models.DatatypeModel) {
    let name = this.#new?.name;
    if (name === void 0 || name === "") {
      name = "Unnamed";
    }
    return new type({ name, operationalBase: import_models2.struct });
  }
}
((ClassSemantics2) => {
  ClassSemantics2.extend = /* @__PURE__ */ Symbol("extend");
})(ClassSemantics || (ClassSemantics = {}));
//# sourceMappingURL=ClassSemantics.js.map
