'use strict';

var AsyncMotionValueAnimation = require('../AsyncMotionValueAnimation.js');
var JSAnimation = require('../JSAnimation.js');
var getValueTransition = require('../utils/get-value-transition.js');
var makeAnimationInstant = require('../utils/make-animation-instant.js');
var defaultTransitions = require('../utils/default-transitions.js');
var getFinal = require('../keyframes/get-final.js');
var isTransitionDefined = require('../utils/is-transition-defined.js');
var frame = require('../../frameloop/frame.js');
var timeConversion = require('../../../../../../../motion-utils@12.36.0/node_modules/motion-utils/dist/es/time-conversion.js');
var globalConfig = require('../../../../../../../motion-utils@12.36.0/node_modules/motion-utils/dist/es/global-config.js');

const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
    const valueTransition = getValueTransition.getValueTransition(transition, name) || {};
    /**
     * Most transition values are currently completely overwritten by value-specific
     * transitions. In the future it'd be nicer to blend these transitions. But for now
     * delay actually does inherit from the root transition if not value-specific.
     */
    const delay = valueTransition.delay || transition.delay || 0;
    /**
     * Elapsed isn't a public transition option but can be passed through from
     * optimized appear effects in milliseconds.
     */
    let { elapsed = 0 } = transition;
    elapsed = elapsed - timeConversion.secondsToMilliseconds(delay);
    const options = {
        keyframes: Array.isArray(target) ? target : [null, target],
        ease: "easeOut",
        velocity: value.getVelocity(),
        ...valueTransition,
        delay: -elapsed,
        onUpdate: (v) => {
            value.set(v);
            valueTransition.onUpdate && valueTransition.onUpdate(v);
        },
        onComplete: () => {
            onComplete();
            valueTransition.onComplete && valueTransition.onComplete();
        },
        name,
        motionValue: value,
        element: isHandoff ? undefined : element,
    };
    /**
     * If there's no transition defined for this value, we can generate
     * unique transition settings for this value.
     */
    if (!isTransitionDefined.isTransitionDefined(valueTransition)) {
        Object.assign(options, defaultTransitions.getDefaultTransition(name, options));
    }
    /**
     * Both WAAPI and our internal animation functions use durations
     * as defined by milliseconds, while our external API defines them
     * as seconds.
     */
    options.duration && (options.duration = timeConversion.secondsToMilliseconds(options.duration));
    options.repeatDelay && (options.repeatDelay = timeConversion.secondsToMilliseconds(options.repeatDelay));
    /**
     * Support deprecated way to set initial value. Prefer keyframe syntax.
     */
    if (options.from !== undefined) {
        options.keyframes[0] = options.from;
    }
    let shouldSkip = false;
    if (options.type === false ||
        (options.duration === 0 && !options.repeatDelay)) {
        makeAnimationInstant.makeAnimationInstant(options);
        if (options.delay === 0) {
            shouldSkip = true;
        }
    }
    if (globalConfig.MotionGlobalConfig.instantAnimations ||
        globalConfig.MotionGlobalConfig.skipAnimations ||
        element?.shouldSkipAnimations) {
        shouldSkip = true;
        makeAnimationInstant.makeAnimationInstant(options);
        options.delay = 0;
    }
    /**
     * If the transition type or easing has been explicitly set by the user
     * then we don't want to allow flattening the animation.
     */
    options.allowFlatten = !valueTransition.type && !valueTransition.ease;
    /**
     * If we can or must skip creating the animation, and apply only
     * the final keyframe, do so. We also check once keyframes are resolved but
     * this early check prevents the need to create an animation at all.
     */
    if (shouldSkip && !isHandoff && value.get() !== undefined) {
        const finalKeyframe = getFinal.getFinalKeyframe(options.keyframes, valueTransition);
        if (finalKeyframe !== undefined) {
            frame.frame.update(() => {
                options.onUpdate(finalKeyframe);
                options.onComplete();
            });
            return;
        }
    }
    return valueTransition.isSync
        ? new JSAnimation.JSAnimation(options)
        : new AsyncMotionValueAnimation.AsyncMotionValueAnimation(options);
};

exports.animateMotionValue = animateMotionValue;
