"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
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 InterviewProgress_exports = {};
__export(InterviewProgress_exports, {
  InterviewProgressMixin: () => InterviewProgressMixin
});
module.exports = __toCommonJS(InterviewProgress_exports);
var import_ConfigurationCC = require("@zwave-js/cc/ConfigurationCC");
var import_core = require("@zwave-js/core");
var import_shared = require("@zwave-js/shared");
var import_math = require("alcalzone-shared/math");
var import_DeviceConfig = require("./80_DeviceConfig.js");
const INTERVIEW_PROGRESS_CC_BAND_START = 2;
const INTERVIEW_PROGRESS_CC_BAND_END = 99;
const INTERVIEW_PROGRESS_DISCOVERY_CAP = 30;
const INTERVIEW_PROGRESS_DISCOVERY_UNIT = 0.4;
const ASSUMED_CONFIG_PARAM_COUNT = 30;
function interviewStageStartProgress(stage) {
  switch (stage) {
    case import_core.InterviewStage.NodeInfo:
      return 1;
    case import_core.InterviewStage.CommandClasses:
      return INTERVIEW_PROGRESS_CC_BAND_START;
    case import_core.InterviewStage.OverwriteConfig:
      return INTERVIEW_PROGRESS_CC_BAND_END;
    case import_core.InterviewStage.Complete:
      return 100;
    default:
      return void 0;
  }
}
__name(interviewStageStartProgress, "interviewStageStartProgress");
class InterviewProgressMixin extends import_DeviceConfig.DeviceConfigMixin {
  static {
    __name(this, "InterviewProgressMixin");
  }
  /** The last reported overall interview progress in percent [0, 100]. Monotonically non-decreasing. */
  _interviewProgress = 0;
  // Working state for the CommandClasses interview stage only; reset between CC interviews.
  /** Whether the CC interview has reached the bulk phase, where the total amount of work is known */
  _ccInterviewBulkPhase = false;
  /** Total remaining CC interview weight at the start of the bulk phase */
  _ccInterviewBulkRemainingWeight = 0;
  /** Width of the progress band [`_interviewProgress` … CC band end] available to the bulk phase */
  _ccInterviewBulkBand = 0;
  /** Start of the progress slice allotted to the CC currently being interviewed */
  _ccInterviewStepStart = 0;
  /** Width of the progress slice allotted to the CC currently being interviewed */
  _ccInterviewStepWidth = 0;
  _ccInterviewCurrentEndpoint;
  _ccInterviewCurrentCC;
  emitInterviewProgress = (0, import_shared.throttle)(
    (progress) => this._emit("interview progress", this, progress),
    250,
    // trailing=true so the last update of a burst is never dropped
    true
  );
  emitInterviewProgressUpdate(stage) {
    this.emitInterviewProgress({
      stage,
      progress: (0, import_math.roundTo)(this._interviewProgress, 2),
      endpoint: this._ccInterviewCurrentEndpoint,
      commandClass: this._ccInterviewCurrentCC
    });
  }
  /**
   * Resets the overall progress baseline to 0 for a fresh interview and emits an initial
   * progress event (stage `None`). This gives consumers a meaningful 0% anchor right away,
   * even if the first interview stage never starts.
   */
  resetInterviewProgressBaseline() {
    this._interviewProgress = 0;
    this._ccInterviewCurrentEndpoint = void 0;
    this._ccInterviewCurrentCC = void 0;
    this.emitInterviewProgressUpdate(import_core.InterviewStage.None);
  }
  /**
   * Resets the per-stage progress tracking at the start of the CC interview.
   * The overall progress baseline is intentionally NOT reset here, so it stays monotonic
   * across a resumed interview. It is reset for a fresh interview in `interviewInternal`.
   */
  resetCCInterviewProgress() {
    this._ccInterviewBulkPhase = false;
    this._ccInterviewBulkRemainingWeight = 0;
    this._ccInterviewBulkBand = 0;
    this._ccInterviewStepStart = 0;
    this._ccInterviewStepWidth = 0;
    this._ccInterviewCurrentEndpoint = void 0;
    this._ccInterviewCurrentCC = void 0;
    this._interviewProgress = Math.max(this._interviewProgress, INTERVIEW_PROGRESS_CC_BAND_START);
  }
  /**
   * Sets up proportional progress distribution for the bulk phase of the CC interview,
   * which is reached once the complete CC inventory is known. From here, progress is
   * distributed proportionally to the remaining interview weight, summed across every
   * not-yet-interviewed CC of the root device and all endpoints.
   */
  setupCCInterviewBulkProgress(...remainingCCs) {
    let remainingWeight = 0;
    for (const ccs of remainingCCs) {
      for (const cc of ccs) {
        remainingWeight += this.ccInterviewWeight(cc);
      }
    }
    this._ccInterviewBulkPhase = true;
    this._ccInterviewBulkRemainingWeight = remainingWeight;
    this._ccInterviewBulkBand = Math.max(0, INTERVIEW_PROGRESS_CC_BAND_END - this._interviewProgress);
  }
  /**
   * The relative weight of a CC's interview, used to distribute interview progress more
   * realistically. The weight approximates the number of device queries the CC's interview
   * performs, so the progress bar advances at a roughly constant per-query rate. Most CCs are
   * quick (weight 1); CCs that iterate over a runtime-enumerated collection get a higher weight
   * derived from the typical size of that collection.
   *
   * Typical collection sizes used to derive the weights below (median across a survey of ~30
   * certified products in the Z-Wave Alliance product database, plus some common sense):
   *
   *   | Collection                          | Typical | Affected CC(s)                                            |
   *   | ----------------------------------- | ------- | --------------------------------------------------------- |
   *   | Configuration parameters            | ~30*    | Configuration (*fallback when no config file)             |
   *   | Supported CCs (queried for version) | ~20     | Version                                                   |
   *   | Association groups                  | ~5      | Association, Association Group Information,               |
   *   |                                     |         | Multi Channel Association, Scene Controller Configuration |
   *   | Notification types                  | ~3      | Notification                                              |
   *   | Multi Channel endpoints             | ~3      | Multi Channel                                             |
   *   | Multilevel Sensor types             | ~3      | Multilevel Sensor                                         |
   *   | Meter scales/rate types             | ~4      | Meter                                                     |
   *   | Alarm Sensor types                  | ~2      | Alarm Sensor                                              |
   *   | Binary Sensor types                 | ~1      | Binary Sensor                                             |
   *   | User codes / credentials            | ~25     | User Code (opt-in only), User Credential                  |
   */
  ccInterviewWeight(cc) {
    switch (cc) {
      case import_core.CommandClasses.Configuration:
        return this.configurationCCInterviewWeight();
      case import_core.CommandClasses["User Credential"]:
        return 25;
      case import_core.CommandClasses["User Code"]:
        return this.driver.getInterviewOptions()?.queryAllUserCodes ? 25 : 2;
      case import_core.CommandClasses.Version:
        return 20;
      case import_core.CommandClasses["Association Group Information"]:
        return 10;
      case import_core.CommandClasses.Notification:
        return 7;
      case import_core.CommandClasses.Association:
      case import_core.CommandClasses["Multi Channel Association"]:
      case import_core.CommandClasses["Multilevel Sensor"]:
        return 6;
      case import_core.CommandClasses["Scene Controller Configuration"]:
      case import_core.CommandClasses.Meter:
      case import_core.CommandClasses.Irrigation:
        return 5;
      case import_core.CommandClasses["Color Switch"]:
      case import_core.CommandClasses["Thermostat Setpoint"]:
      case import_core.CommandClasses["Multi Channel"]:
        return 4;
      case import_core.CommandClasses.Indicator:
      case import_core.CommandClasses["Barrier Operator"]:
        return 3;
      case import_core.CommandClasses.Security:
      case import_core.CommandClasses["Security 2"]:
        return 2;
      case import_core.CommandClasses["Window Covering"]:
      case import_core.CommandClasses.Battery:
      case import_core.CommandClasses["Central Scene"]:
      case import_core.CommandClasses["Node Naming and Location"]:
      case import_core.CommandClasses["Sound Switch"]:
      case import_core.CommandClasses["Entry Control"]:
      case import_core.CommandClasses["Door Lock"]:
      case import_core.CommandClasses["Wake Up"]:
      case import_core.CommandClasses["Energy Production"]:
      case import_core.CommandClasses["Alarm Sensor"]:
      case import_core.CommandClasses["Thermostat Mode"]:
      case import_core.CommandClasses["Thermostat Fan Mode"]:
      case import_core.CommandClasses["Thermostat Fan State"]:
      case import_core.CommandClasses["Thermostat Operating State"]:
      case import_core.CommandClasses["Thermostat Setback"]:
        return 2;
      default:
        return 1;
    }
  }
  /**
   * The interview weight of the Configuration CC scales with the number of parameters that get
   * queried. When they are known up front from the device config file, the weight is the count
   * of distinct parameters, otherwise we assume a typical count.
   */
  configurationCCInterviewWeight() {
    const paramInfo = this.deviceConfig?.paramInformation ?? this.deviceConfig?.endpoints?.get(0)?.paramInformation;
    if (!paramInfo?.size)
      return ASSUMED_CONFIG_PARAM_COUNT;
    return (0, import_ConfigurationCC.getDistinctConfigParameters)(paramInfo).length;
  }
  /** Computes the progress slice width for the CC that is about to be interviewed */
  computeCCInterviewStepWidth(cc) {
    if (this._ccInterviewBulkPhase) {
      if (this._ccInterviewBulkRemainingWeight <= 0)
        return 0;
      const width = this.ccInterviewWeight(cc) / this._ccInterviewBulkRemainingWeight * this._ccInterviewBulkBand;
      return Math.max(0, Math.min(width, INTERVIEW_PROGRESS_CC_BAND_END - this._ccInterviewStepStart));
    } else {
      const width = this.ccInterviewWeight(cc) * INTERVIEW_PROGRESS_DISCOVERY_UNIT;
      return Math.max(0, Math.min(width, INTERVIEW_PROGRESS_DISCOVERY_CAP - this._ccInterviewStepStart));
    }
  }
  /** Reserves a progress slice for the CC that is about to be interviewed */
  reserveCCInterviewStepProgress(endpoint, cc) {
    this._ccInterviewCurrentEndpoint = endpoint;
    this._ccInterviewCurrentCC = cc;
    this._ccInterviewStepStart = this._interviewProgress;
    this._ccInterviewStepWidth = this.computeCCInterviewStepWidth(cc);
  }
  /** Completes the current CC's progress slice and emits an update */
  completeCCInterviewStepProgress() {
    this._interviewProgress = Math.max(this._interviewProgress, this._ccInterviewStepStart + this._ccInterviewStepWidth);
    this.emitInterviewProgressUpdate(import_core.InterviewStage.CommandClasses);
    this._ccInterviewCurrentEndpoint = void 0;
    this._ccInterviewCurrentCC = void 0;
  }
  /**
   * Advances the overall progress to the start of the given interview stage and emits an update
   * reporting that stage as currently in progress.
   */
  reportInterviewStageStarted(stage) {
    const stageProgress = interviewStageStartProgress(stage);
    if (stageProgress == void 0)
      return;
    this._ccInterviewCurrentEndpoint = void 0;
    this._ccInterviewCurrentCC = void 0;
    this._interviewProgress = Math.max(this._interviewProgress, stageProgress);
    this.emitInterviewProgressUpdate(stage);
  }
  /**
   * Returns the assumed interview weight of the given CC, used by CCs to estimate their own
   * interview progress when the actual amount of work is not known up front.
   * Implements the {@link ReportInterviewProgress} trait.
   */
  getInterviewProgressWeight(cc) {
    return this.ccInterviewWeight(cc);
  }
  /**
   * Reports fine-grained progress from within the interview of the CC that is currently
   * being interviewed. Called by CCs (via `getNode(ctx)`) to keep the overall progress moving
   * during long-running interview steps. Implements the {@link ReportInterviewProgress} trait.
   */
  reportInterviewProgress(completed, total) {
    if (this._ccInterviewCurrentCC == void 0)
      return;
    const fraction = total > 0 ? Math.min(1, Math.max(0, completed / total)) : 0;
    const progress = this._ccInterviewStepStart + this._ccInterviewStepWidth * fraction;
    if (progress > this._interviewProgress) {
      this._interviewProgress = progress;
      this.emitInterviewProgressUpdate(import_core.InterviewStage.CommandClasses);
    }
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  InterviewProgressMixin
});
//# sourceMappingURL=90_InterviewProgress.js.map
