import "../motion-utils/dist-es-errors.js";
import { noop } from "../motion-utils/dist-es-noop.js";
import { millisecondsToSeconds, secondsToMilliseconds } from "../motion-utils/dist-es-time-conversion.js";
import { isGenerator } from "../motion-dom/dist-es-animation-generators-utils-is-generator.js";
import { attachTimeline } from "../motion-dom/dist-es-animation-waapi-utils-attach-timeline.js";
import { isWaapiSupportedEasing } from "../motion-dom/dist-es-animation-waapi-utils-easing.js";
import { supportsLinearEasing } from "../motion-dom/dist-es-utils-supports-linear-easing.js";
import { anticipate } from "./dist-es-easing-anticipate.js";
import { backInOut } from "./dist-es-easing-back.js";
import { circInOut } from "./dist-es-easing-circ.js";
import { DOMKeyframesResolver } from "./dist-es-render-dom-DOMKeyframesResolver.js";
import { BaseAnimation } from "./dist-es-animation-animators-BaseAnimation.js";
import { MainThreadAnimation } from "./dist-es-animation-animators-MainThreadAnimation.js";
import { acceleratedValues } from "./dist-es-animation-animators-utils-accelerated-values.js";
import { startWaapiAnimation } from "./dist-es-animation-animators-waapi-index.js";
import { getFinalKeyframe } from "./dist-es-animation-animators-waapi-utils-get-final-keyframe.js";
import { supportsWaapi } from "./dist-es-animation-animators-waapi-utils-supports-waapi.js";
const sampleDelta = 10;
const maxDuration = 2e4;
function requiresPregeneratedKeyframes(options) {
  return isGenerator(options.type) || options.type === "spring" || !isWaapiSupportedEasing(options.ease);
}
function pregenerateKeyframes(keyframes, options) {
  const sampleAnimation = new MainThreadAnimation({
    ...options,
    keyframes,
    repeat: 0,
    delay: 0,
    isGenerator: true
  });
  let state = { done: false, value: keyframes[0] };
  const pregeneratedKeyframes = [];
  let t = 0;
  while (!state.done && t < maxDuration) {
    state = sampleAnimation.sample(t);
    pregeneratedKeyframes.push(state.value);
    t += sampleDelta;
  }
  return {
    times: void 0,
    keyframes: pregeneratedKeyframes,
    duration: t - sampleDelta,
    ease: "linear"
  };
}
const unsupportedEasingFunctions = {
  anticipate,
  backInOut,
  circInOut
};
function isUnsupportedEase(key) {
  return key in unsupportedEasingFunctions;
}
class AcceleratedAnimation extends BaseAnimation {
  constructor(options) {
    super(options);
    const { name, motionValue, element, keyframes } = this.options;
    this.resolver = new DOMKeyframesResolver(keyframes, (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe), name, motionValue, element);
    this.resolver.scheduleResolve();
  }
  initPlayback(keyframes, finalKeyframe) {
    let { duration = 300, times, ease, type, motionValue, name, startTime } = this.options;
    if (!motionValue.owner || !motionValue.owner.current) {
      return false;
    }
    if (typeof ease === "string" && supportsLinearEasing() && isUnsupportedEase(ease)) {
      ease = unsupportedEasingFunctions[ease];
    }
    if (requiresPregeneratedKeyframes(this.options)) {
      const { onComplete, onUpdate, motionValue: motionValue2, element, ...options } = this.options;
      const pregeneratedAnimation = pregenerateKeyframes(keyframes, options);
      keyframes = pregeneratedAnimation.keyframes;
      if (keyframes.length === 1) {
        keyframes[1] = keyframes[0];
      }
      duration = pregeneratedAnimation.duration;
      times = pregeneratedAnimation.times;
      ease = pregeneratedAnimation.ease;
      type = "keyframes";
    }
    const animation = startWaapiAnimation(motionValue.owner.current, name, keyframes, { ...this.options, duration, times, ease });
    animation.startTime = startTime !== null && startTime !== void 0 ? startTime : this.calcStartTime();
    if (this.pendingTimeline) {
      attachTimeline(animation, this.pendingTimeline);
      this.pendingTimeline = void 0;
    } else {
      animation.onfinish = () => {
        const { onComplete } = this.options;
        motionValue.set(getFinalKeyframe(keyframes, this.options, finalKeyframe));
        onComplete && onComplete();
        this.cancel();
        this.resolveFinishedPromise();
      };
    }
    return {
      animation,
      duration,
      times,
      type,
      ease,
      keyframes
    };
  }
  get duration() {
    const { resolved } = this;
    if (!resolved)
      return 0;
    const { duration } = resolved;
    return millisecondsToSeconds(duration);
  }
  get time() {
    const { resolved } = this;
    if (!resolved)
      return 0;
    const { animation } = resolved;
    return millisecondsToSeconds(animation.currentTime || 0);
  }
  set time(newTime) {
    const { resolved } = this;
    if (!resolved)
      return;
    const { animation } = resolved;
    animation.currentTime = secondsToMilliseconds(newTime);
  }
  get speed() {
    const { resolved } = this;
    if (!resolved)
      return 1;
    const { animation } = resolved;
    return animation.playbackRate;
  }
  set speed(newSpeed) {
    const { resolved } = this;
    if (!resolved)
      return;
    const { animation } = resolved;
    animation.playbackRate = newSpeed;
  }
  get state() {
    const { resolved } = this;
    if (!resolved)
      return "idle";
    const { animation } = resolved;
    return animation.playState;
  }
  get startTime() {
    const { resolved } = this;
    if (!resolved)
      return null;
    const { animation } = resolved;
    return animation.startTime;
  }
  /**
   * Replace the default DocumentTimeline with another AnimationTimeline.
   * Currently used for scroll animations.
   */
  attachTimeline(timeline) {
    if (!this._resolved) {
      this.pendingTimeline = timeline;
    } else {
      const { resolved } = this;
      if (!resolved)
        return noop;
      const { animation } = resolved;
      attachTimeline(animation, timeline);
    }
    return noop;
  }
  play() {
    if (this.isStopped)
      return;
    const { resolved } = this;
    if (!resolved)
      return;
    const { animation } = resolved;
    if (animation.playState === "finished") {
      this.updateFinishedPromise();
    }
    animation.play();
  }
  pause() {
    const { resolved } = this;
    if (!resolved)
      return;
    const { animation } = resolved;
    animation.pause();
  }
  stop() {
    this.resolver.cancel();
    this.isStopped = true;
    if (this.state === "idle")
      return;
    this.resolveFinishedPromise();
    this.updateFinishedPromise();
    const { resolved } = this;
    if (!resolved)
      return;
    const { animation, keyframes, duration, type, ease, times } = resolved;
    if (animation.playState === "idle" || animation.playState === "finished") {
      return;
    }
    if (this.time) {
      const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
      const sampleAnimation = new MainThreadAnimation({
        ...options,
        keyframes,
        duration,
        type,
        ease,
        times,
        isGenerator: true
      });
      const sampleTime = secondsToMilliseconds(this.time);
      motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
    }
    const { onStop } = this.options;
    onStop && onStop();
    this.cancel();
  }
  complete() {
    const { resolved } = this;
    if (!resolved)
      return;
    resolved.animation.finish();
  }
  cancel() {
    const { resolved } = this;
    if (!resolved)
      return;
    resolved.animation.cancel();
  }
  static supports(options) {
    const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
    if (!motionValue || !motionValue.owner || !(motionValue.owner.current instanceof HTMLElement)) {
      return false;
    }
    const { onUpdate, transformTemplate } = motionValue.owner.getProps();
    return supportsWaapi() && name && acceleratedValues.has(name) && /**
     * If we're outputting values to onUpdate then we can't use WAAPI as there's
     * no way to read the value from WAAPI every frame.
     */
    !onUpdate && !transformTemplate && !repeatDelay && repeatType !== "mirror" && damping !== 0 && type !== "inertia";
  }
}
export {
  AcceleratedAnimation
};
//# sourceMappingURL=dist-es-animation-animators-AcceleratedAnimation.js.map
