import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
import Parser from '../parser';
import { computeIntersection } from '../utils/computeIntersection';
import { computeSpeed } from '../utils/computeSpeed';
import { computeTurnRate } from '../utils/computeTurnRate';
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';

export const TerminatorsCI = (
  leg: CITerminalEntry,
  nextLeg: TerminalEntry,
  previousFix: NavFix,
  lastCourse: number
): [NavFix?, LineSegment[]?] => {
  const speed = computeSpeed(leg, previousFix);
  const crsIntoEndpoint = leg.Course.toTrue(previousFix);
  const [crsToIntercept, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix);
  const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];

  // Compute overfly arc
  if (previousFix.isFlyOver && !lastCourse.equal(crsIntoEndpoint)) {
    const turnRate = computeTurnRate(speed, Parser.AC_BANK);
    const updatedCrsToIntercept = getGreatCircleBearing(previousFix, nextFix);

    // 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
    while (!lastCourse.equal(crsIntoEndpoint) && !updatedCrsToIntercept.equal(crsToIntercept)) {
      let time = 0;
      if (leg.TurnDir === 'R') {
        const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
        const increment = delta < 0.1 ? delta : 0.1;
        lastCourse = (lastCourse + increment).normaliseDegrees();
        time = increment / turnRate;
      } else {
        const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
        const increment = delta < 0.1 ? delta : 0.1;
        lastCourse = (lastCourse - increment).normaliseDegrees();
        time = increment / turnRate;
      }

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

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

      // Update previousFix
      previousFix.latitude = arcFix.latitude;
      previousFix.longitude = arcFix.longitude;
    }
  }

  const interceptFix: NavFix = {
    ...computeIntersection(previousFix, leg.Course.toTrue(nextFix), nextFix, crsToIntercept)!,
    isFlyOver: leg.IsFlyOver,
    altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
    speed: speed,
    speedConstraint: leg.SpeedLimit,
    altitudeConstraint: leg.Alt,
    IsFAF: leg.IsFAF,
    IsMAP: leg.IsMAP,
  };

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

  return [interceptFix, line];
};
