import { unsafeCSS, LitElement, html } from "lit";
import { property, query } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import componentStyle from "./slider-double.css.js";
import "../icon-button/icon-button.js";
import { classMap } from "lit/directives/class-map.js";
import { customElement } from "../../decorator.js";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result) __defProp(target, key, result);
  return result;
};
var ObcSliderDoubleVariant = /* @__PURE__ */ ((ObcSliderDoubleVariant2) => {
  ObcSliderDoubleVariant2["Normal"] = "normal";
  ObcSliderDoubleVariant2["Enhanced"] = "enhanced";
  ObcSliderDoubleVariant2["NoInput"] = "no-input";
  return ObcSliderDoubleVariant2;
})(ObcSliderDoubleVariant || {});
let ObcSliderDouble = class extends LitElement {
  constructor() {
    super(...arguments);
    this.low = 0;
    this.high = 100;
    this.min = 0;
    this.max = 100;
    this.stepClick = 10;
    this.variant = "normal";
    this.allowSeeking = false;
    this.disabled = false;
    this.seekingSpeed = 1 / 3;
    this.labelUnit = "";
    this.labelDecimals = 0;
    this.labelWidth = "60px";
    this.animationFrame = null;
    this.isMouseDown = false;
    this.targetValue = 0;
    this.isDragging = false;
    this.isTargetingLow = false;
    this.animationStartTime = null;
    this.animationStartValue = 0;
    this.THUMB_WIDTH = 48;
    this.THUMB_VISIBLE_WIDTH = 12;
    this.onWindowMouseMove = (e) => {
      this.onMouseMove(e);
    };
    this.onWindowMouseUp = () => {
      this.onMouseUp();
    };
  }
  /**
   * Handles input changes from the slider thumbs.
   * Updates the low and high values and emits the `value` event.
   *
   * @fires value {ObcSliderDoubleValueEvent}
   */
  onInput() {
    let newLow = parseFloat(this.minInput.value);
    let newHigh = parseFloat(this.maxInput.value);
    if (newLow > this.high) {
      newLow = this.high;
      this.minInput.value = this.high.toString();
    }
    if (newHigh < this.low) {
      newHigh = this.low;
      this.maxInput.value = this.low.toString();
    }
    this.low = newLow;
    this.high = newHigh;
    this.dispatchEvent(
      new CustomEvent("value", { detail: { low: this.low, high: this.high } })
    );
  }
  lowClickValue(e) {
    const rect = this.minInput.getBoundingClientRect();
    const left = rect.left + this.THUMB_WIDTH - this.THUMB_VISIBLE_WIDTH / 2;
    const width = rect.width - this.THUMB_WIDTH;
    const percent = (e.clientX - left) / width;
    return this.min + (this.max - this.min) * percent;
  }
  highClickValue(e) {
    const rect = this.maxInput.getBoundingClientRect();
    const left = rect.left + this.THUMB_VISIBLE_WIDTH / 2;
    const width = rect.width - this.THUMB_WIDTH;
    const percent = (e.clientX - left) / width;
    return this.min + (this.max - this.min) * percent;
  }
  thumbRange() {
    const rect = this.minInput.getBoundingClientRect();
    const valueRange = this.max - this.min;
    const percentageRange = this.THUMB_WIDTH / rect.width;
    return valueRange * percentageRange;
  }
  isClickingMinThumb(e) {
    const value = this.lowClickValue(e);
    if (value > this.low) {
      return false;
    }
    return value >= this.low - this.thumbRange();
  }
  isClickingMaxThumb(e) {
    const value = this.highClickValue(e);
    if (value < this.high) {
      return false;
    }
    return value <= this.high + this.thumbRange();
  }
  isClickingLowTrack(e) {
    const value = this.lowClickValue(e);
    return value <= this.low - this.thumbRange() / 2;
  }
  isClickingHighTrack(e) {
    const value = this.highClickValue(e);
    return value >= this.high + this.thumbRange() / 2;
  }
  isClosestToLowThumb(e) {
    const lowValue = this.lowClickValue(e);
    const highValue = this.highClickValue(e);
    return Math.abs(lowValue - this.low) <= Math.abs(highValue - this.high);
  }
  onMouseDown(e) {
    if (this.variant === "no-input" || this.disabled)
      return;
    if (this.isClickingMinThumb(e)) {
      this.isTargetingLow = true;
      this.isDragging = true;
    } else if (this.isClickingMaxThumb(e)) {
      this.isTargetingLow = false;
      this.isDragging = true;
    } else if (this.isClickingLowTrack(e)) {
      this.isTargetingLow = true;
      this.isDragging = false;
    } else if (this.isClickingHighTrack(e)) {
      this.isTargetingLow = false;
      this.isDragging = false;
    } else if (this.isClosestToLowThumb(e)) {
      this.isTargetingLow = true;
      this.isDragging = false;
    } else {
      this.isTargetingLow = false;
      this.isDragging = false;
    }
    this.isMouseDown = true;
    this.updateTargetValue(e);
    e.preventDefault();
    window.addEventListener("mousemove", this.onWindowMouseMove);
    window.addEventListener("mouseup", this.onWindowMouseUp);
    this.startAnimation();
  }
  onMouseMove(e) {
    if (this.isMouseDown) {
      this.updateTargetValue(e);
    }
  }
  onMouseUp() {
    this.isMouseDown = false;
    this.isDragging = false;
    window.removeEventListener("mousemove", this.onWindowMouseMove);
    window.removeEventListener("mouseup", this.onWindowMouseUp);
    this.stopAnimation();
  }
  updateTargetValue(e) {
    const unroundedValue = this.isTargetingLow ? this.lowClickValue(e) : this.highClickValue(e);
    if (this.step) {
      this.targetValue = Math.round(unroundedValue / this.step) * this.step;
    } else {
      this.targetValue = unroundedValue;
    }
    if (this.isTargetingLow) {
      this.targetValue = Math.max(
        this.min,
        Math.min(this.targetValue, this.high)
      );
    } else {
      this.targetValue = Math.min(
        this.max,
        Math.max(this.targetValue, this.low)
      );
    }
  }
  startAnimation() {
    this.isDragging = this.allowSeeking;
    this.animationStartTime = performance.now();
    this.animationStartValue = this.isTargetingLow ? this.low : this.high;
    const min = this.min;
    const max = this.max;
    const step = this.step;
    const duration = 1 / this.seekingSpeed * 1e3;
    const direction = this.targetValue > this.animationStartValue ? 1 : -1;
    const animate = () => {
      let nextValue = this.targetValue;
      if (!this.isDragging) {
        const now = performance.now();
        const elapsed = now - (this.animationStartTime ?? now);
        const range = Math.abs(max - min);
        const expectedProgress = Math.min(elapsed / duration, 1);
        const expectedValue = this.animationStartValue + direction * range * expectedProgress;
        if (direction > 0) {
          nextValue = step === void 0 ? expectedValue : Math.ceil(expectedValue - min / step) * step + min;
          nextValue = Math.min(this.targetValue, nextValue);
        } else {
          nextValue = step === void 0 ? expectedValue : Math.floor((expectedValue - min) / step) * step + min;
          nextValue = Math.max(this.targetValue, nextValue);
        }
      }
      const isValueChanged = this.isTargetingLow ? this.low !== nextValue : this.high !== nextValue;
      if (isValueChanged) {
        if (this.isTargetingLow) {
          this.low = nextValue;
        } else {
          this.high = nextValue;
        }
        this.dispatchEvent(
          new CustomEvent("value", { detail: { low: this.low, high: this.high } })
        );
      }
      if (direction > 0 && nextValue < this.targetValue || direction < 0 && nextValue > this.targetValue) {
        this.animationFrame = requestAnimationFrame(animate);
      } else if (this.isMouseDown) {
        this.animationStartTime = performance.now();
        this.animationStartValue = this.isTargetingLow ? this.low : this.high;
        this.animationFrame = requestAnimationFrame(animate);
        this.isDragging = true;
      }
    };
    this.animationFrame = requestAnimationFrame(animate);
  }
  stopAnimation() {
    if (this.animationFrame !== null) {
      cancelAnimationFrame(this.animationFrame);
      this.animationFrame = null;
    }
  }
  formatLabel(value) {
    return value.toFixed(this.labelDecimals) + this.labelUnit;
  }
  render() {
    return html`
      <div
        class=${classMap({ label: true, min: true, disabled: this.disabled })}
        style="width: ${this.labelWidth};"
      >
        ${this.formatLabel(this.low)}
      </div>
      <div
        class=${classMap({
      wrapper: true,
      [this.variant]: true,
      mouseDown: this.isMouseDown,
      dragging: this.isDragging,
      disabled: this.disabled
    })}
        @mousedown=${this.onMouseDown}
        @mouseup=${this.onMouseUp}
        @mousemove=${this.onMouseMove}
      >
        <div class="track"></div>
        <input
          type="range"
          min=${this.min}
          max=${this.max}
          class="slider min"
          step=${ifDefined(this.step)}
          .value=${this.low.toString()}
          ?disabled=${this.variant === "no-input" || this.disabled}
          @input=${this.onInput}
        />
        <input
          type="range"
          class="slider max"
          min=${this.min}
          max=${this.max}
          step=${ifDefined(this.step)}
          .value=${this.high.toString()}
          ?disabled=${this.variant === "no-input" || this.disabled}
          @input=${this.onInput}
        />
        <div class="interactive-track"></div>
        <div class="thumb min"></div>
        <div class="thumb max"></div>
      </div>
      <div
        class=${classMap({ label: true, max: true, disabled: this.disabled })}
        style="width: ${this.labelWidth};"
      >
        ${this.formatLabel(this.high)}
      </div>
    `;
  }
};
ObcSliderDouble.styles = unsafeCSS(componentStyle);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "low", 2);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "high", 2);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "min", 2);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "max", 2);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "step", 2);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "stepClick", 2);
__decorateClass([
  property({ type: String })
], ObcSliderDouble.prototype, "variant", 2);
__decorateClass([
  property({ type: Boolean })
], ObcSliderDouble.prototype, "allowSeeking", 2);
__decorateClass([
  property({ type: Boolean })
], ObcSliderDouble.prototype, "disabled", 2);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "seekingSpeed", 2);
__decorateClass([
  property({ type: String })
], ObcSliderDouble.prototype, "labelUnit", 2);
__decorateClass([
  property({ type: Number })
], ObcSliderDouble.prototype, "labelDecimals", 2);
__decorateClass([
  property({ type: String })
], ObcSliderDouble.prototype, "labelWidth", 2);
__decorateClass([
  query('input[type="range"].min')
], ObcSliderDouble.prototype, "minInput", 2);
__decorateClass([
  query('input[type="range"].max')
], ObcSliderDouble.prototype, "maxInput", 2);
ObcSliderDouble = __decorateClass([
  customElement("obc-slider-double")
], ObcSliderDouble);
export {
  ObcSliderDouble,
  ObcSliderDoubleVariant
};
//# sourceMappingURL=slider-double.js.map
