"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 EndpointStoreService_exports = {};
__export(EndpointStoreService_exports, {
  EndpointStoreFactory: () => EndpointStoreFactory,
  EndpointStoreService: () => EndpointStoreService
});
module.exports = __toCommonJS(EndpointStoreService_exports);
var import_general = require("#general");
var import_EndpointStore = require("../../endpoint/storage/EndpointStore.js");
var import_IdentityService = require("../server/IdentityService.js");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const NEXT_NUMBER_KEY = "__nextNumber__";
const logger = import_general.Logger.get("EndpointStoreService");
class EndpointStoreService {
}
class EndpointStoreFactory extends EndpointStoreService {
  #storage;
  #allocatedNumbers = /* @__PURE__ */ new Set();
  #construction;
  #persistedNextNumber;
  #numbersPersisted;
  #numbersToPersist;
  #nextNumber;
  #defaultNextNumber;
  #root;
  get construction() {
    return this.#construction;
  }
  constructor({ storage, nextNumber }) {
    super();
    this.#storage = storage;
    this.#defaultNextNumber = nextNumber ?? 1;
    this.#construction = (0, import_general.Construction)(this);
    this.#construction.start();
  }
  async [import_general.Construction.construct]() {
    this.#nextNumber = await this.#storage.get(NEXT_NUMBER_KEY, this.#defaultNextNumber) % 65535;
    if (!this.#nextNumber) {
      this.#nextNumber = 1;
    } else {
      this.#persistedNextNumber = this.#nextNumber;
    }
    this.#root = await (0, import_general.asyncNew)(import_EndpointStore.EndpointStore, this.#storage);
  }
  async erase() {
    if (this.#numbersPersisted) {
      await this.#numbersPersisted;
    }
    await this.#storage.clearAll();
    this.#construction.setStatus(import_general.Lifecycle.Status.Inactive);
    this.#construction.start();
    this.#allocatedNumbers = /* @__PURE__ */ new Set();
    this.#persistedNextNumber = this.#nextNumber = (this.#defaultNextNumber ?? 1) % 65535;
    this.#root = new import_EndpointStore.EndpointStore(this.#storage, false);
    await this.construction;
  }
  async close() {
    if (this.#numbersPersisted) {
      await this.#numbersPersisted;
    }
  }
  assignNumber(endpoint) {
    if (this.#nextNumber === void 0) {
      throw new import_general.InternalError("Endpoint number assigned prior to store initialization");
    }
    this.#construction.assert();
    const store = this.storeForPart(endpoint);
    if (endpoint.lifecycle.hasNumber) {
      if (this.#allocatedNumbers.has(endpoint.number)) {
        if (this.storeForPart(endpoint).number !== endpoint.number) {
          throw new import_IdentityService.IdentityConflictError(
            `Endpoint ${endpoint.id} number ${endpoint.number} is allocated to another endpoint`
          );
        }
        return;
      }
    } else {
      const knownNumber = store.number;
      if (knownNumber) {
        if (this.#allocatedNumbers.has(knownNumber)) {
          logger.warn(`Stored number ${knownNumber} is already allocated to another endpoint, ignoring`);
        } else {
          endpoint.number = knownNumber;
          return;
        }
      }
      const startNumber = this.#nextNumber;
      while (this.#nextNumber < 1 || this.#allocatedNumbers.has(this.#nextNumber)) {
        this.#nextNumber = (this.#nextNumber + 1) % 65535;
        if (this.#nextNumber === startNumber) {
          throw new import_general.ImplementationError(
            "Cannot add additional endpoints because endpoint numbers are exhausted"
          );
        }
      }
      const number = this.#nextNumber++;
      endpoint.number = number;
    }
    this.#allocatedNumbers.add(endpoint.number);
    store.number = endpoint.number;
    this.#persistNumber(endpoint);
  }
  storeForPart(endpoint) {
    this.#construction.assert();
    if (!endpoint.lifecycle.hasId) {
      throw new import_general.InternalError("Endpoint storage access without assigned ID");
    }
    if (endpoint.owner) {
      return this.storeForPart(endpoint.owner).childStoreFor(endpoint);
    }
    if (endpoint.number !== 0) {
      throw new import_general.InternalError(
        "Endpoint storage inaccessible because endpoint is not a node and is not owned by another endpoint"
      );
    }
    if (!this.#root) {
      throw new import_general.InternalError("Endpoint storage accessed prior to initialization");
    }
    return this.#root;
  }
  /**
   * Lazily persist a newly allocated number and the next number.
   */
  #persistNumber(endpoint) {
    if (this.#numbersToPersist) {
      this.#numbersToPersist.push(endpoint);
      return;
    }
    this.#numbersToPersist = [endpoint];
    const numberPersister = async () => {
      await this.#construction;
      const numbersToPersist = this.#numbersToPersist;
      if (!numbersToPersist) {
        return;
      }
      this.#numbersToPersist = void 0;
      for (const endpoint2 of numbersToPersist) {
        const store = this.storeForPart(endpoint2);
        await store.saveNumber();
      }
      if (this.#nextNumber !== this.#persistedNextNumber) {
        await this.#storage.set(NEXT_NUMBER_KEY, this.#nextNumber);
        this.#persistedNextNumber = this.#nextNumber;
      }
    };
    if (this.#numbersPersisted) {
      this.#numbersPersisted = this.#numbersPersisted.then(numberPersister);
    } else {
      this.#numbersPersisted = numberPersister();
    }
  }
}
//# sourceMappingURL=EndpointStoreService.js.map
