/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { ImplementationError } from "#MatterError.js";
import { Observable, ObservableValue } from "./Observable.js";
class BasicSet {
  #entries;
  #added;
  #deleted;
  #empty;
  #indices;
  #maps;
  constructor(...initialItems) {
    for (const item of initialItems) {
      this.add(item);
    }
  }
  [Symbol.iterator]() {
    return this.#definedEntries[Symbol.iterator]();
  }
  get size() {
    return this.#entries?.size ?? 0;
  }
  map(mapper) {
    return [...this].map(mapper);
  }
  find(predicate) {
    for (const item of this) {
      if (predicate(item)) {
        return item;
      }
    }
  }
  filter(predicate) {
    const result = new Array();
    for (const item of this) {
      if (predicate(item)) {
        result.push(item);
      }
    }
    return result;
  }
  has(item) {
    return this.#entries?.has(item) ?? false;
  }
  add(item) {
    const created = this.create(item);
    if (this.#definedEntries.has(item)) {
      return;
    }
    this.#definedEntries.add(item);
    if (this.#indices) {
      for (const field in this.#indices) {
        const value = created[field];
        if (value === void 0) {
          continue;
        }
        const index = this.#indices[field];
        if (index === void 0 || index.has(value)) {
          continue;
        }
        index.set(value, created);
      }
    }
    this.#added?.emit(created);
    if (this.#empty && this.#empty.value) {
      this.#empty.emit(false);
    }
  }
  get(field, value) {
    return this.#indexOf(field).get(value);
  }
  get #definedEntries() {
    if (this.#entries === void 0) {
      this.#entries = /* @__PURE__ */ new Set();
    }
    return this.#entries;
  }
  #indexOf(field) {
    if (!this.#indices) {
      this.#indices = {};
    }
    let index = this.#indices[field];
    if (index === void 0) {
      index = /* @__PURE__ */ new Map();
      for (const item of this) {
        const value = item[field];
        if (value === void 0 || index.has(value)) {
          continue;
        }
        index.set(value, item);
      }
      this.#indices[field] = index;
    }
    return index;
  }
  /**
   * Obtain key/value map using specific field as key.
   *
   * Note we use {@link This} to constrain usage to sets where {@link T} === {@link AddT} as required by {@link Map}.
   */
  mapOf(field) {
    if (!this.#maps) {
      this.#maps = {};
    }
    let map = this.#maps[field];
    if (map === void 0) {
      map = new MapOfIndexedSet(this, field, this.#indexOf(field));
      this.#maps[field] = map;
    }
    return map;
  }
  delete(itemOrField, value) {
    let item;
    if (value === void 0) {
      item = itemOrField;
    } else {
      item = this.get(itemOrField, value);
      if (item === void 0) {
        return false;
      }
    }
    if (!this.#entries?.delete(item)) {
      return false;
    }
    if (this.#indices) {
      for (const field in this.#indices) {
        const value2 = item[field];
        if (value2 === void 0) {
          continue;
        }
        const index = this.#indices[field];
        if (index !== void 0 && index.get(value2) === item) {
          index.delete(value2);
        }
      }
    }
    this.#deleted?.emit(item);
    if (this.#empty && !this.size) {
      this.#empty.emit(true);
    }
    return true;
  }
  clear() {
    for (const entry of [...this]) {
      this.delete(entry);
    }
  }
  get added() {
    if (this.#added === void 0) {
      this.#added = Observable();
    }
    return this.#added;
  }
  get deleted() {
    if (this.#deleted === void 0) {
      this.#deleted = Observable();
    }
    return this.#deleted;
  }
  get empty() {
    if (this.#empty === void 0) {
      this.#empty = ObservableValue(!this.#entries?.size);
    }
    return this.#empty;
  }
  create(definition) {
    return definition;
  }
}
class MapOfIndexedSet {
  #set;
  #key;
  // This is an optimization for lookup
  #index;
  /**
   * Create a new map.
   *
   * @param set the backing data
   * @param key a property of {@link T} used as the key
   * @param index optional index that optimizes lookup by bypassing {@link IndexedSet#get}
   */
  constructor(set, key, index) {
    this.#set = set;
    this.#key = key;
    this.#index = index;
  }
  clear() {
    this.#set.clear();
  }
  delete(key) {
    const item = this.get(key);
    if (item) {
      return this.#set.delete(item);
    }
    return false;
  }
  forEach(callbackfn, thisArg) {
    if (thisArg) {
      callbackfn = callbackfn.bind(thisArg);
    }
    for (const [k, v] of this) {
      callbackfn(v, k, this);
    }
  }
  get(key) {
    if (this.#index) {
      return this.#index.get(key);
    }
    return this.#set.get(this.#key, key);
  }
  has(key) {
    return this.#index ? this.#index.has(key) : this.#set.get(this.#key, key) !== void 0;
  }
  set(key, value) {
    if (value[this.#key] !== key) {
      throw new MapOfIndexedSet.KeyValueMismatchError(
        `Cannot set key "${key}" because value property ${String(this.#key)} is "${value[this.#key]}"`
      );
    }
    if (this.has(key)) {
      if (this.get(key) === value) {
        return this;
      }
      this.#set.delete(this.#index ? this.#index.get(key) : this.#set.get(this.#key, key));
    }
    this.#set.add(value);
    return this;
  }
  get size() {
    return this.#set.size;
  }
  entries() {
    return this[Symbol.iterator]();
  }
  keys() {
    if (this.#index) {
      return this.#index.keys();
    }
    const keys = [...this.#set].map((item) => item[this.#key]).filter((key) => key !== void 0);
    return keys[Symbol.iterator]();
  }
  values() {
    if (this.#index) {
      return this.#index.values();
    }
    const values = [...this.#set].map((item) => item);
    return values[Symbol.iterator]();
  }
  *[Symbol.iterator]() {
    for (const item of this.#set) {
      const k = item[this.#key];
      if (k === void 0) {
        continue;
      }
      yield [k, item];
    }
  }
  [Symbol.toStringTag] = "Map";
}
((MapOfIndexedSet2) => {
  class KeyValueMismatchError extends ImplementationError {
  }
  MapOfIndexedSet2.KeyValueMismatchError = KeyValueMismatchError;
})(MapOfIndexedSet || (MapOfIndexedSet = {}));
export {
  BasicSet,
  MapOfIndexedSet
};
//# sourceMappingURL=Set.js.map
