import {AnimationCurve} from "../AnimationCurve.js";
import {DraggableAspect} from "../../../ui/DraggableAspect.js";
import {position_canvas_to_curve} from "../draw/position_canvas_to_curve.js";
import Vector2 from "../../../../core/geom/Vector2.js";
import {updateKeyframePosition} from "./updateKeyframePosition.js";
import {number_pretty_print} from "../../../../core/primitives/numbers/number_pretty_print.js";
import {applyActionMove} from "../actionProcessorOperations/applyActionChange.js";

/**
 *
 * @param {element} markerElement
 * @param {CanvasView} graph
 * @param {AABB2} frame
 * @param {Vector2} margin
 * @param {AABB2} validEditableBounds
 * @param {AnimationCurve} curve
 * @param {function} fnUpdateGraph
 * @param {Keyframe} keyframe
 * @param {List} selectedKeyframes
 * @param {ObservedValue} activeKeyframe
 * @param {LabelView} keyCoordinateDisplay
 * @param {function} fnUpdateMarkerPosition
 * @param {Signal} frameUpdated
 * @param {Signal} keyframeChange
 * @param {EmptyView} vContainer
 * @param {ActionProcessor} actionProcessor
 * @param {keyframesContext} actionProcessorCTX
 * @returns {DraggableAspect}
 */
export function createKeyframeDraggableAspect({
                                                  markerElement,
                                                  graph,
                                                  frame,
                                                  margin,
                                                  validEditableBounds,
                                                  curve,
                                                  fnUpdateGraph, keyframe,
                                                  selectedKeyframes,
                                                  activeKeyframe,
                                                  keyCoordinateDisplay,
                                                  fnUpdateMarkerPosition,
                                                  frameUpdated,
                                                  keyframeChange,
                                                  vContainer,
                                                  actionProcessor,
                                                  actionProcessorCTX
                                                }) {
    const curveSet = {
        curveStartIndices: new AnimationCurve(),
        curveStartValues: new AnimationCurve(),
        curveEndIndices: new AnimationCurve(),
        curveEndValue: new AnimationCurve(),
    }

    return new DraggableAspect({
        el: markerElement,
        drag(position) {
            const moveKeyframeToCoord = position_canvas_to_curve(
                graph.size,
                frame,
                margin,
                position.x,
                position.y
            );

            if (isMovingOutOfBounds(moveKeyframeToCoord, keyframe, validEditableBounds, selectedKeyframes)) {
                const deltaPosition = new Vector2(
                    moveKeyframeToCoord.x - keyframe.time,
                    moveKeyframeToCoord.y - keyframe.value
                )

                selectedKeyframes.forEach((selKf) => {
                    if (activeKeyframe.get() !== selKf) {
                        updateKeyframePosition(selKf, deltaPosition, curve);
                    }
                });

                updateKeyframePosition(keyframe, deltaPosition, curve);
            }

            keyCoordinateDisplay.updateText(`${number_pretty_print(moveKeyframeToCoord.x)}, ${number_pretty_print(moveKeyframeToCoord.y)}`);
            keyCoordinateDisplay.position.set(
                position.x,
                position.y - 24
            );

            fnUpdateMarkerPosition();
            frameUpdated.send0();
            fnUpdateGraph();

            keyframeChange.send0();
        },
        dragStart() {
            activeKeyframe.set(keyframe);

            curveSet.curveStartIndices.clear()
            curveSet.curveStartIndices.addMany(curve.keys)
            curveSet.curveStartValues.copy(curve); //deep copy

            vContainer.addChild(keyCoordinateDisplay);
        },
        dragEnd() {
            curveSet.curveEndIndices.clear()
            curveSet.curveEndIndices.addMany(curve.keys)
            curveSet.curveEndValue.copy(curve); //deep copy

            vContainer.removeChild(keyCoordinateDisplay);

            //Reset back before action processor
            for (let i = 0; i < curve.length; i++) {
                curve.keys[i] = curveSet.curveStartIndices.keys[i];
                curve.keys[i].copy(curveSet.curveStartValues.keys[i]);
            }
            applyActionMove('move keyframe(s)', curveSet, actionProcessor, actionProcessorCTX);

        }
    });
}

/**
 * Check if new keyframe position is in the valid bounds
 * @param {Vector2} newCoord
 * @param {Keyframe} keyframe
 * @param {AABB2} validBounds
 * @param {List} selectedKeyframes
 * @returns {boolean}
 */
function isMovingOutOfBounds(newCoord, keyframe, validBounds, selectedKeyframes) {
    const transitionVector = newCoord.clone()._sub(keyframe.time, keyframe.value);

    const isSelectionOutOfBound = (kf) => {
        const adjustedCord = transitionVector.clone()._add(kf.time, kf.value);
        return (!validBounds.containsPoint(adjustedCord.x, adjustedCord.y))
    };

    if (!validBounds.containsPoint(newCoord.x, newCoord.y)
        || selectedKeyframes.some(isSelectionOutOfBound)) {
        return false;
    }
    //all checks passed
    return true;
}