import * as geolib from 'geolib';
import { computeIntersection } from '../utils/computeIntersection.ts';

/**
 * @param crsIntoEndpoint Course into arc endpoint
 * @param crsFromOrigin Course from arc origin point
 * @param start Arc origin point
 * @param end Leg endpoint
 * @param turnDir Turn direction
 * @returns Line segments forming arc from `start`to `end`
 */
export const generateTangentArc = (
  crsIntoEndpoint: number,
  crsFromOrigin: number,
  start: NavFix,
  end: NavFix,
  turnDir?: TurnDirection
) => {
  const line: LineSegment[] = [[start.longitude, start.latitude]];

  // Check if there even is an arc
  if (!crsFromOrigin.equal(crsIntoEndpoint)) {
    // Course to the end of the arc
    let crsFromStartToEnd;
    if (!turnDir || turnDir === 'E') {
      let prov = crsFromOrigin - crsIntoEndpoint;
      prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
      turnDir = prov > 0 ? 'L' : 'R';
    }
    if (turnDir === 'R') {
      const delta = (360 - crsFromOrigin + crsIntoEndpoint).normaliseDegrees();
      crsFromStartToEnd = (crsFromOrigin + delta / 2).normaliseDegrees();
    } else {
      const delta = (crsFromOrigin + 360 - crsIntoEndpoint).normaliseDegrees();
      crsFromStartToEnd = (crsFromOrigin - delta / 2).normaliseDegrees();
    }

    // Arc end
    const intcArcOnCrsIntoEndpoint = computeIntersection(
      start,
      crsFromStartToEnd,
      end,
      crsIntoEndpoint.reciprocalCourse()
    );
    if (!intcArcOnCrsIntoEndpoint) return null;

    let crsOrthogonalOnOrigin;
    let crsOrthogonalOnEndpoint;
    if (turnDir === 'R') {
      crsOrthogonalOnOrigin = (crsFromOrigin + 90).normaliseDegrees();
      crsOrthogonalOnEndpoint = (crsIntoEndpoint + 90).normaliseDegrees();
    } else {
      crsOrthogonalOnOrigin = (crsFromOrigin - 90).normaliseDegrees();
      crsOrthogonalOnEndpoint = (crsIntoEndpoint - 90).normaliseDegrees();
    }

    // Generate arc
    const arcCenter = computeIntersection(
      start,
      crsOrthogonalOnOrigin,
      intcArcOnCrsIntoEndpoint,
      crsOrthogonalOnEndpoint
    );
    if (!arcCenter) return null;
    const arcRad = geolib.getDistance(arcCenter, start);

    crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
    crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
    // Start turn immediately
    if (turnDir === 'R') {
      crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
    } else {
      crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
    }

    while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
      if (turnDir === 'R') {
        const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
        crsOrthogonalOnOrigin += delta < 1 ? delta : 1;
        crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
      } else {
        const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
        crsOrthogonalOnOrigin -= delta < 1 ? delta : 1;
        crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
      }

      const arcFix = geolib.computeDestinationPoint(arcCenter, arcRad, crsOrthogonalOnOrigin);

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

  return line;
};
