/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Switch } from "#clusters/switch";
import { Observable, Time } from "#general";
import { FieldElement } from "#model";
import { ClusterType, StatusCode, StatusResponseError } from "#types";
import { SwitchBehavior } from "./SwitchBehavior.js";
const SwitchServerBase = SwitchBehavior.for(Switch.Complete).with(
  Switch.Feature.LatchingSwitch,
  Switch.Feature.MomentarySwitch,
  Switch.Feature.MomentarySwitchRelease,
  Switch.Feature.MomentarySwitchLongPress,
  Switch.Feature.MomentarySwitchMultiPress
);
const schema = SwitchServerBase.schema.extend({
  children: [
    FieldElement({ name: "longPressDelay", type: "uint32", quality: "M", conformance: "MSL" }),
    FieldElement({ name: "multiPressDelay", type: "uint32", quality: "M", conformance: "MSM" }),
    FieldElement({ name: "momentaryNeutralPosition", type: "uint8", quality: "O", conformance: "MS" })
  ]
});
class SwitchServerLogic extends SwitchServerBase {
  schema = schema;
  initialize() {
    this.state.rawPosition = this.state.currentPosition;
    this.internal.currentUnstablePosition = this.state.currentPosition;
    this.internal.previouslyReportedPosition = this.state.currentPosition;
    this.reactTo(this.events.rawPosition$Changing, this.#assertPositionInRange);
    this.reactTo(this.events.currentPosition$Changing, this.#assertPositionInRange);
    this.reactTo(this.events.rawPosition$Changed, this.#debounceRawPosition);
    this.reactTo(this.events.currentPosition$Changed, this.#handleSwitchPositionChange);
  }
  // TODO remove when Validator logic can assess that with 1.3 introduction
  #assertPositionInRange(position) {
    if (position < 0 || position >= this.state.numberOfPositions) {
      throw new StatusResponseError(`Position ${position} invalid`, StatusCode.ConstraintError);
    }
  }
  #debounceRawPosition(newPosition) {
    if (this.state.debounceDelay !== 0) {
      this.internal.debounceTimer?.stop();
      this.internal.currentUnstablePosition = newPosition;
      this.internal.debounceTimer = Time.getTimer(
        "debounce",
        this.state.debounceDelay,
        this.callback(this.#setCurrentPositionAfterDebounce)
      ).start();
    } else {
      this.state.currentPosition = newPosition;
    }
  }
  #setCurrentPositionAfterDebounce() {
    this.state.currentPosition = this.internal.currentUnstablePosition;
  }
  #handleSwitchPositionChange(newPosition) {
    if (this.features.latchingSwitch) {
      this.events.switchLatched?.emit({ newPosition }, this.context);
      return;
    }
    if (newPosition !== this.state.momentaryNeutralPosition) {
      this.events.initialPress?.emit({ newPosition }, this.context);
    }
    if (this.features.momentarySwitchLongPress) {
      if (newPosition === this.state.momentaryNeutralPosition) {
        if (this.internal.longPressTimer?.isRunning) {
          this.events.shortRelease?.emit(
            { previousPosition: this.internal.previouslyReportedPosition },
            this.context
          );
        } else if (this.internal.currentIsLongPress) {
          this.events.longRelease?.emit(
            { previousPosition: this.internal.previouslyReportedPosition },
            this.context
          );
        }
      }
      this.internal.longPressTimer?.stop();
      this.internal.currentIsLongPress = false;
      this.internal.currentLongPressPosition = null;
      if (newPosition !== this.state.momentaryNeutralPosition) {
        this.internal.currentLongPressPosition = newPosition;
        this.internal.longPressTimer = Time.getTimer(
          "longPress",
          this.state.longPressDelay,
          this.callback(this.#handleLongPress)
        ).start();
      }
    } else if (this.features.momentarySwitchRelease && newPosition === this.state.momentaryNeutralPosition) {
      this.events.shortRelease?.emit(
        { previousPosition: this.internal.previouslyReportedPosition },
        this.context
      );
    }
    if (this.features.momentarySwitchMultiPress) {
      if (this.internal.multiPressTimer?.isRunning && newPosition !== this.state.momentaryNeutralPosition) {
        this.internal.currentNumberOfPressesCounter++;
        this.events.multiPressOngoing?.emit(
          {
            newPosition,
            currentNumberOfPressesCounted: this.internal.currentNumberOfPressesCounter
          },
          this.context
        );
      }
      this.internal.multiPressTimer?.stop();
      this.internal.multiPressTimer = Time.getTimer(
        "multiPress",
        this.state.multiPressDelay,
        this.callback(this.#handleMultiPressComplete)
      ).start();
      if (this.internal.previouslyReportedPosition !== this.state.momentaryNeutralPosition) {
        this.internal.previousMultiPressPosition = this.internal.previouslyReportedPosition;
      }
    }
    this.internal.previouslyReportedPosition = newPosition;
  }
  // Method is called by a timer, so no change to the position for longPressDelay
  #handleLongPress() {
    if (this.internal.currentLongPressPosition === null) {
      return;
    }
    this.events.longPress?.emit({ newPosition: this.internal.currentLongPressPosition }, this.context);
    this.internal.currentIsLongPress = true;
  }
  #handleMultiPressComplete() {
    if (this.internal.previousMultiPressPosition === null) {
      return;
    }
    this.events.multiPressComplete?.emit(
      {
        previousPosition: this.internal.previousMultiPressPosition,
        totalNumberOfPressesCounted: this.internal.currentNumberOfPressesCounter
      },
      this.context
    );
    this.internal.currentNumberOfPressesCounter = 1;
    this.internal.previousMultiPressPosition = null;
  }
  async [Symbol.asyncDispose]() {
    this.internal.debounceTimer?.stop();
    this.internal.longPressTimer?.stop();
    this.internal.multiPressTimer?.stop();
    await super[Symbol.asyncDispose]?.();
  }
}
((SwitchServerLogic2) => {
  class Internal {
    /** Timer to debounce the raw position. */
    debounceTimer;
    /** Timer to detect a long press. */
    longPressTimer;
    /** Timer to detect multi presses. */
    multiPressTimer;
    /** Counter to count the number of presses. */
    currentNumberOfPressesCounter = 1;
    /** Position previously reported in events. */
    previouslyReportedPosition = 0;
    /** Position of the previous multi press. */
    previousMultiPressPosition = null;
    /** Position of the current long press. */
    currentLongPressPosition = null;
    /** Flag to indicate if the current press is a long press. */
    currentIsLongPress = false;
    /** Position of the current unstable position which is debounced */
    currentUnstablePosition = 0;
  }
  SwitchServerLogic2.Internal = Internal;
  class State extends SwitchServerBase.State {
    /** Raw position of the switch. Set this to debounce the value. */
    rawPosition = 0;
    /**
     * Debounce Delay to wait until a newly reported raw position is considered stable and written to the
     * currentPosition attribue.
     */
    debounceDelay = 0;
    /** Time to wait until a value is considered "long" pressed */
    longPressDelay = 0;
    /** Timeframe starting with a stable release to detect multi-presses. */
    multiPressDelay = 0;
    /** Number of the position considered as the neutral position for the momentary switch. */
    momentaryNeutralPosition = 0;
  }
  SwitchServerLogic2.State = State;
  class Events extends SwitchServerBase.Events {
    rawPosition$Changed = Observable();
    rawPosition$Changing = Observable();
  }
  SwitchServerLogic2.Events = Events;
})(SwitchServerLogic || (SwitchServerLogic = {}));
class SwitchServer extends SwitchServerLogic.for(ClusterType(Switch.Base)) {
}
export {
  SwitchServer,
  SwitchServerLogic
};
//# sourceMappingURL=SwitchServer.js.map
