"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
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 __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
var Node_exports = {};
__export(Node_exports, {
  Node: () => Node
});
module.exports = __toCommonJS(Node_exports);
var import_NodeActivity = require("#behavior/context/NodeActivity.js");
var import_IndexBehavior = require("#behavior/system/index/IndexBehavior.js");
var import_NetworkBehavior = require("#behavior/system/network/NetworkBehavior.js");
var import_NetworkRuntime = require("#behavior/system/network/NetworkRuntime.js");
var import_PartsBehavior = require("#behavior/system/parts/PartsBehavior.js");
var import_Endpoint = require("#endpoint/Endpoint.js");
var import_Endpoints = require("#endpoint/properties/Endpoints.js");
var import_MutableEndpoint = require("#endpoint/type/MutableEndpoint.js");
var import_general = require("#general");
var import_root = require("../endpoints/root.js");
var import_NodeLifecycle = require("./NodeLifecycle.js");
var import_ProtocolService = require("./integration/ProtocolService.js");
/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("Node");
class Node extends import_Endpoint.Endpoint {
  #environment;
  #runtime;
  #startInProgress = false;
  #closeInProgress = false;
  constructor(config) {
    const parentEnvironment = config.environment ?? config.owner?.env ?? import_general.Environment.default;
    if (config.id === void 0) {
      config.id = `node${parentEnvironment.vars.increment("node.nextFallbackId")}`;
    }
    super(config);
    this.#environment = new import_general.Environment(config.id, parentEnvironment);
    this.#environment.set(import_NodeActivity.NodeActivity, new import_NodeActivity.NodeActivity());
    this.#environment.set(import_ProtocolService.ProtocolService, new import_ProtocolService.ProtocolService(this));
    if (this.lifecycle.hasNumber) {
      if (this.number !== 0) {
        throw new import_general.ImplementationError("The root endpoint ID must be 0");
      }
    } else {
      this.number = 0;
    }
    this.lifecycle.online.on(() => {
      this.statusUpdate("is online");
    });
    this.lifecycle.offline.on(() => {
      this.statusUpdate("is offline");
    });
    this.lifecycle.goingOffline.on(() => {
      this.statusUpdate("going offline");
    });
  }
  get env() {
    return this.#environment;
  }
  /**
   * The optimized view that supports local Matter protocol implementation.
   */
  get protocol() {
    return this.env.get(import_ProtocolService.ProtocolService).protocol;
  }
  get lifecycle() {
    return super.lifecycle;
  }
  /**
   * Bring the node online.
   */
  async start() {
    if (this.lifecycle.isOnline || this.#startInProgress) {
      return;
    }
    this.#startInProgress = true;
    try {
      this.lifecycle.targetState = "online";
      await this.lifecycle.mutex.produce(this.startWithMutex.bind(this));
    } catch (error) {
      this.lifecycle.targetState = "offline";
      throw error;
    } finally {
      this.#startInProgress = false;
    }
  }
  async startWithMutex() {
    this.env.runtime.add(this);
    try {
      await this.construction.ready;
      if (this.#runtime) {
        return;
      }
      this.statusUpdate("going online");
      this.#runtime = this.createRuntime();
      this.#runtime.construction.start();
      this.#environment.set(import_NetworkRuntime.NetworkRuntime, this.#runtime);
      await this.#runtime.construction.ready;
      await this.act("network startup", (agent) => agent.get(import_NetworkBehavior.NetworkBehavior).startup());
    } catch (e) {
      if (this.#runtime) {
        this.#environment.delete(import_NetworkRuntime.NetworkRuntime, this.#runtime);
        try {
          await this.#runtime.close();
        } catch (error) {
          import_general.MatterError.accept(error);
          logger.info("Failed to tear down runtime", error.message);
        }
        this.#runtime = void 0;
        this.behaviors.internalsOf(import_NetworkBehavior.NetworkBehavior).runtime = void 0;
      }
      this.env.runtime.delete(this);
      throw e;
    }
  }
  /**
   * @deprecated use {@link start}
   */
  async bringOnline() {
    return this.start();
  }
  /**
   * Run the node in standalone mode.  Returns when the node is closed.
   */
  async run() {
    await this.start();
    await this.construction.closed;
  }
  /**
   * Take the node offline but leave state and structure intact.  Happens automatically on close.
   *
   * Once the node is offline you may use {@link start} to bring the node online again.
   */
  async cancel() {
    this.lifecycle.targetState = "offline";
    await this.lifecycle.mutex.produce(this.cancelWithMutex.bind(this));
  }
  /**
   * All endpoints owned by the node.
   *
   * Normally you access endpoints via {@link parts} but you can use this property access endpoints directly by
   * {@link EndpointNumber}.
   */
  get endpoints() {
    return new import_Endpoints.Endpoints(this);
  }
  async cancelWithMutex() {
    if (!this.#runtime) {
      return;
    }
    await this.act((agent) => this.lifecycle.goingOffline.emit(agent.context));
    if (this.#runtime) {
      this.#environment.delete(import_NetworkRuntime.NetworkRuntime, this.#runtime);
    }
    await this.#runtime?.close();
    this.#runtime = void 0;
  }
  async close() {
    if (this.#closeInProgress) {
      return;
    }
    this.#closeInProgress = true;
    try {
      this.lifecycle.targetState = "offline";
      await this.lifecycle.mutex.close();
      await this.closeWithMutex();
    } finally {
      this.#closeInProgress = false;
    }
  }
  async closeWithMutex() {
    if (this.#runtime) {
      await this.cancelWithMutex();
    }
    await super.close();
  }
  async reset() {
    await this.lifecycle.mutex.produce(this.resetWithMutex.bind(this));
  }
  async resetWithMutex() {
    return super.reset();
  }
  get [import_general.DiagnosticPresentation.name]() {
    return ["Runtime for", import_general.Diagnostic.strong(this.toString())];
  }
  get [import_general.Diagnostic.value]() {
    var _stack = [];
    try {
      const nodeActivity = this.#environment.get(import_NodeActivity.NodeActivity);
      const _activity = __using(_stack, nodeActivity.begin("diagnostics"));
      return import_general.Diagnostic.node("\u{1F9E9}", this.id, {
        children: [
          import_general.Diagnostic.strong("Structure"),
          import_general.Diagnostic.list([super[import_general.Diagnostic.value]]),
          import_general.Diagnostic.strong("Activity"),
          nodeActivity[import_general.Diagnostic.value]
        ]
      });
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
  createLifecycle() {
    return new import_NodeLifecycle.NodeLifecycle(this);
  }
  statusUpdate(message) {
    logger.notice(import_general.Diagnostic.strong(this.toString()), message);
  }
  async [import_general.Construction.destruct]() {
    await this.cancelWithMutex();
    await super[import_general.Construction.destruct]();
    import_general.DiagnosticSource.delete(this);
    this.#environment[Symbol.dispose]();
  }
}
((Node2) => {
  function nodeConfigFor(defaultType, configuration, options) {
    if (configuration === void 0) {
      return {
        type: defaultType,
        ...options
      };
    }
    if (configuration.deviceType !== void 0) {
      return {
        type: configuration,
        ...options
      };
    }
    return {
      type: defaultType,
      ...configuration
    };
  }
  Node2.nodeConfigFor = nodeConfigFor;
  function forEndpoint(endpoint) {
    const node = endpoint.ownerOfType(import_root.RootEndpoint);
    if (node === void 0) {
      throw new import_general.ImplementationError(`Cannot complete operation because ${endpoint} is not installed in a node`);
    }
    if (!(node instanceof Node2)) {
      throw new import_general.ImplementationError(`Root endpoint for ${endpoint} is not a node`);
    }
    return node;
  }
  Node2.forEndpoint = forEndpoint;
  Node2.CommonRootEndpoint = (0, import_MutableEndpoint.MutableEndpoint)({
    name: import_root.RootEndpoint.name,
    deviceType: import_root.RootEndpoint.deviceType,
    deviceRevision: import_root.RootEndpoint.deviceRevision,
    deviceClass: import_root.RootEndpoint.deviceClass,
    requirements: import_root.RootEndpoint.requirements,
    behaviors: {
      parts: import_PartsBehavior.PartsBehavior,
      index: import_IndexBehavior.IndexBehavior
    }
  });
})(Node || (Node = {}));
//# sourceMappingURL=Node.js.map
