/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { AsyncObservable, EventEmitter, ImplementationError, Observable } from "#general";
import { ClusterComposer, ClusterType, ElementModifier } from "#types";
import { Behavior } from "../Behavior.js";
import { NetworkBehavior } from "../system/network/NetworkBehavior.js";
import { createType } from "./ClusterBehaviorUtil.js";
import { ClusterInterface } from "./ClusterInterface.js";
class ClusterBehavior extends Behavior {
  /**
   * The ID of ClusterBehavior implementations is the uncapitalized cluster name.
   */
  static id;
  /**
   * The cluster implemented by this behavior.
   */
  get cluster() {
    return this.constructor.cluster;
  }
  /**
   * Supported features as a flag object.
   */
  get features() {
    return this.cluster.supportedFeatures;
  }
  /**
   * Every cluster behavior has an associated ClusterType defined statically.
   */
  static cluster = ClusterType.Unknown;
  /**
   * Method definitions.
   */
  static Interface = ClusterInterface.Empty;
  /**
   * All ClusterBehavior initialization currently runs as part of {@link Endpoint} initialization.
   */
  static early = true;
  /**
   * Keep networking alive until I'm destroyed.
   */
  static dependencies = [NetworkBehavior];
  /**
   * Automatically lock state on command invoke.
   */
  static lockOnInvoke = true;
  constructor(agent, backing) {
    super(agent, backing);
    const cluster = this.constructor.cluster;
    if (!cluster) {
      throw new ImplementationError("ClusterBehavior class has no cluster defined");
    }
  }
  /**
   * Create a new behavior for a specific {@link ClusterType}.
   *
   * If you invoke directly on {@link ClusterBehavior} you will receive a new implementation that reports all commands
   * as unimplemented.
   *
   * If you invoke on an existing subclass, you will receive a new implementation with the cluster in the subclass
   * replaced.  You should generally only do this with a {@link ClusterType} with the same ID.
   */
  static for(cluster, schema) {
    return createType(cluster, this, schema);
  }
  /**
   * Create a new behavior with different cluster features.
   */
  static with(...features) {
    const newCluster = new ClusterComposer(this.cluster).compose(features);
    return this.for(newCluster);
  }
  /**
   * Create a new behavior with modified cluster elements.
   */
  static alter(alterations) {
    return this.for(new ElementModifier(this.cluster).alter(alterations));
  }
  /**
   * Create a new behavior with additional cluster features marked "mandatory".
   *
   * This informs matter.js that an application supports these elements.
   */
  static enable(flags) {
    return this.for(new ElementModifier(this.cluster).enable(flags));
  }
  /**
   * Create a ClusterBehavior like this one with different interface methods.
   *
   * The Interface "property" is type-only.  We define a method however to keep the API consistent.  At runtime the
   * method is a no-op.
   */
  static withInterface() {
    return this;
  }
  static supports(other) {
    if (!Behavior.supports.call(this, other)) {
      return false;
    }
    const otherCluster = other.cluster;
    if (!otherCluster) {
      return false;
    }
    const otherFeatures = otherCluster.supportedFeatures;
    const myFeatures = this.cluster.supportedFeatures;
    for (const name in otherFeatures) {
      if (otherFeatures[name] && !myFeatures[name]) {
        return false;
      }
    }
    return true;
  }
  requireAttributeEnabled(attributeName) {
    if (this.state[attributeName] === void 0) {
      throw new ImplementationError(
        `To use this feature, please enable attribute ${String(attributeName)} by setting the value during initialization.`
      );
    }
    return this.state[attributeName];
  }
  assertAttributeEnabled(attributeName) {
    if (this.state[attributeName] === void 0) {
      throw new ImplementationError(
        `To use this feature, please enable attribute ${String(attributeName)} by setting the value during initialization.`
      );
    }
  }
}
((ClusterBehavior2) => {
  class Events extends EventEmitter {
    /**
     * Emitted when state associated with this behavior is first mutated by a specific interaction.
     */
    interactionBegin = Observable();
    /**
     * Emitted when a mutating interaction completes.
     */
    interactionEnd = AsyncObservable();
  }
  ClusterBehavior2.Events = Events;
})(ClusterBehavior || (ClusterBehavior = {}));
export {
  ClusterBehavior
};
//# sourceMappingURL=ClusterBehavior.js.map
