import { LitElement, html, unsafeCSS } from "lit";
import { property } from "lit/decorators.js";
import componentStyle from "./poi-base.css.js";
import { ObcPoiType, ObcPoiState, ObcPoiValue, POI_ATTR, PoiDataVisualRectPreference } from "./poi.js";
import { ObcPoiButtonType } from "../poi-button/poi-button.js";
import { ObcPoiHeaderState } from "../poi-header/poi-header.js";
var __defProp = Object.defineProperty;
var __decorateClass = (decorators, target, key, kind) => {
  var result = void 0;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = decorator(target, key, result) || result;
  if (result) __defProp(target, key, result);
  return result;
};
function stripWhitespaceTextNodes(el) {
  for (const node of Array.from(el.childNodes)) {
    if (node.nodeType === Node.TEXT_NODE && (node.textContent ?? "").trim() === "") {
      node.remove();
    }
  }
}
const X_FILTER_CUTOFF_HZ = 16;
const X_FILTER_DEADBAND_PX = 0.1;
const X_MOVING_HINT_MS = 120;
const VALID_POI_TYPES = new Set(Object.values(ObcPoiType));
const VALID_POI_STATES = new Set(Object.values(ObcPoiState));
const _ObcPoiBase = class _ObcPoiBase extends LitElement {
  constructor() {
    super(...arguments);
    this.type = ObcPoiType.Line;
    this.value = ObcPoiValue.Unchecked;
    this.state = ObcPoiState.Enabled;
    this.selected = false;
    this.buttonType = ObcPoiButtonType.Button;
    this.overlapOpaque = false;
    this.data = [];
    this.hasHeader = false;
    this.headerContent = "";
    this.hasPointer = false;
    this.pointerType = null;
    this.pointerState = null;
    this.relativeDirection = 0;
    this.x = 0;
    this.y = 192;
    this.buttonY = null;
    this.fixedTarget = false;
    this.buttonOffsetX = 0;
    this.targetOffsetX = 0;
    this.boxWidth = null;
    this.boxHeight = null;
    this.lineCompensationY = 0;
    this.outsideAngle = 315;
    this.animatePosition = false;
    this.filteredX = 0;
    this.xFilterTarget = 0;
    this.xFilterInitialized = false;
    this.lastXFilterTimestampMs = 0;
    this.xFilterRaf = 0;
    this.xMovingHintTimeout = null;
    this.stepXFilter = (nowMs) => {
      this.xFilterRaf = 0;
      if (!this.isConnected || !this.xFilterInitialized) {
        return;
      }
      const dtSeconds = this.lastXFilterTimestampMs > 0 ? Math.min(
        0.25,
        Math.max(1 / 120, (nowMs - this.lastXFilterTimestampMs) / 1e3)
      ) : 1 / 60;
      this.lastXFilterTimestampMs = nowMs;
      const alpha = 1 - Math.exp(-2 * Math.PI * X_FILTER_CUTOFF_HZ * dtSeconds);
      const delta = this.xFilterTarget - this.filteredX;
      const nextX = Math.abs(delta) <= X_FILTER_DEADBAND_PX ? this.xFilterTarget : this.filteredX + delta * alpha;
      const changed = Math.abs(nextX - this.filteredX) > 1e-6;
      this.filteredX = nextX;
      if (changed) {
        this.style.setProperty("--obc-poi-data-x", `${this.filteredX}px`);
        this.dispatchLayoutChange();
      }
      const settled = Math.abs(this.xFilterTarget - this.filteredX) <= X_FILTER_DEADBAND_PX;
      if (settled) {
        this.filteredX = this.xFilterTarget;
        this.style.setProperty("--obc-poi-data-x", `${this.filteredX}px`);
        this.lastXFilterTimestampMs = 0;
        this.markXMoving();
        return;
      }
      this.markXMoving();
      this.xFilterRaf = requestAnimationFrame(this.stepXFilter);
    };
  }
  connectedCallback() {
    stripWhitespaceTextNodes(this);
    super.connectedCallback();
    this.setAttribute(POI_ATTR, "");
    this.setupHeaderObserver();
  }
  /* ---------- Layout change ---------- */
  dispatchLayoutChange() {
    this.dispatchEvent(
      new CustomEvent("obc-poi-data-layout-change", {
        bubbles: true,
        composed: true
      })
    );
  }
  syncXFilterTarget(nextX) {
    this.xFilterTarget = nextX;
    if (!this.xFilterInitialized) {
      this.xFilterInitialized = true;
      this.filteredX = nextX;
      this.style.setProperty("--obc-poi-data-x", `${this.filteredX}px`);
      this.markXMoving();
      return;
    }
    if (Math.abs(nextX - this.filteredX) <= X_FILTER_DEADBAND_PX) {
      this.filteredX = this.xFilterTarget;
      this.style.setProperty("--obc-poi-data-x", `${this.filteredX}px`);
      if (this.xFilterRaf) {
        cancelAnimationFrame(this.xFilterRaf);
        this.xFilterRaf = 0;
      }
      this.lastXFilterTimestampMs = 0;
      this.markXMoving();
      return;
    }
    if (!this.xFilterRaf) {
      this.lastXFilterTimestampMs = 0;
      this.markXMoving();
      this.xFilterRaf = requestAnimationFrame(this.stepXFilter);
    }
  }
  markXMoving() {
    this.setAttribute("data-x-moving", "true");
    if (this.xMovingHintTimeout !== null) {
      window.clearTimeout(this.xMovingHintTimeout);
    }
    this.xMovingHintTimeout = window.setTimeout(() => {
      this.removeAttribute("data-x-moving");
      this.xMovingHintTimeout = null;
    }, X_MOVING_HINT_MS);
  }
  /* ---------- Lifecycle ---------- */
  disconnectedCallback() {
    super.disconnectedCallback();
    this.headerObserver?.disconnect();
    this.headerObserver = void 0;
    if (this.xFilterRaf) {
      cancelAnimationFrame(this.xFilterRaf);
      this.xFilterRaf = 0;
    }
    this.lastXFilterTimestampMs = 0;
    if (this.xMovingHintTimeout !== null) {
      window.clearTimeout(this.xMovingHintTimeout);
      this.xMovingHintTimeout = null;
    }
  }
  setupHeaderObserver() {
    this.headerObserver?.disconnect();
    this.headerObserver = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        if (mutation.type === "childList") {
          this.syncHeaderContent();
          this.syncHeaderState();
          return;
        }
        if (mutation.type === "attributes" && mutation.target instanceof HTMLElement && mutation.target.getAttribute("slot") === "header") {
          this.syncHeaderContent();
          this.syncHeaderState();
          return;
        }
      }
    });
    this.headerObserver.observe(this, {
      childList: true,
      attributes: true,
      attributeFilter: ["slot"]
    });
  }
  updated(changedProperties) {
    if (changedProperties.has("x")) {
      this.syncXFilterTarget(Number.isFinite(this.x) ? this.x : 0);
    }
    if (changedProperties.has("buttonY") || changedProperties.has("y") || changedProperties.has("fixedTarget") || changedProperties.has("lineCompensationY") || changedProperties.has("selected") || changedProperties.has("type")) {
      this.updatePosition();
    }
    if (changedProperties.has("x") || changedProperties.has("buttonY") || changedProperties.has("y") || changedProperties.has("buttonOffsetX") || changedProperties.has("targetOffsetX") || changedProperties.has("lineCompensationY") || changedProperties.has("fixedTarget") || changedProperties.has("selected") || changedProperties.has("type")) {
      this.dispatchLayoutChange();
    }
    this.syncHeaderContent();
    this.syncHeaderState();
  }
  firstUpdated(_changedProperties) {
    this.syncHeaderContent();
    this.syncHeaderState();
  }
  /* ---------- Vertical positioning ---------- */
  updatePosition() {
    if (this.fixedTarget) {
      if (typeof this.buttonY === "number" && Number.isFinite(this.buttonY)) {
        this.style.top = `${this.buttonY - this.effectiveLineLength}px`;
      } else {
        this.style.removeProperty("top");
      }
    } else if (typeof this.buttonY === "number" && Number.isFinite(this.buttonY)) {
      this.style.top = `${this.buttonY}px`;
    } else {
      this.style.removeProperty("top");
    }
  }
  get selectedVerticalOffset() {
    if (!this.selected) {
      return 0;
    }
    const offset = getComputedStyle(this).getPropertyValue(
      "--obc-poi-target-selected-vertical-offset"
    );
    const parsed = Number.parseFloat(offset);
    return Number.isFinite(parsed) ? parsed : 0;
  }
  get layerVerticalOffset() {
    const offset = getComputedStyle(this).getPropertyValue(
      "--obc-poi-target-layer-vertical-offset"
    );
    const parsed = Number.parseFloat(offset);
    return Number.isFinite(parsed) ? parsed : 0;
  }
  get resolvedPoiType() {
    return VALID_POI_TYPES.has(this.type) ? this.type : ObcPoiType.Line;
  }
  get resolvedPoiState() {
    return VALID_POI_STATES.has(this.state) ? this.state : ObcPoiState.Enabled;
  }
  /* ---------- Poi — visual query ---------- */
  /**
   * Override in each variant to query the correct inner component tag names.
   * Returns the poi wrapper element and poi-button element from the shadow DOM.
   */
  getVisualNodes() {
    return this.queryVisualNodes("obc-poi", "obc-poi-button");
  }
  /**
   * Helper for common `getVisualNodes()` pattern: query poi tag in own shadow,
   * then button tag. When the button is slotted into poi (aton/vessel variants),
   * it lives in this variant's shadow root. When using the fallback (data variant),
   * it lives inside poi's shadow root.
   */
  queryVisualNodes(poiTag, buttonTag) {
    const poi = this.shadowRoot?.querySelector(poiTag);
    const button = this.shadowRoot?.querySelector(buttonTag) ?? poi?.shadowRoot?.querySelector(buttonTag);
    const buttonShadow = button?.shadowRoot ?? null;
    const buttonWrapper = buttonShadow?.querySelector(
      ".button-wrapper"
    );
    const wrapper = buttonShadow?.querySelector(
      ".wrapper"
    );
    return { poi, button, wrapper, buttonWrapper };
  }
  getVisualRect(preference = PoiDataVisualRectPreference.Largest) {
    const { poi, button, wrapper, buttonWrapper } = this.getVisualNodes();
    const candidates = [wrapper, buttonWrapper, button, poi].filter(
      (element) => !!element
    );
    if (preference === PoiDataVisualRectPreference.Group) {
      const hasDataWrapper = wrapper?.classList.contains("has-data") ?? false;
      return (hasDataWrapper ? wrapper?.getBoundingClientRect() : null) ?? buttonWrapper?.getBoundingClientRect() ?? wrapper?.getBoundingClientRect() ?? button?.getBoundingClientRect() ?? poi?.getBoundingClientRect() ?? this.getBoundingClientRect();
    }
    if (preference === PoiDataVisualRectPreference.Anchor) {
      return buttonWrapper?.getBoundingClientRect() ?? button?.getBoundingClientRect() ?? wrapper?.getBoundingClientRect() ?? poi?.getBoundingClientRect() ?? this.getBoundingClientRect();
    }
    if (preference === PoiDataVisualRectPreference.Size) {
      return wrapper?.getBoundingClientRect() ?? buttonWrapper?.getBoundingClientRect() ?? button?.getBoundingClientRect() ?? poi?.getBoundingClientRect() ?? this.getBoundingClientRect();
    }
    if (candidates.length === 0) {
      return this.getBoundingClientRect();
    }
    const candidateRects = candidates.map(
      (element) => element.getBoundingClientRect()
    );
    return candidateRects.reduce(
      (best, rect) => rect.height > best.height ? rect : best
    );
  }
  getVisualElement(preference = PoiDataVisualRectPreference.Size) {
    const { poi, button, wrapper, buttonWrapper } = this.getVisualNodes();
    if (preference === PoiDataVisualRectPreference.Group) {
      const hasDataWrapper = wrapper?.classList.contains("has-data") ?? false;
      return (hasDataWrapper ? wrapper : null) ?? buttonWrapper ?? wrapper ?? button ?? poi ?? this;
    }
    if (preference === PoiDataVisualRectPreference.Anchor) {
      return buttonWrapper ?? button ?? wrapper ?? poi ?? this;
    }
    return wrapper ?? buttonWrapper ?? button ?? poi ?? this;
  }
  getPointerElement() {
    const { poi } = this.getVisualNodes();
    return poi?.shadowRoot?.querySelector(
      "obc-poi-pointer.pointer"
    ) ?? null;
  }
  /* ---------- Render helpers ---------- */
  /** Computed effective line length accounting for compensation and vertical offsets. */
  get effectiveLineLength() {
    const resolvedPoiType = this.resolvedPoiType;
    const lineLength = Number.isFinite(this.y) ? this.y : 0;
    const lineCompensation = Number.isFinite(this.lineCompensationY) ? this.lineCompensationY : 0;
    const totalVerticalOffset = this.selectedVerticalOffset + this.layerVerticalOffset;
    return resolvedPoiType === ObcPoiType.Line || resolvedPoiType === ObcPoiType.Offset ? lineLength + lineCompensation + totalVerticalOffset : lineLength;
  }
  /** Computed effective local button Y offset. */
  get effectiveLocalButtonY() {
    const totalVerticalOffset = this.selectedVerticalOffset + this.layerVerticalOffset;
    return -totalVerticalOffset;
  }
  /**
   * Override in each variant to render the button slot content
   * placed inside `<obc-poi>`. The base class provides the outer
   * `<obc-poi>` wrapper with all shared property bindings.
   */
  renderButtonSlot() {
    return html`
      <obc-poi-button
        slot="button"
        exportparts="icon"
        .relativeDirection=${this.relativeDirection}
        .selected=${this.selected}
        .hasHeader=${this.hasHeader}
        .headerContent=${this.headerContent}
        .state=${this.resolvedPoiState}
        .value=${this.value}
        .overlapOpaque=${this.overlapOpaque}
        .type=${this.buttonType}
        .data=${this.data}
      >
        <slot></slot>
      </obc-poi-button>
    `;
  }
  get resolvedHeaderState() {
    switch (this.resolvedPoiState) {
      case ObcPoiState.Caution:
        return ObcPoiHeaderState.Caution;
      case ObcPoiState.Warning:
        return ObcPoiHeaderState.Warning;
      case ObcPoiState.Alarm:
        return ObcPoiHeaderState.Alarm;
      case ObcPoiState.Enabled:
      default:
        return this.selected ? ObcPoiHeaderState.Selected : ObcPoiHeaderState.Enabled;
    }
  }
  syncHeaderContent() {
    const headerChildren = Array.from(this.children).filter(
      (child) => child instanceof HTMLElement && child.getAttribute("slot") === "header"
    );
    if (headerChildren.length === 0 && !this.hasHeader) {
      return;
    }
    if (headerChildren.length > 0 && !this.hasHeader && this.closest("obc-poi-layer-stack") === null) {
      this.hasHeader = true;
    }
    const target = this.shadowRoot?.querySelector('[slot="button"]') ?? this.shadowRoot?.querySelector("obc-poi");
    if (!target) {
      return;
    }
    for (const child of headerChildren) {
      if (child.parentElement !== target) {
        target.appendChild(child);
      }
      this.applyHeaderState(child);
    }
  }
  applyHeaderState(root) {
    const headers = [
      ...root instanceof Element && root.matches("obc-poi-header") ? [root] : [],
      ...root.querySelectorAll("obc-poi-header")
    ];
    for (const header of headers) {
      header.state = this.resolvedHeaderState;
      header.setAttribute("state", this.resolvedHeaderState);
    }
  }
  syncHeaderState() {
    this.applyHeaderState(this.renderRoot);
  }
  render() {
    return html`
      <obc-poi
        exportparts="icon"
        .type=${this.resolvedPoiType}
        .value=${this.value}
        .state=${this.resolvedPoiState}
        .x=${0}
        .y=${this.effectiveLineLength}
        .buttonY=${this.effectiveLocalButtonY}
        .fixedTarget=${false}
        .outsideAngle=${this.outsideAngle}
        .hasPointer=${this.hasPointer}
        .hasHeader=${this.hasHeader}
        .headerContent=${this.headerContent}
        .animatePosition=${this.animatePosition}
        .relativeDirection=${this.relativeDirection}
        .buttonType=${this.buttonType}
        .overlapOpaque=${this.overlapOpaque}
        .pointerType=${this.pointerType}
        .pointerState=${this.pointerState}
        .selected=${this.selected}
        .data=${this.data}
        .buttonOffsetX=${this.buttonOffsetX}
        .targetOffsetX=${this.targetOffsetX}
        .boxWidth=${this.boxWidth}
        .boxHeight=${this.boxHeight}
      >
        ${this.renderButtonSlot()}
      </obc-poi>
    `;
  }
};
_ObcPoiBase.styles = [unsafeCSS(componentStyle)];
let ObcPoiBase = _ObcPoiBase;
__decorateClass([
  property({ type: String })
], ObcPoiBase.prototype, "type");
__decorateClass([
  property({ type: String, reflect: true })
], ObcPoiBase.prototype, "value");
__decorateClass([
  property({ type: String })
], ObcPoiBase.prototype, "state");
__decorateClass([
  property({ type: Boolean })
], ObcPoiBase.prototype, "selected");
__decorateClass([
  property({ type: String, attribute: "button-type" })
], ObcPoiBase.prototype, "buttonType");
__decorateClass([
  property({ type: Boolean, attribute: "overlap-opaque" })
], ObcPoiBase.prototype, "overlapOpaque");
__decorateClass([
  property({ attribute: false })
], ObcPoiBase.prototype, "data");
__decorateClass([
  property({ type: Boolean, attribute: "has-header" })
], ObcPoiBase.prototype, "hasHeader");
__decorateClass([
  property({ type: String, attribute: "header-content" })
], ObcPoiBase.prototype, "headerContent");
__decorateClass([
  property({ type: Boolean })
], ObcPoiBase.prototype, "hasPointer");
__decorateClass([
  property({ type: String, attribute: "pointer-type" })
], ObcPoiBase.prototype, "pointerType");
__decorateClass([
  property({ type: String, attribute: "pointer-state" })
], ObcPoiBase.prototype, "pointerState");
__decorateClass([
  property({ type: Number })
], ObcPoiBase.prototype, "relativeDirection");
__decorateClass([
  property({ type: Number })
], ObcPoiBase.prototype, "x");
__decorateClass([
  property({ type: Number })
], ObcPoiBase.prototype, "y");
__decorateClass([
  property({ type: Number, attribute: "button-y" })
], ObcPoiBase.prototype, "buttonY");
__decorateClass([
  property({ type: Boolean, attribute: "fixed-target" })
], ObcPoiBase.prototype, "fixedTarget");
__decorateClass([
  property({ type: Number, attribute: "button-offset-x" })
], ObcPoiBase.prototype, "buttonOffsetX");
__decorateClass([
  property({ type: Number, attribute: "target-offset-x" })
], ObcPoiBase.prototype, "targetOffsetX");
__decorateClass([
  property({ type: Number, attribute: "box-width" })
], ObcPoiBase.prototype, "boxWidth");
__decorateClass([
  property({ type: Number, attribute: "box-height" })
], ObcPoiBase.prototype, "boxHeight");
__decorateClass([
  property({ type: Number, attribute: "line-compensation-y" })
], ObcPoiBase.prototype, "lineCompensationY");
__decorateClass([
  property({ type: Number, attribute: "outside-angle" })
], ObcPoiBase.prototype, "outsideAngle");
__decorateClass([
  property({ type: Boolean, attribute: "animate-position" })
], ObcPoiBase.prototype, "animatePosition");
export {
  ObcPoiBase,
  ObcPoiValue as PoiBaseValue,
  PoiDataVisualRectPreference as PoiBaseVisualRectPreference
};
//# sourceMappingURL=poi-base.js.map
