import { makeAppObjectRepo } from "@vived/core";
import { Angle, ParametricLine, Quaternion, Vector3 } from "@vived/core";
import { makeMockSceneRayPickUC } from "../../Babylon/Mocks/MockSceneRayPickUC";
import { makeBaseAppState } from "../../BaseApp/Entity/BaseAppState";
import { makeChallengeEntityMock } from "../../Challenge/Mocks/ChallengeEntityMock";
import { makeTransform3D } from "../../Transform3D/Entities/Transform3D";
import { LabelDropDragMode, makeLabelDropRepo, } from "../Entities/LabelDropRepo";
import { makeMockLabelDropCorrectAnimationUC } from "../Mocks/MockCorrectAnimation";
import { makeMockDropAnimation } from "../Mocks/MockDropAnimation";
import { makeMockLabelDropIncorrectAnimationUC } from "../Mocks/MockIncorrectAnimation";
import { makeDragLabelDropWorldUC } from "./DragLabelDropWorldUC";
// Default setup
// Hit is 1,2,3 (world)
// Stylus is 1,2,0 (world)
// Local hit is 0,0,3 (stylus)
function makeTestRig() {
    const appObjects = makeAppObjectRepo();
    const challenge = makeChallengeEntityMock(appObjects);
    const baseApp = makeBaseAppState(appObjects.getOrCreate("BaseApp"));
    const labelDropAO = appObjects.getOrCreate("labelDrop");
    const mockDropAnimation = makeMockDropAnimation(appObjects);
    const mockCorrectAnimation = makeMockLabelDropCorrectAnimationUC(appObjects);
    const mockIncorrectAnimation = makeMockLabelDropIncorrectAnimationUC(appObjects);
    const labelDrop = makeLabelDropRepo(labelDropAO);
    const changeObserver = jest.fn();
    labelDrop.addChangeObserver(changeObserver);
    const mockRayPick = makeMockSceneRayPickUC(appObjects);
    const mockPick = jest.fn().mockReturnValue({
        hit: true,
        worldPickPoint: new Vector3(1, 2, 3),
    });
    mockRayPick.sceneRayPick = mockPick;
    const mockStylusTransform = makeTransform3D(appObjects.getOrCreate("stylus"));
    labelDrop.dragHelperTransform.parent = mockStylusTransform;
    mockStylusTransform.worldPosition = new Vector3(1, 2, 0);
    const uc = makeDragLabelDropWorldUC(labelDropAO);
    challenge.selectedChallenge = "LABEL_DROP";
    baseApp.isAuthoring = false;
    return {
        appObjects,
        labelDrop,
        uc,
        changeObserver,
        baseApp,
        challenge,
        mockPick,
        mockStylusTransform,
        mockDropAnimation,
        mockCorrectAnimation,
        mockIncorrectAnimation,
    };
}
describe("Drag Label Drop UC", () => {
    it("initializes id on press, mode and alpha", () => {
        const { uc, labelDrop } = makeTestRig();
        expect(labelDrop.draggingID).toBeUndefined();
        uc.initializeDrag("id1", ParametricLine.Forward());
        expect(labelDrop.draggingID).toEqual("id1");
        expect(labelDrop.dragMode).toEqual(LabelDropDragMode.WORLD);
        expect(labelDrop.dragAlpha).toEqual(1);
    });
    it("Sets the world position of the label", () => {
        const { uc, labelDrop } = makeTestRig();
        uc.initializeDrag("id1", ParametricLine.Forward());
        const expectPos = new Vector3(1, 2, 3 * uc.percentOfPickDepth);
        expect(labelDrop.dragLabelTransform.worldPosition).toEqual(expectPos);
    });
    it("Sets the world rotation of the label", () => {
        const { uc, labelDrop, mockStylusTransform } = makeTestRig();
        const rot = Quaternion.FromYawPitchRoll(Angle.FromDegrees(10), Angle.FromDegrees(20), Angle.FromDegrees(30));
        mockStylusTransform.worldRotation = rot;
        uc.initializeDrag("id1", ParametricLine.Forward());
        expect(Quaternion.Close(labelDrop.dragLabelTransform.worldRotation, rot)).toEqual(true);
    });
    it("Does not initialize if challenge is not label drop", () => {
        const { uc, labelDrop, challenge } = makeTestRig();
        challenge.selectedChallenge = "NONE";
        uc.initializeDrag("id1", ParametricLine.Forward());
        expect(labelDrop.draggingID).toBeUndefined();
    });
    it("Does not initialize if authoring", () => {
        const { uc, labelDrop, baseApp } = makeTestRig();
        baseApp.isAuthoring = true;
        uc.initializeDrag("id1", ParametricLine.Forward());
        expect(labelDrop.draggingID).toBeUndefined();
    });
    it("Updates the position on drag", () => {
        const { uc, labelDrop, mockPick, mockStylusTransform } = makeTestRig();
        uc.initializeDrag("id", ParametricLine.Forward());
        // Move stylus to 10,20,30 (world)
        // Set pick to 10,20,0 (world)
        // Local pick is 0,0, 30 (local stylus)
        // Reduced is 0,0, 28.5 (assume 95%)
        // Reduced is 10,20,1.5 (world)
        mockStylusTransform.worldPosition = new Vector3(10, 20, 30);
        const mockPickResult = {
            hit: true,
            worldPickPoint: new Vector3(10, 20, 0),
        };
        mockPick.mockReturnValue(mockPickResult);
        uc.updateDrag(ParametricLine.Forward());
        const expectedDepth = 30 * (1 - uc.percentOfPickDepth);
        const expectedPos = new Vector3(10, 20, expectedDepth);
        expect(Vector3.Close(labelDrop.dragLabelTransform.worldPosition, expectedPos)).toEqual(true);
    });
    it("Sets the position at zero parallax if there is no intersection", () => {
        const { uc, labelDrop, mockPick } = makeTestRig();
        uc.initializeDrag("id", ParametricLine.Forward());
        const mockPickResult = {
            hit: false,
        };
        mockPick.mockReturnValue(mockPickResult);
        uc.updateDrag(ParametricLine.Forward());
        const expectedPos = new Vector3(1, 2, 0);
        expect(Vector3.Close(labelDrop.dragLabelTransform.worldPosition, expectedPos)).toEqual(true);
    });
    it("Updates the rotation on drag", () => {
        const { uc, labelDrop, mockStylusTransform } = makeTestRig();
        uc.initializeDrag("id", ParametricLine.Forward());
        expect(Quaternion.Close(labelDrop.dragLabelTransform.worldRotation, Quaternion.Identity())).toEqual(true);
        const rot = Quaternion.FromYawPitchRoll(Angle.FromDegrees(10), Angle.FromDegrees(20), Angle.FromDegrees(30));
        mockStylusTransform.worldRotation = rot;
        uc.updateDrag(ParametricLine.Forward());
        expect(Quaternion.Close(labelDrop.dragLabelTransform.worldRotation, rot)).toEqual(true);
    });
    it("Does not set the 3D position if it did not initialize", () => {
        const { uc, labelDrop, mockPick } = makeTestRig();
        labelDrop.dragLabelTransform.worldPosition = Vector3.Zero();
        mockPick.mockReturnValue({
            worldPickPoint: new Vector3(1, 2, 3),
            hit: true,
            normal: new Vector3(4, 5, 6),
            objectPickMeta: {
                appObjectID: "appObj1",
            },
        });
        uc.updateDrag(ParametricLine.Forward());
        expect(labelDrop.dragLabelTransform.worldPosition).toEqual(Vector3.Zero());
    });
    it("Submits the object hit when released", () => {
        const { uc, labelDrop, mockPick } = makeTestRig();
        const mockPickResult = {
            hit: true,
            worldPickPoint: new Vector3(1, 2, 3),
            appObjectID: "appObj1",
        };
        mockPick.mockReturnValue(mockPickResult);
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        labelDrop.submitLabelDrop = jest.fn();
        uc.releaseDrag(ParametricLine.Forward());
        expect(labelDrop.submitLabelDrop).toHaveBeenCalledWith("labelDrop1", "appObj1");
    });
    it("submits a label drop answer if an app object is hit", () => {
        const { uc, labelDrop, mockPick } = makeTestRig();
        const mockPickResult = {
            hit: true,
            worldPickPoint: new Vector3(1, 2, 3),
            appObjectID: "appObj1",
        };
        mockPick.mockReturnValue(mockPickResult);
        labelDrop.submitLabelDrop = jest.fn();
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        uc.releaseDrag(ParametricLine.Forward());
        expect(labelDrop.submitLabelDrop).toHaveBeenCalled();
    });
    it("Does not submit a label drop answer if an app object is not hit", async () => {
        const { uc, labelDrop, mockPick } = makeTestRig();
        labelDrop.submitLabelDrop = jest.fn();
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        const mockPickResult = {
            hit: false,
        };
        mockPick.mockReturnValue(mockPickResult);
        uc.releaseDrag(ParametricLine.Forward());
        expect(labelDrop.submitLabelDrop).not.toHaveBeenCalled();
    });
    it("Kicks off the drop animation", () => {
        const { uc, mockPick, mockDropAnimation } = makeTestRig();
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        const mockPickResult = {
            hit: false,
        };
        mockPick.mockReturnValue(mockPickResult);
        uc.releaseDrag(ParametricLine.Forward());
        expect(mockDropAnimation.start).toHaveBeenCalled();
    });
    it("Sets the desired position for the drop animation", () => {
        const { uc, mockPick, mockDropAnimation } = makeTestRig();
        expect(mockDropAnimation.desiredFinalPosition).toEqual(Vector3.Zero());
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        const mockPickResult = {
            hit: false,
        };
        mockPick.mockReturnValue(mockPickResult);
        uc.releaseDrag(ParametricLine.Forward());
        expect(mockDropAnimation.desiredFinalPosition).toEqual(new Vector3(1, 2, 3));
    });
    it("Kicks off the incorrect animation", () => {
        const { uc, mockPick, mockIncorrectAnimation, labelDrop } = makeTestRig();
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        const mockPickResult = {
            hit: true,
            worldPickPoint: new Vector3(1, 2, 3),
            appObjectID: "appObj",
        };
        mockPick.mockReturnValue(mockPickResult);
        labelDrop.submitLabelDrop = jest.fn().mockReturnValue(false);
        uc.releaseDrag(ParametricLine.Forward());
        expect(mockIncorrectAnimation.start).toHaveBeenCalled();
    });
    it("Sets the desired position for the incorrect animation", () => {
        const { uc, mockPick, mockIncorrectAnimation, labelDrop } = makeTestRig();
        expect(mockIncorrectAnimation.desiredFinalPosition).toEqual(Vector3.Zero());
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        const mockPickResult = {
            hit: true,
            worldPickPoint: new Vector3(1, 2, 3),
            appObjectID: "appObj",
        };
        mockPick.mockReturnValue(mockPickResult);
        labelDrop.submitLabelDrop = jest.fn().mockReturnValue(false);
        uc.releaseDrag(ParametricLine.Forward());
        expect(mockIncorrectAnimation.desiredFinalPosition).toEqual(new Vector3(1, 2, 3));
    });
    it("Kicks off the correct animation", () => {
        const { uc, mockPick, mockCorrectAnimation, labelDrop } = makeTestRig();
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        const mockPickResult = {
            hit: true,
            worldPickPoint: new Vector3(1, 2, 3),
            appObjectID: "appObj",
        };
        mockPick.mockReturnValue(mockPickResult);
        labelDrop.submitLabelDrop = jest.fn().mockReturnValue(true);
        uc.releaseDrag(ParametricLine.Forward());
        expect(mockCorrectAnimation.start).toHaveBeenCalled();
    });
    it("Does not submit if no object hit on release", () => {
        const { uc, labelDrop, mockPick } = makeTestRig();
        mockPick.mockReturnValue({
            worldPickPoint: new Vector3(1, 2, 3),
            hit: false,
            normal: new Vector3(4, 5, 6),
            objectPickMeta: {},
        });
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        labelDrop.submitLabelDrop = jest.fn();
        uc.releaseDrag(ParametricLine.Forward());
        expect(labelDrop.submitLabelDrop).not.toHaveBeenCalled();
    });
    it("Resets dragging ID after the animation ends", async () => {
        const { uc, labelDrop } = makeTestRig();
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        expect(labelDrop.draggingID).toEqual("labelDrop1");
        await uc.releaseDrag(ParametricLine.Forward());
        expect(labelDrop.draggingID).toBeUndefined();
    });
    it("Resets the result after the animation ends", async () => {
        const { uc, labelDrop } = makeTestRig();
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        expect(labelDrop.draggingID).toEqual("labelDrop1");
        const spy = jest.spyOn(labelDrop, "resultIsCorrect", "set");
        await uc.releaseDrag(ParametricLine.Forward());
        expect(spy).toHaveBeenCalledWith(undefined);
    });
    it("Sets mode to review if correct if there are no more labels after the animation has completed", async () => {
        const { uc, labelDrop, baseApp, mockPick } = makeTestRig();
        expect(baseApp.isReviewing).toEqual(false);
        const mockPickResult = {
            hit: true,
            worldPickPoint: new Vector3(1, 2, 3),
            appObjectID: "appObj",
        };
        mockPick.mockReturnValue(mockPickResult);
        labelDrop.getRemainingLabelDrops = jest.fn().mockReturnValue([]);
        labelDrop.submitLabelDrop = jest.fn().mockReturnValue(true);
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        await uc.releaseDrag(ParametricLine.Forward());
        expect(baseApp.isReviewing).toEqual(true);
    });
    it("Does not set the mode to review  if there are  more labels", () => {
        const { uc, labelDrop, baseApp } = makeTestRig();
        expect(baseApp.isReviewing).toEqual(false);
        labelDrop.getRemainingLabelDrops = jest
            .fn()
            .mockReturnValue(["anbotherLabel"]);
        labelDrop.submitLabelDrop = jest.fn().mockReturnValue(true);
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        uc.releaseDrag(ParametricLine.Forward());
        expect(baseApp.isReviewing).toEqual(false);
    });
    it("Cancels any running animations on initialization", () => {
        const { uc, labelDrop, mockDropAnimation } = makeTestRig();
        labelDrop.getRemainingLabelDrops = jest.fn().mockReturnValue([]);
        uc.initializeDrag("labelDrop1", ParametricLine.Forward());
        uc.releaseDrag(ParametricLine.Forward());
        expect(mockDropAnimation.start).toHaveBeenCalled();
        uc.initializeDrag("labelDrop2", ParametricLine.Forward());
        expect(mockDropAnimation.cancel).toHaveBeenCalled();
    });
});
//# sourceMappingURL=DragLabelDropWorldUC.test.js.map