/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { INSTALL_BEHAVIOR } from "#endpoint/Agent.js";
import {
  AsyncObservable,
  EventEmitter,
  GeneratedClass,
  NotImplementedError,
  Observable,
  Transaction
} from "#general";
import { ClassSemantics, Schema, Semantics } from "#model";
import { DerivedState, EmptyState } from "./state/StateType.js";
import { RootSupervisor } from "./supervision/RootSupervisor.js";
const BACKING = /* @__PURE__ */ Symbol("endpoint-owner");
const STATE = /* @__PURE__ */ Symbol("state");
const INTERNAL = /* @__PURE__ */ Symbol("internal");
const EVENTS = /* @__PURE__ */ Symbol("events");
const SUPERVISOR = /* @__PURE__ */ Symbol("supervisor");
class Behavior {
  #agent;
  /**
   * Each behavior implementation has an ID that uniquely identifies the type of behavior.  An Endpoint may only have
   * one behavior with the specified ID.
   *
   * Endpoint instances store each behavior in a property with the same name as the behavior's ID.
   *
   * EndpointBuilder also uses the ID when replacing behaviors using the with() builder method.
   */
  static id;
  /**
   * A behavior's schema controls access to data, commands and events.
   *
   * Schema is inferred from the methods and properties of the behavior but you can specify explicitly for additional
   * control.
   */
  static get schema() {
    return Schema(this) ?? Schema.empty;
  }
  /**
   * By default behaviors load lazily as they are accessed.  You can set this flag to true to force behaviors to load
   * immediately when the endpoint initializes.
   */
  static early = false;
  /**
   * The agent that owns the behavior.
   */
  get agent() {
    return this.#agent;
  }
  /**
   * The endpoint that owns behavior's agent.
   */
  get endpoint() {
    return this.#agent.endpoint;
  }
  /**
   * The context in which the behavior operates.
   */
  get context() {
    return this.#agent.context;
  }
  /**
   * The endpoint's environment.
   */
  get env() {
    return this.endpoint.env;
  }
  /**
   * The Behavior's type.
   */
  get type() {
    return this.constructor;
  }
  constructor(agent, backing) {
    this.#agent = agent;
    this[BACKING] = backing;
    if (agent !== void 0) {
      agent[INSTALL_BEHAVIOR](this);
    }
  }
  /**
   * The data supervisor for the behavior.  The supervisor controls validation and access to behavior data.
   */
  static get supervisor() {
    if (Object.hasOwn(this, SUPERVISOR)) {
      return this[SUPERVISOR];
    }
    return this[SUPERVISOR] = RootSupervisor.for(Schema(this) ?? Schema.empty);
  }
  /**
   * Implementation of endpoint-scoped state.  Subclasses may override to extend.
   */
  static State = EmptyState;
  /**
   * Implementation of internal state.  Subclasses may override to extend.
   */
  static Internal = EmptyState;
  /**
   * Implementation of the events property.  Subclasses may override to extend.
   */
  static Events = EventEmitter;
  /**
   * Behaviors are ephemeral and should not perform initialization in their constructor.  They can override this
   * method instead.
   *
   * This method may be synchronous or asyncronous.  If asynchronous, the behavior will not be available for external
   * use until initialization completes.
   */
  initialize(_options) {
  }
  /**
   * Release resources.  This is the public API for releasing application resources held by behaviors in internal
   * state.
   */
  [Symbol.asyncDispose]() {
  }
  /**
   * Description used in diagnostic messages.
   */
  toString() {
    return `${this.endpoint}.${this.type.id}`;
  }
  /**
   * Lifetime associated with this behavior type for {@link endpoint}.
   */
  get lifetime() {
    return {
      join: (...name) => {
        return this[BACKING].construction.join(...name);
      }
    };
  }
  /**
   * Install a {@link Reactor}.
   *
   * Important: The reactor MUST be a real JS function - arrow functions will not work!
   */
  reactTo(observable, reactor, options) {
    this[BACKING].reactTo(observable, reactor, options);
  }
  maybeReactTo(observable, reactor, options) {
    if (observable) {
      this.reactTo(observable, reactor, options);
    }
  }
  /**
   * Stop reacting to specified conditions.
   *
   * @param selector the observable and/or reactor to disable; if omitted terminates all reaction
   */
  stopReacting(selector) {
    return this[BACKING].stopReacting(selector);
  }
  /**
   * Create a generic callback function that has the same properties as a {@link Reactor}.
   *
   * Like a reactor, the callback's "this" will be bound to an active Behavior instance.
   * Because of this: The reactor MUST be a real JS function - arrow functions will not work!
   */
  callback(reactor, options) {
    const observable = Observable();
    this.reactTo(observable, reactor, options);
    return observable.emit.bind(observable);
  }
  /**
   * Create an async callback.
   *
   * @see {@link callback}
   */
  asyncCallback(reactor, options) {
    const observable = AsyncObservable();
    this.reactTo(observable, reactor, options);
    return observable.emit.bind(observable);
  }
  /**
   * Does this behavior support functionality of a specific implementation?
   */
  static supports(other) {
    return this === other || this.prototype instanceof other;
  }
  /**
   * Default state values.
   */
  static get defaults() {
    return new this.State();
  }
  /**
   * Create a new behavior with different default state values.
   */
  static set(defaults) {
    return GeneratedClass({
      name: this.name,
      base: this,
      staticProperties: {
        State: DerivedState({
          name: `${this.name}$State`,
          base: this.State,
          values: defaults
        })
      }
    });
  }
  /**
   * Explicitly reference other {@link Behavior}s as dependencies, ensuring this behavior is destroyed first.
   *
   * This probably won't be commonly necessary.  If it is we can instrument Agent to collect dependencies
   * automatically.
   */
  static dependencies;
  get [Transaction.Resource.reference]() {
    return this[BACKING].datasource;
  }
}
Object.defineProperties(Behavior.prototype, {
  state: {
    get() {
      if (!this[STATE]) {
        this[STATE] = this[BACKING].datasource.reference(this.context);
      }
      return this[STATE];
    },
    enumerable: true
  },
  internal: {
    get() {
      if (!this[INTERNAL]) {
        this[INTERNAL] = this[BACKING].getInternal();
      }
      return this[INTERNAL];
    },
    enumerable: false
  },
  events: {
    get() {
      if (!this[EVENTS]) {
        this[EVENTS] = this[BACKING].events;
      }
      return this[EVENTS];
    },
    enumerable: true
  }
});
Object.defineProperties(Behavior, {
  [ClassSemantics.extend]: {
    value(decoration) {
      const type = decoration.new;
      if (Object.hasOwn(type, "schema") && type.schema !== void 0) {
        decoration.mutableModel = type.schema;
      }
      const { State, Events, defaults } = type;
      if (!State || !defaults) {
        return;
      }
      if (ClassSemantics.hasOwnSemantics(State)) {
        const stateSemantics = Semantics.classOf(State);
        stateSemantics.mutableModel = decoration.mutableModel;
      }
      if (ClassSemantics.hasOwnSemantics(Events)) {
        const eventSemantics = Semantics.classOf(Events);
        eventSemantics.mutableModel = decoration.mutableModel;
      }
      decoration.defineUnknownMembers(defaults);
    }
  }
});
((Behavior2) => {
  function unimplemented(..._args) {
    throw new NotImplementedError();
  }
  Behavior2.unimplemented = unimplemented;
})(Behavior || (Behavior = {}));
export {
  Behavior
};
//# sourceMappingURL=Behavior.js.map
