import {Action} from "../../../../core/process/undo/Action.js";
import {AnimationCurve} from "../AnimationCurve.js";
import {Keyframe} from "../Keyframe.js";
import List from "../../../../core/collection/list/List.js";

export class keyframesContext{
    /**
     *
     * @param {AnimationCurve} curve
     * @param {function} removeKeyframe
     * @param {function} addKeyframe
     * @param {Map} keyframeViews
     * @param {ObservedValue} activeKeyframe
     * @param {List} selectedKeyframes
     * @param {function} handleCurveUpdate
     * @param {function} updateGraph
     */
    constructor({curve, removeKeyframe, addKeyframe, keyframeViews, activeKeyframe, selectedKeyframes, handleCurveUpdate, updateGraph}) {
        this.refCurve = curve;
        this.fnRemoveKeyframe = removeKeyframe;
        this.fnAddKeyframe = addKeyframe;
        this.refKeyframeViews = keyframeViews;
        this.refActiveKeyframe = activeKeyframe;
        this.refSelectionKeyframes = selectedKeyframes;
        this.fnHandleCurveUpdate = handleCurveUpdate;
        this.fnUpdateGraph = updateGraph;
    }
}

export class RemoveKeyframeAction extends Action {
    /**
     *
     * @param {List} passKeyframes
     * @param {Keyframe} passNullKeyframe
     * @param {keyframesContext} context
     */
    constructor(passKeyframes, passNullKeyframe, context) {
        super();
        this.copyKfToDelete = passKeyframes.clone();
        this.refNullKeyframe = passNullKeyframe;

        this.actionContext = context;

        this.linkedActiveKeyframe = this.actionContext.refActiveKeyframe.get();
        this.linkedSelectionKeyframes = new List();
        this.actionContext.refSelectionKeyframes.forEach(keyframe => {
            this.linkedSelectionKeyframes.add(keyframe);
        })
    }

    async apply(context){
        this.copyKfToDelete.forEach((selKF) => {
            this.actionContext.refCurve.remove(selKF);
            this.actionContext.fnRemoveKeyframe(selKF);
        });
        this.actionContext.fnHandleCurveUpdate();

        this.actionContext.refActiveKeyframe.set(this.refNullKeyframe);
        this.actionContext.refSelectionKeyframes.reset();
    }

    async revert(context){
        this.copyKfToDelete.forEach((selKF) => {
            this.actionContext.refCurve.add(selKF);
            this.actionContext.fnAddKeyframe(selKF);
        });
        this.actionContext.fnHandleCurveUpdate();
    }
}

export class AddKeyframeAction extends Action {
    /**
     *
     * @param {Keyframe} passKeyframe
     * @param {keyframesContext} context
     */
    constructor(passKeyframe, context) {
        super();
        this.newKeyframeToAdd = passKeyframe.clone();

        this.actionContext = context;
    }

    async apply(context){
        this.actionContext.refCurve.add(this.newKeyframeToAdd);
        this.actionContext.fnAddKeyframe(this.newKeyframeToAdd);
        this.actionContext.fnHandleCurveUpdate();
    }

    async revert(context){
        this.actionContext.refCurve.remove(this.newKeyframeToAdd);
        this.actionContext.fnRemoveKeyframe(this.newKeyframeToAdd);

        this.actionContext.fnHandleCurveUpdate();
    }
}

export class MoveKeyframeAction extends Action {
    /**
     *
     * @param {AnimationCurve} passCurveValueStart
     * @param {AnimationCurve} passCurveRefStart
     * @param {AnimationCurve} passCurveValueEnd
     * @param {AnimationCurve} passCurveRefEnd
     * @param {keyframesContext} context
     */
    constructor(passCurveValueStart, passCurveRefStart, passCurveValueEnd, passCurveRefEnd, context) {
        super();
        this.curveRefStart = passCurveRefStart;
        this.curveValueStart = passCurveValueStart.clone();

        this.curveRefEnd = passCurveRefEnd
        this.curveValueEnd = passCurveValueEnd.clone();

        this.actionContext = context;
    }

    async apply(context){
        for (let i = 0; i < this.actionContext.refCurve.length; i++) {
            this.actionContext.refCurve.keys[i] = this.curveRefEnd.keys[i];
            this.actionContext.refCurve.keys[i].copy(this.curveValueEnd.keys[i]);
        }
        this.actionContext.fnHandleCurveUpdate();
    }

    async revert(context){
        for (let i = 0; i < this.actionContext.refCurve.length; i++) {
            this.actionContext.refCurve.keys[i] = this.curveRefStart.keys[i];
            this.actionContext.refCurve.keys[i].copy(this.curveValueStart.keys[i]);
        }
        this.actionContext.fnHandleCurveUpdate();
    }
}

export class TangentChangeAction extends Action {
    /**
     *
     * @param {Keyframe} passKeyframe
     * @param {Keyframe} passKeyframeValueStart
     * @param {Keyframe} passKeyframeValueEnd
     * @param {function} handleTangentUpdate
     * @param {keyframesContext} context
     */
    constructor(passKeyframe, passKeyframeValueStart, passKeyframeValueEnd, handleTangentUpdate, context) {
        super();
        this.refKeyframe = passKeyframe;
        this.keyframeValueStart = passKeyframeValueStart.clone();
        this.keyframeValueEnd = passKeyframeValueEnd.clone();

        this.actionContext = context;
        this.fnHandleTangentUpdate = handleTangentUpdate
    }

    async apply(context){
        this.refKeyframe.copy(this.keyframeValueEnd);

        this.fnHandleTangentUpdate();
        this.actionContext.fnHandleCurveUpdate();
    }

    async revert(context){
        this.refKeyframe.copy(this.keyframeValueStart);

        this.fnHandleTangentUpdate();
        this.actionContext.fnHandleCurveUpdate();
    }
}

export class SelectionAction extends Action {
    /**
     *
     * @param {List} passSelectionKeyframe
     * @param {List} passSelectionList
     * @param {List} passPrevSelectionList
     * @param {keyframesContext} context
     */
    constructor(passSelectionKeyframe, passSelectionList, passPrevSelectionList, context) {
        super();
        this.refSelectionKeyframe = passSelectionKeyframe;
        this.keyframeList = new List()
        this.keyframeList.copy(passSelectionList)
        this.prevKeyframeList = new List()
        this.prevKeyframeList.copy(passPrevSelectionList)
        this.actionContext = context;
    }

    async apply(context){
        this.refSelectionKeyframe.reset()
        this.keyframeList.forEach(k => {
            this.refSelectionKeyframe.add(k)
        })
        this.actionContext.fnUpdateGraph();
    }

    async revert(context){
        this.refSelectionKeyframe.reset()
        this.prevKeyframeList.forEach(k => {
            this.refSelectionKeyframe.add(k)
        })
        this.actionContext.fnUpdateGraph();
    }
}