"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 WindowCoveringServer_exports = {};
__export(WindowCoveringServer_exports, {
  CalibrationMode: () => CalibrationMode,
  MovementDirection: () => MovementDirection,
  MovementType: () => MovementType,
  WindowCoveringBaseServer: () => WindowCoveringBaseServer,
  WindowCoveringServer: () => WindowCoveringServer
});
module.exports = __toCommonJS(WindowCoveringServer_exports);
var import_general = require("@matter/general");
var import_types = require("@matter/types");
var import_window_covering = require("@matter/types/clusters/window-covering");
var import_WindowCoveringBehavior = require("./WindowCoveringBehavior.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("WindowCoveringServer");
const WindowCoveringBase = import_WindowCoveringBehavior.WindowCoveringBehavior.with(
  import_window_covering.WindowCovering.Feature.Lift,
  import_window_covering.WindowCovering.Feature.Tilt,
  import_window_covering.WindowCovering.Feature.PositionAwareLift,
  import_window_covering.WindowCovering.Feature.PositionAwareTilt
);
var MovementType = /* @__PURE__ */ ((MovementType2) => {
  MovementType2[MovementType2["Lift"] = 0] = "Lift";
  MovementType2[MovementType2["Tilt"] = 1] = "Tilt";
  return MovementType2;
})(MovementType || {});
var CalibrationMode = /* @__PURE__ */ ((CalibrationMode2) => {
  CalibrationMode2[CalibrationMode2["Enabled"] = 0] = "Enabled";
  CalibrationMode2[CalibrationMode2["Running"] = 1] = "Running";
  CalibrationMode2[CalibrationMode2["Disabled"] = 2] = "Disabled";
  return CalibrationMode2;
})(CalibrationMode || {});
var MovementDirection = /* @__PURE__ */ ((MovementDirection2) => {
  MovementDirection2[MovementDirection2["Open"] = 0] = "Open";
  MovementDirection2[MovementDirection2["Close"] = 1] = "Close";
  MovementDirection2[MovementDirection2["DefinedByPosition"] = 2] = "DefinedByPosition";
  return MovementDirection2;
})(MovementDirection || {});
const WC_PERCENT100THS_MIN_OPEN = 0;
const WC_PERCENT100THS_MAX_CLOSED = 1e4;
const WC_PERCENT100THS_COEFFICIENT = 100;
class WindowCoveringBaseServer extends WindowCoveringBase {
  initialize() {
    this.internal.inMaintenanceMode = !!this.state.mode.maintenanceMode;
    this.internal.calibrationMode = this.state.mode.calibrationMode && !this.state.mode.maintenanceMode ? 0 /* Enabled */ : 2 /* Disabled */;
    this.reactTo(this.events.mode$Changing, this.#handleModeChanging);
    const configStatus = this.state.configStatus;
    configStatus.operational = !this.internal.inMaintenanceMode && !this.state.mode.calibrationMode;
    if (this.features.lift) {
      if (this.features.positionAwareLift) {
        configStatus.liftPositionAware = true;
      }
    }
    if (this.features.tilt) {
      if (this.features.positionAwareTilt) {
        configStatus.tiltPositionAware = true;
      }
    }
    if (!(0, import_general.isDeepEqual)(configStatus, this.state.configStatus)) {
      this.state.configStatus = configStatus;
    }
    if (this.features.tilt) {
      this.state.targetPositionTiltPercent100ths = this.state.currentPositionTiltPercent100ths;
    }
    if (this.features.lift) {
      this.state.targetPositionLiftPercent100ths = this.state.currentPositionLiftPercent100ths;
    }
    if (!this.internal.disableOperationalModeHandling) {
      this.reactTo(this.events.currentPositionLiftPercent100ths$Changing, this.#syncLiftCurrentPositions);
      this.reactTo(this.events.currentPositionTiltPercent100ths$Changing, this.#syncTiltCurrentPositions);
      this.reactTo(this.events.targetPositionLiftPercent100ths$Changing, this.#handleLiftTargetPositionChanging);
      this.reactTo(this.events.targetPositionTiltPercent100ths$Changing, this.#handleTiltTargetPositionChanging);
      this.reactTo(this.events.operationalStatus$Changing, this.#handleOperationalStatusChanging);
    }
  }
  /**
   * Sync the mode attribute with the configStatus attribute and the internal state.
   */
  #handleModeChanging(mode) {
    if (mode.maintenanceMode && mode.calibrationMode) {
      mode.calibrationMode = false;
    }
    if (mode.maintenanceMode && !this.state.supportsMaintenanceMode) {
      throw new import_types.StatusResponseError("Maintenance mode not supported", import_types.StatusCode.ConstraintError);
    }
    this.internal.inMaintenanceMode = !!mode.maintenanceMode;
    if (mode.calibrationMode) {
      if (!this.internal.supportsCalibration) {
        throw new import_types.StatusResponseError("Calibration not supported", import_types.StatusCode.ConstraintError);
      }
      if (this.internal.calibrationMode === 1 /* Running */) {
      } else {
        this.internal.calibrationMode = 0 /* Enabled */;
      }
    } else {
      this.internal.calibrationMode = 2 /* Disabled */;
    }
    const configStatus = this.state.configStatus;
    configStatus.operational = !mode.maintenanceMode || mode.calibrationMode && !this.internal.supportsCalibration;
    configStatus.liftMovementReversed = !!mode.motorDirectionReversed;
    if ((0, import_general.isDeepEqual)(configStatus, this.state.configStatus)) {
      this.agent.asLocalActor(() => {
        this.state.configStatus = configStatus;
      });
    }
    logger.debug(
      `Mode changed to ${import_general.Diagnostic.json(mode)} and config status to ${import_general.Diagnostic.json(configStatus)} and internal calibration mode to ${this.internal.calibrationMode}`
    );
  }
  /** Update the global operational status based on the lift or tilt status. */
  #handleOperationalStatusChanging(operationalStatus) {
    const globalStatus = operationalStatus.lift !== import_window_covering.WindowCovering.MovementStatus.Stopped ? operationalStatus.lift : operationalStatus.tilt;
    operationalStatus.global = globalStatus;
    logger.debug(
      `Operational status changed to ${import_general.Diagnostic.json(operationalStatus)} with new global status ${globalStatus}`
    );
    this.state.operationalStatus = operationalStatus;
  }
  /** Update the operational state when the target lift position changes. */
  #handleLiftTargetPositionChanging(percent100ths) {
    if (this.features.positionAwareLift) {
      this.state.operationalStatus.lift = this.#computeOperationalState(
        percent100ths,
        this.state.currentPositionLiftPercent100ths
      );
    }
  }
  /** Update the operational state when the target tilt position changes. */
  #handleTiltTargetPositionChanging(percent100ths) {
    if (this.features.positionAwareTilt) {
      this.state.operationalStatus.tilt = this.#computeOperationalState(
        percent100ths,
        this.state.currentPositionTiltPercent100ths
      );
    }
  }
  /** Sync the current lift position attributes and the operational state. */
  #syncLiftCurrentPositions(percent100ths) {
    if (this.features.positionAwareLift) {
      this.state.currentPositionLiftPercentage = percent100ths === null ? percent100ths : Math.floor(percent100ths / WC_PERCENT100THS_COEFFICIENT);
      if (this.state.operationalStatus.lift !== import_window_covering.WindowCovering.MovementStatus.Stopped && percent100ths === this.state.targetPositionLiftPercent100ths) {
        this.state.operationalStatus.lift = import_window_covering.WindowCovering.MovementStatus.Stopped;
        logger.debug("Lift movement stopped, target value reached");
      }
    }
    logger.debug(
      `Syncing lift position ${this.state.currentPositionLiftPercent100ths === null ? null : (this.state.currentPositionLiftPercent100ths / 100).toFixed(2)} to ${this.state.currentPositionLiftPercentage}%`
    );
  }
  /** Sync the current tilt position attributes and the operational state. */
  #syncTiltCurrentPositions(percent100ths) {
    if (this.features.positionAwareTilt) {
      this.state.currentPositionTiltPercentage = percent100ths === null ? percent100ths : Math.floor(percent100ths / WC_PERCENT100THS_COEFFICIENT);
      if (this.state.operationalStatus.tilt !== import_window_covering.WindowCovering.MovementStatus.Stopped && percent100ths === this.state.targetPositionTiltPercent100ths) {
        this.state.operationalStatus.tilt = import_window_covering.WindowCovering.MovementStatus.Stopped;
        logger.debug("Tilt movement stopped, target value reached");
      }
    }
    logger.debug(
      `Syncing tilt position ${this.state.currentPositionTiltPercent100ths === null ? null : (this.state.currentPositionTiltPercent100ths / 100).toFixed(2)} to ${this.state.currentPositionTiltPercentage}%`
    );
  }
  /** Compute the operational state based on the current and target position. */
  #computeOperationalState(target, current) {
    if (current === null || target === null) {
      return import_window_covering.WindowCovering.MovementStatus.Stopped;
    } else if (current === target) {
      return import_window_covering.WindowCovering.MovementStatus.Stopped;
    } else if (current < target) {
      return import_window_covering.WindowCovering.MovementStatus.Closing;
    } else {
      return import_window_covering.WindowCovering.MovementStatus.Opening;
    }
  }
  /**
   * Asserts if the device can be controlled or not because of an active Maintenance mode or a calibration is
   * required but not supported.
   */
  #assertMotionLockStatus() {
    if (this.internal.inMaintenanceMode) {
      throw new import_types.StatusResponseError("Device is in maintenance mode", import_types.StatusCode.Busy);
    }
    switch (this.internal.calibrationMode) {
      case 0 /* Enabled */:
        if (!this.internal.supportsCalibration) {
          throw new import_types.StatusResponseError("Calibration not implemented", import_types.StatusCode.Failure);
        }
        break;
      case 1 /* Running */:
        break;
      case 2 /* Disabled */:
        break;
    }
    if (!this.state.configStatus.operational) {
      throw new import_types.StatusResponseError("Device is not operational", import_types.StatusCode.Failure);
    }
  }
  /**
   * Calibrate the device.  The default implementation takes no action. Override to implement calibration if
   * necessary.
   *
   * @protected
   */
  executeCalibration() {
  }
  /**
   * Perform actual "movement".  Override to initiate movement of your device.
   * The logic tries to determine the direction to Open or Close also when a target percentage is given. The direction
   * value `DefinedByPosition` only is set if we can not determine the direction based on the current data.
   * When a `targetPercent100ths` is set (not undefined) then this is the target value to use.
   *
   * The default implementation logs and immediately updates current position to the target positions.  This is
   * probably not desirable for a real device so do not invoke `super.handleMovement()` from your implementation.
   *
   * @param type Which element should move, Lift or Tilt
   * @param reversed If the motor is configured reversed
   * @param direction The direction of the movement (Open, Close, DefinedByPosition)
   * @param targetPercent100ths Optionally the target position in percent 100ths. It depends on the used feature set
   * of the cluster if this is provided or not.
   *
   * @protected
   */
  handleMovement(type, reversed, direction, targetPercent100ths) {
    if (this.internal.disableOperationalModeHandling) {
      return;
    }
    switch (type) {
      case 0 /* Lift */:
        if (this.features.positionAwareLift) {
          if (targetPercent100ths === void 0) {
            throw new import_general.ImplementationError("Target position must be defined for position aware lift");
          }
          this.state.currentPositionLiftPercent100ths = targetPercent100ths;
        }
        break;
      case 1 /* Tilt */:
        if (this.features.positionAwareTilt) {
          if (targetPercent100ths === void 0) {
            throw new import_general.ImplementationError("Target position must be defined for position aware lift");
          }
          this.state.currentPositionTiltPercent100ths = targetPercent100ths;
        }
        break;
    }
    const directionInfo = direction === 2 /* DefinedByPosition */ ? ` in direction by position` : ` in direction ${direction === 1 /* Close */ ? "Close" : "Open"}`;
    const targetInfo = targetPercent100ths === void 0 ? "" : ` to target position ${(targetPercent100ths / 100).toFixed(2)}`;
    logger.debug(
      `Moving the device ${type === 0 /* Lift */ ? "Lift" : "Tilt"}${directionInfo} (reversed=${reversed})${targetInfo}`
    );
  }
  /**
   * Handle a movement. If calibration is supported and needed then {@link executeCalibration} runs before the actual
   * movement. The method increases the numberOfActuations* attribute and updates the operational status.
   *
   * Actual movement occurs in {@link handleMovement} as a worker. Thus, this method returns before actual movement
   * completes.
   */
  #prepareMovement(type, direction, targetPercent100ths) {
    if (this.internal.supportsCalibration && this.internal.calibrationMode === 0 /* Enabled */) {
      return this.env.runtime.add(
        (0, import_general.Worker)({
          name: `calibrating ${this}`,
          done: this.#executeCalibrationAndMove(type, direction, targetPercent100ths),
          lifetime: this.lifetime
        })
      );
    }
    if (type === 0 /* Lift */ && this.state.configStatus.liftMovementReversed) {
      logger.debug("Lift movement is reversed");
    }
    switch (type) {
      case 0 /* Lift */:
        this.state.numberOfActuationsLift = (this.state.numberOfActuationsLift ?? 0) + 1;
        if (this.features.positionAwareLift && direction === 2 /* DefinedByPosition */ && targetPercent100ths !== void 0 && this.state.currentPositionLiftPercent100ths !== null) {
          direction = targetPercent100ths > this.state.currentPositionLiftPercent100ths ? 1 /* Close */ : 0 /* Open */;
        }
        if (!this.internal.disableOperationalModeHandling && direction !== 2 /* DefinedByPosition */) {
          this.state.operationalStatus.lift = direction === 1 /* Close */ ? import_window_covering.WindowCovering.MovementStatus.Closing : import_window_covering.WindowCovering.MovementStatus.Opening;
        }
        break;
      case 1 /* Tilt */:
        this.state.numberOfActuationsTilt = (this.state.numberOfActuationsTilt ?? 0) + 1;
        if (this.features.positionAwareLift && direction === 2 /* DefinedByPosition */ && targetPercent100ths !== void 0 && this.state.currentPositionTiltPercent100ths !== null) {
          direction = targetPercent100ths > this.state.currentPositionTiltPercent100ths ? 1 /* Close */ : 0 /* Open */;
        }
        if (!this.internal.disableOperationalModeHandling && direction !== 2 /* DefinedByPosition */) {
          this.state.operationalStatus.tilt = direction === 1 /* Close */ ? import_window_covering.WindowCovering.MovementStatus.Closing : import_window_covering.WindowCovering.MovementStatus.Opening;
        }
        break;
    }
    const done = this.handleMovement(
      type,
      type === 0 /* Lift */ && !!this.state.configStatus.liftMovementReversed,
      direction,
      targetPercent100ths
    );
    if (done) {
      this.env.runtime.add(
        (0, import_general.Worker)({
          name: `moving ${this}`,
          done,
          lifetime: this.lifetime
        })
      );
    }
  }
  #executeCalibrationAndMove(type, direction, targetPercent100ths) {
    let calibration;
    if (this.internal.calibrationMode === 0 /* Enabled */ && this.internal.supportsCalibration) {
      this.internal.calibrationMode = 1 /* Running */;
      calibration = this.executeCalibration();
    }
    return import_general.MaybePromise.then(calibration, () => {
      this.internal.calibrationMode = 2 /* Disabled */;
      return this.#prepareMovement(type, direction, targetPercent100ths);
    });
  }
  /**
   * Stop device movement.  Sets the target position to the current position and updates operational state. Override
   * to implement the actual stop movement logic.
   *
   * If you update the current positions you can include the default logic via "super.handleStopMovement()".
   *
   * @protected
   */
  handleStopMovement() {
    if (this.internal.disableOperationalModeHandling) {
      return;
    }
    if (this.features.positionAwareLift) {
      this.state.targetPositionLiftPercent100ths = this.state.currentPositionLiftPercent100ths;
    }
    if (this.features.positionAwareTilt) {
      this.state.targetPositionTiltPercent100ths = this.state.currentPositionTiltPercent100ths;
    }
    if (!this.features.positionAwareLift && !this.features.positionAwareTilt) {
      this.state.operationalStatus = {
        global: import_window_covering.WindowCovering.MovementStatus.Stopped,
        lift: import_window_covering.WindowCovering.MovementStatus.Stopped,
        tilt: import_window_covering.WindowCovering.MovementStatus.Stopped
      };
    }
  }
  #triggerLiftMotion(direction, targetPercent100ths) {
    this.#prepareMovement(0 /* Lift */, direction, targetPercent100ths);
  }
  #triggerTiltMotion(direction, targetPercent100ths) {
    this.#prepareMovement(1 /* Tilt */, direction, targetPercent100ths);
  }
  /**
   * Move the WindowCovering up or open. For position aware devices the target position is set to 0%. The method calls
   * the handleMovement method to actually move the device.
   */
  upOrOpen() {
    this.#assertMotionLockStatus();
    let targetLiftPercent100ths;
    let targetTiltPercent100ths;
    if (this.features.positionAwareLift) {
      this.state.targetPositionLiftPercent100ths = targetLiftPercent100ths = WC_PERCENT100THS_MIN_OPEN;
    }
    if (this.features.positionAwareTilt) {
      this.state.targetPositionTiltPercent100ths = targetTiltPercent100ths = WC_PERCENT100THS_MIN_OPEN;
    }
    if (this.features.lift) {
      this.#triggerLiftMotion(0 /* Open */, targetLiftPercent100ths);
    }
    if (this.features.tilt) {
      this.#triggerTiltMotion(0 /* Open */, targetTiltPercent100ths);
    }
  }
  /**
   * Move the WindowCovering down or close. For position aware devices the target position is set to 100%. The method
   * calls the handleMovement method to actually move the device.
   */
  downOrClose() {
    this.#assertMotionLockStatus();
    let targetLiftPercent100ths;
    let targetTiltPercent100ths;
    if (this.features.positionAwareLift) {
      this.state.targetPositionLiftPercent100ths = targetLiftPercent100ths = WC_PERCENT100THS_MAX_CLOSED;
    }
    if (this.features.positionAwareTilt) {
      this.state.targetPositionTiltPercent100ths = targetTiltPercent100ths = WC_PERCENT100THS_MAX_CLOSED;
    }
    if (this.features.lift) {
      this.#triggerLiftMotion(1 /* Close */, targetLiftPercent100ths);
    }
    if (this.features.tilt) {
      this.#triggerTiltMotion(1 /* Close */, targetTiltPercent100ths);
    }
  }
  /**
   * Stop any movement of the WindowCovering. The method calls the handleStopMovement method to actually stop the
   * movement of the device.
   */
  stopMotion() {
    this.#assertMotionLockStatus();
    return this.handleStopMovement();
  }
  /**
   * Move the WindowCovering to a specific tilt value. The method calls the handleMovement method to actually move the
   * device to the defined position.
   */
  goToLiftPercentage({ liftPercent100thsValue }) {
    this.#assertMotionLockStatus();
    if (this.features.positionAwareLift) {
      this.state.targetPositionLiftPercent100ths = liftPercent100thsValue;
      this.#triggerLiftMotion(
        2 /* DefinedByPosition */,
        this.state.targetPositionLiftPercent100ths ?? void 0
      );
    } else {
      if (liftPercent100thsValue === 0) {
        this.upOrOpen();
      } else {
        this.downOrClose();
      }
    }
  }
  /**
   * Move the WindowCovering to a specific tilt value. The method calls the handleMovement method to actually move the
   * device to the defined position.
   */
  goToTiltPercentage({ tiltPercent100thsValue }) {
    this.#assertMotionLockStatus();
    if (this.features.positionAwareTilt) {
      this.state.targetPositionTiltPercent100ths = tiltPercent100thsValue ?? null;
      this.#triggerTiltMotion(
        2 /* DefinedByPosition */,
        this.state.targetPositionTiltPercent100ths ?? void 0
      );
    } else {
      if (tiltPercent100thsValue === 0) {
        this.upOrOpen();
      } else {
        this.downOrClose();
      }
    }
  }
}
((WindowCoveringBaseServer2) => {
  class Internal {
    /** Does the device supports calibration? */
    supportsCalibration = false;
    /** Status of the Device Calibration mode. */
    calibrationMode = 2 /* Disabled */;
    /** Status of the Device Maintenance mode. */
    inMaintenanceMode = false;
    /**
     * Disable OperationalMode and position value management.
     * This requires the device developer to set all these states (operationalMode, percentage and
     * absolute values according to the feature set) according to the Matter specification!
     */
    disableOperationalModeHandling = false;
  }
  WindowCoveringBaseServer2.Internal = Internal;
  class State extends WindowCoveringBase.State {
    /** Does the device supports maintenance mode? */
    supportsMaintenanceMode = true;
  }
  WindowCoveringBaseServer2.State = State;
})(WindowCoveringBaseServer || (WindowCoveringBaseServer = {}));
class WindowCoveringServer extends WindowCoveringBaseServer.for((0, import_types.ClusterType)(import_window_covering.WindowCovering.Base)) {
}
//# sourceMappingURL=WindowCoveringServer.js.map
