import { makeAppObjectRepo } from "@vived/core";
import { Angle, Matrix, ParametricLine, Quaternion, Vector2, Vector3, } from "@vived/core";
import { ScenePixelPickUC } from "../../Babylon";
import { makeMockDispatchStateChange } from "../../Dispatcher";
import { makeSceneRootEntity } from "../Entities";
import { makeOrbitSceneRootUC, OrbitSceneRootUC } from "./OrbitSceneRoot";
class MockPick extends ScenePixelPickUC {
    constructor() {
        super(...arguments);
        this.scenePixelPick = jest.fn();
    }
}
function makeTestRig() {
    const appObjects = makeAppObjectRepo();
    const sceneRootAO = appObjects.getOrCreate("sceneRoot");
    const sceneRoot = makeSceneRootEntity(sceneRootAO);
    const orbit = makeOrbitSceneRootUC(sceneRootAO);
    const appAO = appObjects.getOrCreate("appAO");
    const mockPick = new MockPick(appAO, ScenePixelPickUC.type);
    const mockDispatch = makeMockDispatchStateChange(appObjects);
    const doDispatchMock = jest.fn();
    mockDispatch.doDispatch = doDispatchMock;
    return {
        orbit,
        sceneRoot,
        mockPick,
        mockDispatch,
        doDispatchMock,
        appObjects,
    };
}
describe("Orbit Scene Root Use Case", () => {
    it("Rotates about global X when moving the mouse in the y direction", () => {
        const { sceneRoot, orbit } = makeTestRig();
        const initialRotation = Quaternion.FromYawPitchRoll(Angle.FromDegrees(30), Angle.FromDegrees(45), Angle.FromDegrees(0));
        sceneRoot.rotation = initialRotation;
        orbit.initializeOrbit(Vector2.Zero());
        const drag = new Vector2(0, 10);
        const expectOrbitQuaternion = Quaternion.FromAngleAxis(Vector3.Left(), Angle.FromDegrees(orbit.sensitivity * 10));
        const expectFinalQuaterion = Quaternion.Multiply(expectOrbitQuaternion, initialRotation);
        orbit.doOrbit(drag);
        const rootRotation = sceneRoot.rotation;
        expect(Quaternion.Close(rootRotation, expectFinalQuaterion)).toBe(true);
    });
    it("Rotates about local Y when moving the mouse in the X direction", () => {
        const { sceneRoot, orbit } = makeTestRig();
        const initialRotation = Quaternion.FromYawPitchRoll(Angle.FromDegrees(30), Angle.FromDegrees(45), Angle.FromDegrees(0));
        sceneRoot.rotation = initialRotation;
        orbit.initializeOrbit(Vector2.Zero());
        const drag = new Vector2(10, 0);
        const worldHeadingAxis = sceneRoot.transform.localDirectionToWorld(Vector3.Down());
        const expectOrbitQuaternion = Quaternion.FromAngleAxis(worldHeadingAxis, Angle.FromDegrees(orbit.sensitivity * 10));
        const expectFinalQuaterion = Quaternion.Multiply(expectOrbitQuaternion, initialRotation);
        orbit.doOrbit(drag);
        const rootRotation = sceneRoot.transform.worldRotation;
        expect(Quaternion.Close(rootRotation, expectFinalQuaterion)).toBe(true);
    });
    it("Flips the heading angle if the root is flipped upside down", () => {
        const { sceneRoot, orbit } = makeTestRig();
        const initialRotation = Quaternion.FromYawPitchRoll(Angle.FromDegrees(0), Angle.FromDegrees(100), Angle.FromDegrees(0));
        sceneRoot.transform.localRotation = initialRotation;
        orbit.initializeOrbit(Vector2.Zero());
        const drag = new Vector2(10, 0);
        const worldHeadingAxis = sceneRoot.transform.localDirectionToWorld(Vector3.Down());
        const expectOrbitQuaternion = Quaternion.FromAngleAxis(worldHeadingAxis, Angle.FromDegrees(orbit.sensitivity * -10));
        const expectFinalQuaterion = Quaternion.Multiply(expectOrbitQuaternion, initialRotation);
        orbit.doOrbit(drag);
        expect(Quaternion.Close(sceneRoot.rotation, expectFinalQuaterion)).toBe(true);
    });
    it("Orbits around the global origin if there is no pick hit", () => {
        const { sceneRoot, orbit } = makeTestRig();
        sceneRoot.position = new Vector3(1, 2, 3);
        orbit.initializeOrbit(Vector2.Zero());
        orbit.doOrbit(new Vector2(100, 0));
        const expectedRotationMatrix = Matrix.FromRotationAxisAngle(Vector3.Down(), Angle.FromDegrees(orbit.sensitivity * 100));
        const expectedPos = Vector3.Transform(new Vector3(1, 2, 3), expectedRotationMatrix);
        expect(sceneRoot.position).toEqual(expectedPos);
    });
    it("Orbits around the pick point", () => {
        const { sceneRoot, orbit, mockPick } = makeTestRig();
        expect(sceneRoot.position).toEqual(Vector3.Zero());
        const pickPos = new Vector3(1, 2, 3);
        const pick = {
            hit: true,
            sceneRay: ParametricLine.Forward(),
            worldPickPoint: pickPos,
        };
        mockPick.scenePixelPick.mockReturnValue(pick);
        orbit.initializeOrbit(Vector2.Zero());
        orbit.doOrbit(new Vector2(100, 0));
        const expectedRotationMatrix = Matrix.FromRotationAxisAngle(Vector3.Down(), Angle.FromDegrees(orbit.sensitivity * 100));
        const transformedPick = Vector3.Transform(pickPos, expectedRotationMatrix);
        const expectedPos = Vector3.Subtract(pickPos, transformedPick);
        expect(sceneRoot.position).toEqual(expectedPos);
    });
    it("Dispatches a state change if the rotaiton has changed since init", () => {
        const { sceneRoot, orbit, doDispatchMock } = makeTestRig();
        const initialRotation = Quaternion.FromYawPitchRoll(Angle.FromDegrees(30), Angle.FromDegrees(45), Angle.FromDegrees(0));
        sceneRoot.rotation = initialRotation;
        orbit.initializeOrbit(Vector2.Zero());
        const finalRotation = Quaternion.FromYawPitchRoll(Angle.FromDegrees(30), Angle.FromDegrees(-45), Angle.FromDegrees(0));
        sceneRoot.rotation = finalRotation;
        orbit.finishOrbit();
        expect(doDispatchMock).toHaveBeenCalled();
    });
    it("Does not dispatch a state change if the rotaiton has not changed", () => {
        const { sceneRoot, orbit, doDispatchMock } = makeTestRig();
        const initialRotation = Quaternion.FromYawPitchRoll(Angle.FromDegrees(30), Angle.FromDegrees(45), Angle.FromDegrees(0));
        sceneRoot.rotation = initialRotation;
        orbit.initializeOrbit(Vector2.Zero());
        orbit.finishOrbit();
        expect(doDispatchMock).not.toHaveBeenCalled();
    });
    it("Gets the UC", () => {
        const { orbit, appObjects } = makeTestRig();
        expect(OrbitSceneRootUC.get(appObjects)).toEqual(orbit);
    });
    it("Quits updating after orbit has been cancelled", () => {
        const { sceneRoot, orbit, mockPick } = makeTestRig();
        expect(sceneRoot.position).toEqual(Vector3.Zero());
        const pickPos = new Vector3(1, 2, 3);
        const pick = {
            hit: true,
            sceneRay: ParametricLine.Forward(),
            worldPickPoint: pickPos,
        };
        mockPick.scenePixelPick.mockReturnValue(pick);
        orbit.initializeOrbit(Vector2.Zero());
        orbit.cancelOrbit();
        orbit.doOrbit(new Vector2(100, 0));
        expect(sceneRoot.position).toEqual(Vector3.Zero());
    });
});
//# sourceMappingURL=OrbitSceneRoot.test.js.map