import { unsafeCSS, LitElement, nothing, svg, html } from "lit";
import { property } from "lit/decorators.js";
import componentStyle from "./compass-sector.css.js";
import { WatchCircleType, innerRingRadiusFor, OUTER_RING_RADIUS } from "../watch/watch.js";
import { TickmarkType, TickmarkStyle } from "../watch/tickmark.js";
import { arrow, ArrowStyle } from "../compass/arrow.js";
import { AdviceState } from "../watch/advice.js";
import { SetpointBundle } from "../../svghelpers/setpoint-bundle.js";
import { computeZoomToFitArcFrame } from "../../svghelpers/arc-frame.js";
import { customElement } from "../../decorator.js";
import { InstrumentState, Priority } from "../types.js";
import { RotPosition } from "../rate-of-turn/rot-renderer.js";
import { RotType } from "../rate-of-turn/rot-renderer.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 CompassSectorPriorityElement = /* @__PURE__ */ ((CompassSectorPriorityElement2) => {
  CompassSectorPriorityElement2["hdg"] = "hdg";
  CompassSectorPriorityElement2["cog"] = "cog";
  CompassSectorPriorityElement2["rot"] = "rot";
  return CompassSectorPriorityElement2;
})(CompassSectorPriorityElement || {});
const PADDING = 72;
const WATCH_TYPE = WatchCircleType.triple;
const INNER_RADIUS = innerRingRadiusFor(WATCH_TYPE);
const ARC_HALF_EXTENT = 60;
function tickDensityForFOV(fov) {
  if (fov <= 30) {
    return { mainInterval: 10, primaryInterval: 5, secondaryInterval: 1 };
  } else if (fov <= 60) {
    return { mainInterval: 10, primaryInterval: 5, secondaryInterval: void 0 };
  } else if (fov <= 120) {
    return { mainInterval: 30, primaryInterval: 10, secondaryInterval: 5 };
  } else {
    return { mainInterval: 90, primaryInterval: 30, secondaryInterval: 10 };
  }
}
function normalizeAngle(a) {
  return (a % 360 + 360) % 360;
}
let ObcCompassSector = class extends LitElement {
  constructor() {
    super(...arguments);
    this.heading = 0;
    this.courseOverGround = 0;
    this.headingSetpoint = null;
    this.atHeadingSetpoint = false;
    this.headingSetpointAtZeroDeadband = 0.5;
    this.headingSetpointOverride = false;
    this.autoAtHeadingSetpoint = true;
    this.autoAtHeadingSetpointDeadband = 2;
    this.animateSetpoint = false;
    this.touching = false;
    this.headingAdvices = [];
    this.minFOV = 30;
    this.rotPosition = RotPosition.innerCircle;
    this.rotationsPerMinute = 1;
    this.rotMaxValue = 10;
    this.state = InstrumentState.active;
    this.priority = Priority.regular;
    this.priorityElements = [
      "hdg"
      /* hdg */
    ];
    this.tickmarksInside = false;
    this.zoomToFitArc = false;
    this._headingSp = new SetpointBundle({
      angularWraparound: true,
      onAnimationEnd: () => this.requestUpdate()
    });
    this._halfFOV = 30;
    this._arcHalfExtent = ARC_HALF_EXTENT;
    this._scale = 1;
    this._radiusOffset = 0;
    this._cachedViewBox = "";
    this._cachedAreas = [];
    this._cachedTickmarks = [];
    this._cachedAdvices = [];
  }
  willUpdate(changed) {
    super.willUpdate(changed);
    this._headingSp.sync({
      setpoint: this.headingSetpoint ?? void 0,
      newSetpoint: this.newHeadingSetpoint,
      atSetpoint: this.atHeadingSetpoint,
      touching: this.touching,
      autoAtSetpoint: this.autoAtHeadingSetpoint,
      autoAtSetpointDeadband: this.autoAtHeadingSetpointDeadband,
      setpointAtZeroDeadband: this.headingSetpointAtZeroDeadband,
      setpointOverride: this.headingSetpointOverride,
      animateSetpoint: this.animateSetpoint
    });
    const arcInputsChanged = changed.has("heading") || changed.has("courseOverGround") || changed.has("minFOV") || changed.has("zoomToFitArc");
    if (arcInputsChanged) {
      let diff = this.courseOverGround - this.heading;
      if (diff > 180) diff -= 360;
      else if (diff < -180) diff += 360;
      const minFov = Math.max(1, this.minFOV);
      const MARGIN = 15;
      if (this.zoomToFitArc) {
        const needed = Math.max(minFov, Math.abs(diff) + MARGIN);
        if (needed <= ARC_HALF_EXTENT) {
          this._halfFOV = needed;
          this._arcHalfExtent = needed;
          this._scale = 1;
        } else {
          this._halfFOV = needed;
          this._arcHalfExtent = ARC_HALF_EXTENT;
          this._scale = ARC_HALF_EXTENT / needed;
        }
      } else {
        this._halfFOV = Math.max(minFov, Math.abs(diff));
        this._arcHalfExtent = ARC_HALF_EXTENT;
        this._scale = ARC_HALF_EXTENT / this._halfFOV;
      }
      this._cachedAreas = [
        {
          startAngle: this.heading - this._arcHalfExtent,
          endAngle: this.heading + this._arcHalfExtent,
          roundInsideCut: true,
          roundOutsideCut: true
        }
      ];
      this._computeViewBox();
      this._cachedTickmarks = this._buildTickmarks();
    }
    if (arcInputsChanged || changed.has("headingAdvices")) {
      this._cachedAdvices = this._buildAdvices();
    }
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    this._headingSp.dispose();
  }
  // ---------------------------------------------------------------------------
  // Angle mapping — maps compass degrees to arc positions
  // ---------------------------------------------------------------------------
  _mapAngle(compassDeg) {
    let diff = compassDeg - this.heading;
    if (diff > 180) diff -= 360;
    else if (diff < -180) diff += 360;
    return this.heading + diff * this._scale;
  }
  // ---------------------------------------------------------------------------
  // Areas — fixed 120° arc centered on heading (built in willUpdate)
  // ---------------------------------------------------------------------------
  // ---------------------------------------------------------------------------
  // Tickmarks — compass-degree labels at mapped arc positions
  // ---------------------------------------------------------------------------
  _buildTickmarks() {
    const fov = this._halfFOV * 2;
    const { mainInterval, primaryInterval, secondaryInterval } = tickDensityForFOV(fov);
    const halfFov = this._halfFOV;
    const compassStart = this.heading - halfFov;
    const compassEnd = this.heading + halfFov;
    const tickmarks = [];
    const added = /* @__PURE__ */ new Set();
    const addTick = (arcAngle, type, text) => {
      const key = Math.round(arcAngle * 1e3);
      if (added.has(key)) return;
      added.add(key);
      tickmarks.push({ angle: arcAngle, type, text });
    };
    const step = secondaryInterval ?? primaryInterval;
    const firstTick = Math.ceil(compassStart / step) * step;
    for (let compassDeg = firstTick; compassDeg <= compassEnd; compassDeg += step) {
      const norm = normalizeAngle(compassDeg);
      const arcAngle = this._mapAngle(compassDeg);
      const isMain = norm % mainInterval === 0;
      const isPrimary = norm % primaryInterval === 0;
      if (isMain) {
        addTick(arcAngle, TickmarkType.main, Math.round(norm).toString());
      } else if (isPrimary) {
        addTick(arcAngle, TickmarkType.primary);
      } else {
        addTick(arcAngle, TickmarkType.secondary);
      }
    }
    return tickmarks;
  }
  // ---------------------------------------------------------------------------
  // ViewBox
  // ---------------------------------------------------------------------------
  _computeViewBox() {
    if (this.zoomToFitArc) {
      const targetSize = (176 + PADDING) * 2;
      const frame = computeZoomToFitArcFrame({
        areas: this._cachedAreas,
        outerRadius: OUTER_RING_RADIUS,
        innerRadius: INNER_RADIUS,
        extension: PADDING,
        targetSize
      });
      this._radiusOffset = frame.radiusOffset;
      this._cachedViewBox = frame.viewBox;
      this._cachedArcFrame = frame;
    } else {
      this._radiusOffset = 0;
      const width = (176 + PADDING) * 2;
      this._cachedViewBox = `-${width / 2} -${width / 2} ${width} ${width}`;
      this._cachedArcFrame = void 0;
    }
  }
  // ---------------------------------------------------------------------------
  // North arrow — rendered in overlay at mapped 0° position
  // ---------------------------------------------------------------------------
  _renderNorthArrow(rOff) {
    let northOffset = -this.heading;
    if (northOffset > 180) northOffset -= 360;
    else if (northOffset < -180) northOffset += 360;
    if (Math.abs(northOffset) > this._halfFOV) return nothing;
    const northArcAngle = this._mapAngle(0);
    const radius = OUTER_RING_RADIUS + rOff;
    return svg`
      <g transform="rotate(${northArcAngle}) translate(0, ${-radius})">
        <path fill-rule="evenodd" clip-rule="evenodd"
          d="M-17.8457 24.984 0 0 17.8458 24.984C11.9868 24.3338 6.0324 24 0 24-6.0323 24-11.9867 24.3338-17.8457 24.984Z"
          fill="var(--instrument-frame-tertiary-color)"/>
      </g>
    `;
  }
  // ---------------------------------------------------------------------------
  // Priority & advice helpers
  // ---------------------------------------------------------------------------
  _angleInRange(value, min, max) {
    const v = normalizeAngle(value);
    const start = normalizeAngle(min);
    const end = normalizeAngle(max);
    return start <= end ? v >= start && v <= end : v >= start || v <= end;
  }
  _buildAdvices() {
    return this.headingAdvices.map(({ minAngle, maxAngle, hinted, type }) => {
      const state = this._angleInRange(this.heading, minAngle, maxAngle) ? AdviceState.triggered : hinted ? AdviceState.hinted : AdviceState.regular;
      return {
        minAngle: this._mapAngle(minAngle),
        maxAngle: this._mapAngle(maxAngle),
        type,
        state
      };
    });
  }
  get _rotEndAngle() {
    const maxVal = this.rotMaxValue || 1;
    const barCompassDeg = this.rotationsPerMinute / maxVal * ARC_HALF_EXTENT;
    return this._mapAngle(this.heading + barCompassDeg);
  }
  priorityFor(element) {
    const selected = Array.isArray(this.priorityElements) ? this.priorityElements : [];
    return selected.includes(element) ? this.priority : Priority.regular;
  }
  // ---------------------------------------------------------------------------
  // Render
  // ---------------------------------------------------------------------------
  render() {
    const rotation = -this.heading;
    const viewBox = this._cachedViewBox;
    const rOff = this._radiusOffset;
    const mappedCOG = this._mapAngle(this.courseOverGround);
    const mappedSetpoint = this.headingSetpoint != null ? this._mapAngle(this.headingSetpoint) : void 0;
    const mappedNewSetpoint = this.newHeadingSetpoint != null ? this._mapAngle(this.newHeadingSetpoint) : void 0;
    return html`
      <div class="container">
        <obc-watch
          .touching=${this.touching}
          .padding=${PADDING}
          .advices=${this._cachedAdvices}
          .tickmarks=${this._cachedTickmarks}
          .tickmarkStyle=${TickmarkStyle.regular}
          .tickmarksInside=${this.tickmarksInside}
          .state=${this.state}
          .watchCircleType=${WATCH_TYPE}
          .northArrow=${false}
          .areas=${this._cachedAreas}
          .arcFrame=${this._cachedArcFrame}
          .zoomToFitArc=${this.zoomToFitArc}
          .tickFadeAngle=${this._arcHalfExtent * 0.2}
          .rotation=${rotation}
          .angleSetpoint=${mappedSetpoint}
          .newAngleSetpoint=${mappedNewSetpoint}
          .atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}
          .angleSetpointAtZeroDeadband=${this.headingSetpointAtZeroDeadband}
          .setpointOverride=${this.headingSetpointOverride}
          .priority=${this.priority}
          .animateSetpoint=${this.animateSetpoint}
          .rotType=${this.rotType}
          .rotPosition=${this.rotPosition}
          .rotStartAngle=${this.heading}
          .rotEndAngle=${this._rotEndAngle}
          .rotPriority=${this.priorityFor(
      "rot"
      /* rot */
    )}
          .rotationsPerMinute=${this.rotationsPerMinute}
        >
        </obc-watch>
        <svg viewBox="${viewBox}" transform="rotate(${rotation})">
          ${this._renderNorthArrow(rOff)}
          ${arrow(
      ArrowStyle.HDG,
      this.heading,
      this.priorityFor(
        "hdg"
        /* hdg */
      ),
      rOff
    )}
          ${arrow(
      ArrowStyle.COG,
      mappedCOG,
      this.priorityFor(
        "cog"
        /* cog */
      ),
      rOff
    )}
        </svg>
      </div>
    `;
  }
};
ObcCompassSector.styles = unsafeCSS(componentStyle);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "heading", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "courseOverGround", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "headingSetpoint", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "newHeadingSetpoint", 2);
__decorateClass([
  property({ type: Boolean })
], ObcCompassSector.prototype, "atHeadingSetpoint", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "headingSetpointAtZeroDeadband", 2);
__decorateClass([
  property({ type: Boolean })
], ObcCompassSector.prototype, "headingSetpointOverride", 2);
__decorateClass([
  property({ type: Boolean, attribute: false })
], ObcCompassSector.prototype, "autoAtHeadingSetpoint", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "autoAtHeadingSetpointDeadband", 2);
__decorateClass([
  property({ type: Boolean })
], ObcCompassSector.prototype, "animateSetpoint", 2);
__decorateClass([
  property({ type: Boolean })
], ObcCompassSector.prototype, "touching", 2);
__decorateClass([
  property({ type: Array, attribute: false })
], ObcCompassSector.prototype, "headingAdvices", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "minFOV", 2);
__decorateClass([
  property({ type: String })
], ObcCompassSector.prototype, "rotType", 2);
__decorateClass([
  property({ type: String })
], ObcCompassSector.prototype, "rotPosition", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "rotationsPerMinute", 2);
__decorateClass([
  property({ type: Number })
], ObcCompassSector.prototype, "rotMaxValue", 2);
__decorateClass([
  property({ type: String })
], ObcCompassSector.prototype, "state", 2);
__decorateClass([
  property({ type: String })
], ObcCompassSector.prototype, "priority", 2);
__decorateClass([
  property({ type: Array, attribute: false })
], ObcCompassSector.prototype, "priorityElements", 2);
__decorateClass([
  property({ type: Boolean })
], ObcCompassSector.prototype, "tickmarksInside", 2);
__decorateClass([
  property({ type: Boolean })
], ObcCompassSector.prototype, "zoomToFitArc", 2);
ObcCompassSector = __decorateClass([
  customElement("obc-compass-sector")
], ObcCompassSector);
export {
  CompassSectorPriorityElement,
  ObcCompassSector,
  RotPosition,
  RotType
};
//# sourceMappingURL=compass-sector.js.map
