"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 Children_exports = {};
__export(Children_exports, {
  Children: () => Children
});
module.exports = __toCommonJS(Children_exports);
var import_ElementTag = require("#common/ElementTag.js");
var import_general = require("@matter/general");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
let ModelConstructor = void 0;
function Children(initial, adopt, reroot, disown) {
  const children = Array();
  let reified = false;
  let indices;
  let onNameChanged;
  function reify() {
    if (reified) {
      return;
    }
    for (let i = 0; i < children.length; i++) {
      let child = children[i];
      if (child instanceof ModelConstructor) {
        continue;
      }
      child = ModelConstructor.create(child);
      doAdopt(child);
      children[i] = child;
    }
    reified = true;
  }
  function hasModel(child) {
    if (child instanceof ModelConstructor) {
      return true;
    }
    return child.children?.some(hasModel);
  }
  function insertionFormOf(child) {
    if (child instanceof ModelConstructor) {
      return child;
    }
    if (reified || hasModel(child)) {
      return ModelConstructor.create(child);
    }
    return child;
  }
  function indexInsert(index, key, model) {
    const existing = index[key];
    if (existing) {
      if (Array.isArray(existing)) {
        existing.push(model);
      } else {
        index[key] = [existing, model];
      }
    } else {
      index[key] = model;
    }
  }
  function indexDelete(index, key, model) {
    const existing = index[key];
    if (existing === model) {
      delete index[key];
    }
    if (Array.isArray(existing)) {
      const pos = existing.indexOf(model);
      if (pos === -1) {
        return;
      }
      existing.splice(pos, 1);
      if (existing.length === 1) {
        index[key] = existing[0];
      }
      return;
    }
  }
  function buildIndex(type) {
    reify();
    const byId = Array();
    const byName = {};
    for (const child of children) {
      if (child instanceof type) {
        const id = child.effectiveId;
        if (id !== void 0) {
          indexInsert(byId, id, child);
        }
        indexInsert(byName, child.name, child);
      }
    }
    const slot = { byId, byName };
    if (!indices) {
      indices = /* @__PURE__ */ new Map();
    }
    indices.set(type, slot);
    return slot;
  }
  function deleteChild(child) {
    if (indices) {
      for (const [type, slot] of indices.entries()) {
        if (child instanceof type) {
          if (child.id) {
            indexDelete(slot.byId, child.id, child);
          }
          indexDelete(slot.byName, child.name, child);
        }
      }
    }
    onNameChanged?.(child.name, void 0);
    doDisown(child);
  }
  function addChild(child) {
    if (child.parent?.children === children) {
      return;
    }
    if (indices) {
      for (const [type, slot] of indices.entries()) {
        if (child instanceof type) {
          if (child.id) {
            indexInsert(slot.byId, child.id, child);
          }
          indexInsert(slot.byName, child.name, child);
        }
      }
    }
    doAdopt(child);
  }
  function doReroot(child, isOwned) {
    if (!reroot(child, isOwned) || !child.hasChildren) {
      return;
    }
    child.children.rerootAll(isOwned);
  }
  function rerootAll(isOwned) {
    for (const child of children) {
      if (child instanceof ModelConstructor) {
        doReroot(child, isOwned);
      }
    }
  }
  function doAdopt(child) {
    adopt(child);
    onNameChanged?.(child.name, child);
    doReroot(child, true);
  }
  function doDisown(child) {
    onNameChanged?.(child.name, void 0);
    if (disown(child)) {
      doReroot(child, false);
    }
  }
  function get(type, idOrName) {
    const value = all(type, idOrName);
    if (Array.isArray(value)) {
      return value[0];
    }
    return value;
  }
  function all(type, idOrName) {
    const slot = indices?.get(type) ?? buildIndex(type);
    if (idOrName === void 0) {
      return Object.values(slot.byName).flatMap((entry) => entry);
    }
    let result;
    if (typeof idOrName === "number") {
      result = slot.byId[idOrName];
    } else {
      result = slot.byName[idOrName];
    }
    if (result === void 0) {
      return [];
    }
    if (Array.isArray(result)) {
      return result;
    }
    return [result];
  }
  function selectTypes(tags) {
    if (tags === void 0 || tags === "*") {
      return [ModelConstructor];
    }
    if (typeof tags === "string") {
      tags = [tags];
    }
    const result = Array();
    for (const tag of tags) {
      const type = ModelConstructor.types[tag];
      if (type === void 0) {
        throw new import_general.ImplementationError(`Unknown element tag "${tag}"`);
      }
      result.push(type);
    }
    return result;
  }
  function indexLookup(selector, indexName, allowedTags, except, processor) {
    for (const type of selectTypes(allowedTags)) {
      let slot = indices?.get(type);
      if (slot === void 0) {
        slot = buildIndex(type);
      }
      const index = slot[indexName];
      const entry = index[selector];
      if (Array.isArray(entry)) {
        for (const subentry of entry) {
          if (except?.has(subentry)) {
            continue;
          }
          const result = processor(subentry);
          if (result !== void 0) {
            return result;
          }
        }
        continue;
      }
      if (entry) {
        if (except?.has(entry)) {
          continue;
        }
        const result = processor(entry);
        if (result) {
          return result;
        }
      }
    }
  }
  function indexApply(selector, allowedTags, except) {
    for (const type of selectTypes(allowedTags)) {
      let index = indices?.get(type)?.byName;
      if (!index) {
        index = buildIndex(type).byName;
      }
      for (const key in index) {
        const entry = index[key];
        if (Array.isArray(entry)) {
          for (const subentry of entry) {
            if (except?.has(subentry)) {
              continue;
            }
            if (selector(subentry)) {
              return subentry;
            }
          }
          continue;
        }
        if (except?.has(entry)) {
          continue;
        }
        if (selector(entry)) {
          return entry;
        }
      }
    }
  }
  function select(selector, allowedTags, except) {
    reify();
    if (typeof selector === "string") {
      return indexLookup(selector, "byName", allowedTags, except, (model) => model);
    }
    if (typeof selector === "number") {
      return indexLookup(selector, "byId", allowedTags, except, (model) => model);
    }
    return indexApply(selector, allowedTags, except);
  }
  function selectAll(selector, allowedTags, except) {
    reify();
    const results = Array();
    if (typeof selector === "string") {
      indexLookup(selector, "byName", allowedTags, except, (model) => {
        results.push(model);
      });
    } else {
      indexLookup(selector, "byId", allowedTags, except, (model) => {
        results.push(model);
      });
    }
    return results;
  }
  function updateId(child, oldId) {
    if (!indices) {
      return;
    }
    for (const [type, slot] of indices.entries()) {
      if (child instanceof type) {
        if (oldId !== void 0) {
          indexDelete(slot.byId, oldId, child);
        }
        if (child.id !== void 0) {
          indexInsert(slot.byId, child.id, child);
        }
      }
    }
  }
  function updateName(child, oldName) {
    if (onNameChanged) {
      onNameChanged(oldName, void 0);
      onNameChanged(child.name, child);
    }
    if (!indices) {
      return;
    }
    for (const [type, slot] of indices.entries()) {
      if (child instanceof type) {
        if (oldName !== void 0) {
          indexDelete(slot.byName, oldName, child);
        }
        if (child.name !== void 0) {
          indexInsert(slot.byName, child.name, child);
        }
      }
    }
  }
  function splice(index, deleteCount, ...toAdd) {
    toAdd = toAdd.map((child) => {
      child = insertionFormOf(child);
      if (child instanceof ModelConstructor) {
        doAdopt(child);
      }
      return child;
    });
    const result = children.splice(index, deleteCount ?? 0, ...toAdd);
    return result.map((child) => {
      if (child instanceof ModelConstructor) {
        doDisown(child);
      } else {
        child = ModelConstructor.create(child);
      }
      return child;
    });
  }
  function finalize() {
    for (const child of self) {
      child.finalize();
    }
    Object.freeze(children);
  }
  function validateChild(value) {
    if (value instanceof ModelConstructor) {
      return;
    }
    if (value === void 0 || value === null) {
      throw new import_general.ImplementationError(`Child cannot be ${value}`);
    }
    if (typeof value !== "object") {
      throw new import_general.ImplementationError(`Child must be an object (child is typeof ${typeof value})`);
    }
    const { tag } = value;
    if (typeof tag !== "string") {
      throw new import_general.ImplementationError(`Child must have a string tag (tag is typeof ${typeof tag})`);
    }
    if (tag[0] < "a" || tag[0] > "z" || !((0, import_general.camelize)(tag, true) in import_ElementTag.ElementTag)) {
      throw new import_general.ImplementationError(`Child tag "${tag}" is unknown`);
    }
  }
  const self = new Proxy(children, {
    get(_target, name, receiver) {
      if (typeof name === "string" && name.match(/^\d+$/)) {
        let child = children[name];
        if (child && !(child instanceof ModelConstructor)) {
          child = ModelConstructor.create(child);
          addChild(child);
          children[name] = child;
        }
        return child;
      }
      switch (name) {
        case "get":
          return get;
        case "all":
          return all;
        case "select":
          return select;
        case "selectAll":
          return selectAll;
        case "updateId":
          return updateId;
        case "updateName":
          return updateName;
        case "splice":
          return splice;
        case "freeze":
          return finalize;
        case "toString":
          return () => `[Children: ${children.length}]`;
        case "rerootAll":
          return rerootAll;
      }
      return Reflect.get(children, name, receiver);
    },
    set(_target, name, value, receiver) {
      if (typeof name !== "string" || !name.match(/^\d+$/)) {
        switch (name) {
          case "length":
            if (value > children.length) {
              return true;
            }
            break;
          case "onNameChanged":
            reify();
            onNameChanged = value;
            return true;
        }
        return Reflect.set(children, name, value, receiver);
      }
      validateChild(value);
      const existing = children[name];
      if (existing !== void 0) {
        if (existing === value) {
          return true;
        }
        if (existing instanceof ModelConstructor) {
          deleteChild(existing);
        }
      }
      let targetIndex = name;
      if (value.parent?.children === self) {
        const currentIndex = children.indexOf(value);
        if (currentIndex !== -1) {
          children.splice(currentIndex, 1);
          if (currentIndex < targetIndex) {
            targetIndex--;
          }
        }
      } else {
        value = insertionFormOf(value);
        if (value instanceof ModelConstructor) {
          addChild(value);
        }
      }
      if (targetIndex > children.length) {
        targetIndex = children.length;
      }
      children[targetIndex] = value;
      return true;
    },
    deleteProperty: (_target, p) => {
      let child;
      if (typeof p === "string" && p.match(/^\d+$/)) {
        child = children[p];
      }
      delete children[p];
      if (child instanceof ModelConstructor && !children.includes(child)) {
        deleteChild(child);
      }
      return true;
    }
  });
  initial = [...initial];
  self.push(...initial);
  return self;
}
Children.installModelConstructor = (constructor) => ModelConstructor = constructor;
//# sourceMappingURL=Children.js.map
