/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { ElementTag } from "#common/ElementTag.js";
import { SchemaImplementationError } from "#common/errors.js";
import { FeatureSet } from "#common/FeatureSet.js";
import { ImplementationError } from "@matter/general";
import { ModelTraversal } from "./ModelTraversal.js";
const DEFAULT_TAGS = /* @__PURE__ */ new Set([ElementTag.Field, ElementTag.Attribute]);
const GLOBAL_IDS = /* @__PURE__ */ new Set([65533, 65532, 65531, 65530, 65529, 65528]);
const cache = /* @__PURE__ */ new WeakMap();
function Scope(subject, options = {}) {
  let owner;
  if (subject.isScope || options.forceOwner) {
    owner = subject;
  } else {
    const scope = new ModelTraversal().findScope(subject);
    if (scope === void 0) {
      throw new SchemaImplementationError(subject, `No parent scope`);
    }
    owner = scope;
  }
  const useCache = options.forceCache || Object.isFrozen(owner);
  let deconflictedMemberCache;
  let conformantMemberCache;
  let { featureNames, supportedFeatures } = owner;
  if (!featureNames) {
    featureNames = new FeatureSet();
  }
  if (!supportedFeatures) {
    supportedFeatures = new FeatureSet();
  }
  if (useCache && !options.disableCache) {
    const cached = cache.get(owner);
    if (cached) {
      return cached;
    }
    deconflictedMemberCache = /* @__PURE__ */ new Map();
    conformantMemberCache = /* @__PURE__ */ new Map();
  }
  let shadows;
  const canonicalIdentityLevels = {};
  let level = 0;
  new ModelTraversal().visitInheritance(owner, (scope) => {
    level++;
    for (const model of scope.children) {
      for (const identity of [`n${model.tag}\u241C${model.name}\u241C${model.discriminator ?? ""}`]) {
        const canonical = canonicalIdentityLevels[identity];
        if (canonical === void 0) {
          canonicalIdentityLevels[identity] = {
            level,
            models: [model]
          };
        } else if (canonical.level === level) {
          canonical.models.push(model);
        } else {
          if (!shadows) {
            shadows = /* @__PURE__ */ new WeakMap();
          }
          shadows.set(model, canonical.models);
        }
      }
    }
  });
  const result = {
    owner,
    isShadow: shadows ? (model) => shadows.has(model) : () => false,
    extensionOf: shadows ? (model) => shadows.get(model)?.[0] : (model) => model,
    modelFor: shadows ? (model) => shadows.get(model)?.[0] ?? model : (model) => model,
    membersOf
  };
  function membersOf(parent, options2 = {}) {
    const { conformance: conformanceMode, tags } = options2;
    const allMembers = findAllMembers(parent, Array.isArray(tags) ? new Set(tags) : tags ?? DEFAULT_TAGS, result);
    if (parent.tag === ElementTag.Cluster) {
      injectGlobalAttributes(owner, allMembers);
    }
    if (!conformanceMode || conformanceMode === "ignore") {
      return allMembers;
    }
    const conformantOnly = conformanceMode === "conformant";
    if (!conformantOnly && conformanceMode !== "deconflicted") {
      throw new ImplementationError(`Invalid member conformance mode ${conformanceMode}`);
    }
    return filterWithConformance(
      parent,
      allMembers,
      featureNames,
      supportedFeatures,
      conformantOnly,
      conformantOnly ? deconflictedMemberCache : conformantMemberCache
    );
  }
  if (useCache) {
    cache.set(owner, result);
  }
  return result;
}
((Scope2) => {
  Scope2.IgnoreConformance = "ignore";
  Scope2.DeconflictedConformance = "deconflicted";
  Scope2.ConformantConformance = "conformant";
})(Scope || (Scope = {}));
function findAllMembers(parent, tags, scope) {
  const members = Array();
  const defined = {};
  const visited = /* @__PURE__ */ new Set();
  const traversal = new ModelTraversal();
  let level = 0;
  const childSearchVisitor = (model) => {
    const extension = scope.modelFor(model);
    if (extension !== model && !visited.has(extension)) {
      traversal.visitInheritance(extension, childSearchVisitor);
      return false;
    }
    visited.add(model);
    level++;
    for (const child of model.children) {
      if (!tags.has(child.tag)) {
        continue;
      }
      const nameIdentity = `s\u241C${child.tag}\u241C${child.name}\u241C${child.discriminator ?? ""}`;
      const nameLevel = defined[nameIdentity];
      if (nameLevel !== void 0 && nameLevel < level) {
        continue;
      }
      defined[nameIdentity] = level;
      members.push(child);
    }
  };
  traversal.visitInheritance(parent, childSearchVisitor);
  return members;
}
function injectGlobalAttributes(scope, members) {
  const missingGlobalIds = new Set(GLOBAL_IDS);
  for (const m of members) {
    if (m.tag === ElementTag.Attribute && m.id) {
      missingGlobalIds.delete(m.id);
    }
  }
  if (missingGlobalIds.size) {
    const root = new ModelTraversal().findRoot(scope);
    if (root) {
      for (const id of missingGlobalIds) {
        const global = root.children.select(id, [ElementTag.Attribute]);
        if (global) {
          members.push(global);
        }
      }
    }
  }
}
function filterWithConformance(parent, members, features, supportedFeatures, conformantOnly, cache2) {
  const cached = cache2?.get(parent);
  if (cached) {
    return cached;
  }
  const selectedMembers = {};
  for (const member of members) {
    const { conformance } = member;
    if (!conformance) {
      throw new ImplementationError(
        `Conformance filtering invoked on ${member} which does not support conformance`
      );
    }
    if (conformantOnly && !conformance.isApplicable(features, supportedFeatures)) {
      continue;
    }
    const other = selectedMembers[member.name];
    if (other !== void 0) {
      if (!conformantOnly && !conformance.isApplicable(features, supportedFeatures)) {
        continue;
      }
      const { conformance: otherConformance } = other;
      if (!otherConformance) {
        throw new ImplementationError(
          `Conformance filtering invoked on ${other} which does not support conformance`
        );
      }
      if (otherConformance.isApplicable(features, supportedFeatures)) {
        throw new SchemaImplementationError(
          parent,
          `There are multiple definitions of "${member.name}" that cannot be differentiated by conformance`
        );
      }
    }
    selectedMembers[member.name] = member;
  }
  const result = Object.values(selectedMembers);
  if (cache2) {
    cache2.set(parent, result);
  }
  return result;
}
export {
  Scope
};
//# sourceMappingURL=Scope.js.map
