import * as geolib from 'geolib';
import Parser from '../parser.ts';
import { computeSpeed } from '../utils/computeSpeed.ts';
import { computeTurnRate } from '../utils/computeTurnRate.ts';

// NOTE: Distance not adjusted for altitude in this demo
export const TerminatorsFC = (
  leg: FCTerminalEntry,
  previousFix: NavFix,
  lastCourse: number
): [NavFix?, LineSegment[]?] => {
  const refFix = {
    latitude: leg.WptLat,
    longitude: leg.WptLon,
  };
  const speed = computeSpeed(leg, previousFix);
  const turnRate = computeTurnRate(speed, Parser.AC_BANK);
  const trackIntoEndpoint = leg.Course.toTrue(refFix);

  const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];

  if (previousFix.isFlyOver) {
    let crsIntoEndpoint = trackIntoEndpoint;

    // Check if there even is an arc
    if (!crsIntoEndpoint.equal(lastCourse)) {
      // Turn Dir
      if (!leg.TurnDir || leg.TurnDir === 'E') {
        let prov = lastCourse - crsIntoEndpoint;
        prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
        leg.TurnDir = prov > 0 ? 'L' : 'R';
      }

      // Generate arc
      let condition = false;
      do {
        let time = 0;
        if (leg.TurnDir === 'R') {
          const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
          const increment = delta < 1 ? delta : 1;
          lastCourse = (lastCourse + increment).normaliseDegrees();
          time = increment / turnRate;
        } else {
          const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
          const increment = delta < 1 ? delta : 1;
          lastCourse = (lastCourse - increment).normaliseDegrees();
          time = increment / turnRate;
        }

        const arcFix = geolib.computeDestinationPoint(
          {
            latitude: line.at(-1)![1],
            longitude: line.at(-1)![0],
          },
          ((speed * time) / 3600).toMetre(),
          lastCourse
        );

        line.push([arcFix.longitude, arcFix.latitude]);

        if (leg.TurnDir === 'R') {
          condition = lastCourse > trackIntoEndpoint;
        } else {
          condition = lastCourse < trackIntoEndpoint;
        }
      } while (condition);
    }
  }

  const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
  if (line.length > 1) {
    lastCourse = geolib.getGreatCircleBearing(
      {
        latitude: line.at(-2)![1],
        longitude: line.at(-2)![0],
      },
      arcEnd
    );
  }

  const targetFix: NavFix = {
    ...geolib.computeDestinationPoint(arcEnd, leg.Distance.toMetre(), lastCourse),
    name: leg.Distance.toString(),
    isFlyOver: true,
    altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
    speed: speed,
    speedConstraint: leg.SpeedLimit,
    altitudeConstraint: leg.Alt,
  };

  line.push([targetFix.longitude, targetFix.latitude]);

  return [targetFix, line];
};
