import { Cartographic } from "../../Source/Cesium.js";
import { Ellipsoid } from "../../Source/Cesium.js";
import { EllipsoidGeodesic } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";

describe("Core/EllipsoidGeodesic", function () {
  it("throws without start", function () {
    expect(function () {
      var elGeo = new EllipsoidGeodesic();
      return elGeo.interpolateUsingSurfaceDistance(0);
    }).toThrowDeveloperError();
  });

  it("throws without end", function () {
    expect(function () {
      var elGeo = new EllipsoidGeodesic(new Cartographic(Math.PI, Math.PI));
      return elGeo.interpolateUsingSurfaceDistance(0);
    }).toThrowDeveloperError();
  });

  it("throws without unique position", function () {
    expect(function () {
      var elGeo = new EllipsoidGeodesic(
        new Cartographic(Math.PI, Math.PI),
        new Cartographic(0, Math.PI)
      );
      return elGeo.interpolateUsingSurfaceDistance(0);
    }).toThrowDeveloperError();
  });

  it("setEndPoints throws without start", function () {
    expect(function () {
      var elGeo = new EllipsoidGeodesic();
      elGeo.setEndPoints();
    }).toThrowDeveloperError();
  });

  it("setEndPoints throws without end", function () {
    expect(function () {
      var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0);
      var elGeo = new EllipsoidGeodesic();
      elGeo.setEndPoints(start);
      return elGeo.interpolateUsingSurfaceDistance(0);
    }).toThrowDeveloperError();
  });

  it("getSurfaceDistance throws if start or end never defined", function () {
    expect(function () {
      var elGeo = new EllipsoidGeodesic();
      return elGeo.surfaceDistance;
    }).toThrowDeveloperError();
  });

  it("getStartHeading throws if start or end never defined", function () {
    expect(function () {
      var elGeo = new EllipsoidGeodesic();
      return elGeo.startHeading;
    }).toThrowDeveloperError();
  });

  it("getEndHeading throws if start or end never defined", function () {
    expect(function () {
      var elGeo = new EllipsoidGeodesic();
      return elGeo.endHeading;
    }).toThrowDeveloperError();
  });

  it("works with two points", function () {
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(fifteenDegrees, fifteenDegrees);
    var thirtyDegrees = Math.PI / 6;
    var end = new Cartographic(thirtyDegrees, thirtyDegrees);

    var geodesic = new EllipsoidGeodesic(start, end);
    expect(start).toEqual(geodesic.start);
    expect(end).toEqual(geodesic.end);
  });

  it("sets end points", function () {
    var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0);
    var end = new Cartographic(CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO);
    var geodesic = new EllipsoidGeodesic();
    geodesic.setEndPoints(start, end);
    expect(start).toEqual(geodesic.start);
    expect(end).toEqual(geodesic.end);
  });

  it("gets start heading", function () {
    var ellipsoid = new Ellipsoid(6, 6, 3);
    var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0);
    var end = new Cartographic(Math.PI, 0);

    var geodesic = new EllipsoidGeodesic(start, end, ellipsoid);
    expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(
      geodesic.startHeading,
      CesiumMath.EPSILON11
    );
  });

  it("gets end heading", function () {
    var ellipsoid = new Ellipsoid(6, 6, 3);
    var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0);
    var end = new Cartographic(Math.PI, 0);

    var geodesic = new EllipsoidGeodesic(start, end, ellipsoid);
    expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(
      geodesic.endHeading,
      CesiumMath.EPSILON11
    );
  });

  it("computes distance at equator", function () {
    var ellipsoid = new Ellipsoid(6, 6, 3);
    var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0);
    var end = new Cartographic(Math.PI, 0);

    var geodesic = new EllipsoidGeodesic(start, end, ellipsoid);
    expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(
      geodesic.surfaceDistance,
      CesiumMath.EPSILON11
    );
  });

  it("computes distance very close to equator", function () {
    // See https://github.com/CesiumGS/cesium/issues/9248

    var ellipsoid = new Ellipsoid(6, 6, 3);

    var start = new Cartographic(-CesiumMath.EPSILON10, CesiumMath.EPSILON10);

    var end = new Cartographic(+CesiumMath.EPSILON10, CesiumMath.EPSILON10);

    var geodesic = new EllipsoidGeodesic(start, end, ellipsoid);
    expect(geodesic.surfaceDistance).not.toBeNaN();
  });

  it("computes distance at meridian", function () {
    var ellipsoid = new Ellipsoid(6, 6, 6);
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(CesiumMath.PI_OVER_TWO, fifteenDegrees);
    var fortyfiveDegrees = Math.PI / 4;
    var end = new Cartographic(CesiumMath.PI_OVER_TWO, fortyfiveDegrees);

    var geodesic = new EllipsoidGeodesic(start, end, ellipsoid);
    var thirtyDegrees = Math.PI / 6;
    expect(thirtyDegrees * 6).toEqualEpsilon(
      geodesic.surfaceDistance,
      CesiumMath.EPSILON11
    );
  });

  it("computes distance at pole", function () {
    var ellipsoid = new Ellipsoid(6, 6, 6);
    var seventyfiveDegrees = (Math.PI / 12) * 5;
    var fortyfiveDegrees = Math.PI / 4;
    var start = new Cartographic(0, -fortyfiveDegrees);
    var end = new Cartographic(Math.PI, -seventyfiveDegrees);

    var geodesic = new EllipsoidGeodesic(start, end, ellipsoid);
    var sixtyDegrees = Math.PI / 3;
    expect(sixtyDegrees * 6).toEqualEpsilon(
      geodesic.surfaceDistance,
      CesiumMath.EPSILON11
    );
  });

  it("interpolates start and end points", function () {
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(fifteenDegrees, fifteenDegrees);
    var thirtyDegrees = Math.PI / 6;
    var end = new Cartographic(thirtyDegrees, thirtyDegrees);

    var geodesic = new EllipsoidGeodesic(start, end);
    var distance = geodesic.surfaceDistance;

    var first = geodesic.interpolateUsingSurfaceDistance(0.0);
    var last = geodesic.interpolateUsingSurfaceDistance(distance);

    expect(start.longitude).toEqualEpsilon(
      first.longitude,
      CesiumMath.EPSILON13
    );
    expect(start.latitude).toEqualEpsilon(first.latitude, CesiumMath.EPSILON13);
    expect(end.longitude).toEqualEpsilon(last.longitude, CesiumMath.EPSILON13);
    expect(end.latitude).toEqualEpsilon(last.latitude, CesiumMath.EPSILON13);
  });

  it("interpolates midpoint", function () {
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(fifteenDegrees, 0);
    var fortyfiveDegrees = Math.PI / 4;
    var end = new Cartographic(fortyfiveDegrees, 0);
    var thirtyDegrees = Math.PI / 6;
    var expectedMid = new Cartographic(thirtyDegrees, 0);

    var geodesic = new EllipsoidGeodesic(start, end);
    var distance = Ellipsoid.WGS84.radii.x * fifteenDegrees;

    var midpoint = geodesic.interpolateUsingSurfaceDistance(distance);

    expect(expectedMid.longitude).toEqualEpsilon(
      midpoint.longitude,
      CesiumMath.EPSILON13
    );
    expect(expectedMid.latitude).toEqualEpsilon(
      midpoint.latitude,
      CesiumMath.EPSILON13
    );
  });

  it("interpolates start and end points using fraction", function () {
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(fifteenDegrees, fifteenDegrees);
    var thirtyDegrees = Math.PI / 6;
    var end = new Cartographic(thirtyDegrees, thirtyDegrees);

    var geodesic = new EllipsoidGeodesic(start, end);

    var first = geodesic.interpolateUsingFraction(0);
    var last = geodesic.interpolateUsingFraction(1);

    expect(start.longitude).toEqualEpsilon(
      first.longitude,
      CesiumMath.EPSILON13
    );
    expect(start.latitude).toEqualEpsilon(first.latitude, CesiumMath.EPSILON13);
    expect(end.longitude).toEqualEpsilon(last.longitude, CesiumMath.EPSILON13);
    expect(end.latitude).toEqualEpsilon(last.latitude, CesiumMath.EPSILON13);
  });

  it("interpolates midpoint using fraction", function () {
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(fifteenDegrees, 0);
    var fortyfiveDegrees = Math.PI / 4;
    var end = new Cartographic(fortyfiveDegrees, 0);
    var thirtyDegrees = Math.PI / 6;
    var expectedMid = new Cartographic(thirtyDegrees, 0);

    var geodesic = new EllipsoidGeodesic(start, end);

    var midpoint = geodesic.interpolateUsingFraction(0.5);

    expect(expectedMid.longitude).toEqualEpsilon(
      midpoint.longitude,
      CesiumMath.EPSILON12
    );
    expect(expectedMid.latitude).toEqualEpsilon(
      midpoint.latitude,
      CesiumMath.EPSILON12
    );
  });

  it("interpolates midpoint fraction using result parameter", function () {
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(fifteenDegrees, 0);
    var fortyfiveDegrees = Math.PI / 4;
    var end = new Cartographic(fortyfiveDegrees, 0);
    var thirtyDegrees = Math.PI / 6;
    var expectedMid = new Cartographic(thirtyDegrees, 0);

    var geodesic = new EllipsoidGeodesic(start, end);
    var result = new Cartographic();
    var midpoint = geodesic.interpolateUsingFraction(0.5, result);
    expect(result).toBe(midpoint);

    expect(expectedMid.longitude).toEqualEpsilon(
      result.longitude,
      CesiumMath.EPSILON12
    );
    expect(expectedMid.latitude).toEqualEpsilon(
      result.latitude,
      CesiumMath.EPSILON12
    );
  });

  it("interpolates midpoint using result parameter", function () {
    var fifteenDegrees = Math.PI / 12;
    var start = new Cartographic(fifteenDegrees, 0);
    var fortyfiveDegrees = Math.PI / 4;
    var end = new Cartographic(fortyfiveDegrees, 0);
    var thirtyDegrees = Math.PI / 6;
    var expectedMid = new Cartographic(thirtyDegrees, 0);

    var geodesic = new EllipsoidGeodesic(start, end);
    var distance = Ellipsoid.WGS84.radii.x * fifteenDegrees;

    var result = new Cartographic();
    var midpoint = geodesic.interpolateUsingSurfaceDistance(distance, result);

    expect(result).toBe(midpoint);

    expect(expectedMid.longitude).toEqualEpsilon(
      result.longitude,
      CesiumMath.EPSILON13
    );
    expect(expectedMid.latitude).toEqualEpsilon(
      result.latitude,
      CesiumMath.EPSILON13
    );
  });
});
