import { Cartesian3 } from "../../Source/Cesium.js";
import { CatmullRomSpline } from "../../Source/Cesium.js";
import { HermiteSpline } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";

describe("Core/CatmullRomSpline", function () {
  var points;
  var times;

  beforeEach(function () {
    points = [
      new Cartesian3(-1.0, -1.0, 0.0),
      new Cartesian3(-0.5, -0.125, 0.0),
      new Cartesian3(0.5, 0.125, 0.0),
      new Cartesian3(1.0, 1.0, 0.0),
    ];
    times = [0.0, 1.0, 2.0, 3.0];
  });

  it("constructor throws without points or times", function () {
    expect(function () {
      return new CatmullRomSpline();
    }).toThrowDeveloperError();
  });

  it("constructor throws when control points length is less than 2", function () {
    expect(function () {
      return new CatmullRomSpline({
        points: [Cartesian3.ZERO],
      });
    }).toThrowDeveloperError();
  });

  it("constructor throws when times.length is not equal to points.length", function () {
    expect(function () {
      return new CatmullRomSpline({
        points: points,
        times: [0.0, 1.0],
      });
    }).toThrowDeveloperError();
  });

  it("sets start and end tangents", function () {
    var start = Cartesian3.subtract(points[1], points[0], new Cartesian3());
    var end = Cartesian3.subtract(
      points[points.length - 1],
      points[points.length - 2],
      new Cartesian3()
    );
    var crs = new CatmullRomSpline({
      points: points,
      times: times,
      firstTangent: start,
      lastTangent: end,
    });

    expect(start).toEqual(crs.firstTangent);
    expect(end).toEqual(crs.lastTangent);
  });

  it("computes start and end tangents", function () {
    var controlPoint0 = Cartesian3.clone(points[0]);
    var controlPoint1 = Cartesian3.clone(points[1]);
    var controlPoint2 = Cartesian3.clone(points[2]);

    var start = new Cartesian3();
    start = Cartesian3.multiplyByScalar(
      Cartesian3.subtract(
        Cartesian3.subtract(
          Cartesian3.multiplyByScalar(controlPoint1, 2.0, start),
          controlPoint2,
          start
        ),
        controlPoint0,
        start
      ),
      0.5,
      start
    );

    var controlPointn0 = Cartesian3.clone(points[points.length - 1]);
    var controlPointn1 = Cartesian3.clone(points[points.length - 2]);
    var controlPointn2 = Cartesian3.clone(points[points.length - 3]);

    var end = new Cartesian3();
    end = Cartesian3.multiplyByScalar(
      Cartesian3.add(
        Cartesian3.subtract(
          controlPointn0,
          Cartesian3.multiplyByScalar(controlPointn1, 2.0, end),
          end
        ),
        controlPointn2,
        end
      ),
      0.5,
      end
    );

    var crs = new CatmullRomSpline({
      points: points,
      times: times,
    });

    expect(start).toEqual(crs.firstTangent);
    expect(end).toEqual(crs.lastTangent);
  });

  it("evaluate throws without time", function () {
    var crs = new CatmullRomSpline({
      points: points,
      times: times,
    });

    expect(function () {
      crs.evaluate();
    }).toThrowDeveloperError();
  });

  it("evaluate throws when time is out of range", function () {
    var crs = new CatmullRomSpline({
      points: points,
      times: times,
    });

    expect(function () {
      crs.evaluate(times[0] - 1.0);
    }).toThrowDeveloperError();
  });

  it("check Catmull-Rom spline against a Hermite spline", function () {
    var crs = new CatmullRomSpline({
      points: points,
      times: times,
    });

    var tangents = [crs.firstTangent];
    for (var i = 1; i < points.length - 1; ++i) {
      tangents.push(
        Cartesian3.multiplyByScalar(
          Cartesian3.subtract(points[i + 1], points[i - 1], new Cartesian3()),
          0.5,
          new Cartesian3()
        )
      );
    }
    tangents.push(crs.lastTangent);

    var hs = HermiteSpline.createC1({
      points: points,
      tangents: tangents,
      times: times,
    });

    var granularity = 0.5;
    for (var j = times[0]; j <= times[points.length - 1]; j = j + granularity) {
      expect(hs.evaluate(j)).toEqualEpsilon(
        crs.evaluate(j),
        CesiumMath.EPSILON4
      );
    }
  });

  it("evaluate with result parameter", function () {
    var crs = new CatmullRomSpline({
      points: points,
      times: times,
    });
    var result = new Cartesian3();

    var point = crs.evaluate(times[0], result);
    expect(point).toBe(result);
    expect(result).toEqual(points[0]);
  });

  it("spline with 2 control points defaults to lerp", function () {
    points = points.slice(0, 2);
    times = times.slice(0, 2);

    var crs = new CatmullRomSpline({
      points: points,
      times: times,
    });

    var t = (times[0] + times[1]) * 0.5;
    expect(crs.evaluate(t)).toEqual(
      Cartesian3.lerp(points[0], points[1], t, new Cartesian3())
    );
  });

  it("spline with 2 control points defaults to lerp and result parameter", function () {
    points = points.slice(0, 2);
    times = times.slice(0, 2);

    var crs = new CatmullRomSpline({
      points: points,
      times: times,
    });

    var t = (times[0] + times[1]) * 0.5;
    var result = new Cartesian3();
    var actual = crs.evaluate(t, result);
    expect(actual).toBe(result);
    expect(actual).toEqual(
      Cartesian3.lerp(points[0], points[1], t, new Cartesian3())
    );
  });
});
