import { time as u } from "../frameloop/sync-time.es.js";
import { mix as P } from "../utils/mix/index.es.js";
import { frameloopDriver as I } from "./drivers/frame.es.js";
import { inertia as K } from "./generators/inertia.es.js";
import { keyframes as c } from "./generators/keyframes.es.js";
import { calcGeneratorDuration as O } from "./generators/utils/calc-duration.es.js";
import { getGeneratorVelocity as V } from "./generators/utils/velocity.es.js";
import { getFinalKeyframe as C } from "./keyframes/get-final.es.js";
import { replaceTransitionType as E } from "./utils/replace-transition-type.es.js";
import { WithPromise as N } from "./utils/WithPromise.es.js";
import { invariant as W } from "../../../../../../motion-utils@12.36.0/node_modules/motion-utils/dist/es/errors.es.js";
import { pipe as $ } from "../../../../../../motion-utils@12.36.0/node_modules/motion-utils/dist/es/pipe.es.js";
import { millisecondsToSeconds as f, secondsToMilliseconds as B } from "../../../../../../motion-utils@12.36.0/node_modules/motion-utils/dist/es/time-conversion.es.js";
import { clamp as J } from "../../../../../../motion-utils@12.36.0/node_modules/motion-utils/dist/es/clamp.es.js";
const U = (g) => g / 100;
class rt extends N {
  constructor(t) {
    super(), this.state = "idle", this.startTime = null, this.isStopped = !1, this.currentTime = 0, this.holdTime = null, this.playbackSpeed = 1, this.delayState = {
      done: !1,
      value: void 0
    }, this.stop = () => {
      var s, e;
      const { motionValue: i } = this.options;
      i && i.updatedAt !== u.now() && this.tick(u.now()), this.isStopped = !0, this.state !== "idle" && (this.teardown(), (e = (s = this.options).onStop) == null || e.call(s));
    }, this.options = t, this.initAnimation(), this.play(), t.autoplay === !1 && this.pause();
  }
  initAnimation() {
    const { options: t } = this;
    E(t);
    const { type: i = c, repeat: s = 0, repeatDelay: e = 0, repeatType: n, velocity: m = 0 } = t;
    let { keyframes: r } = t;
    const l = i || c;
    process.env.NODE_ENV !== "production" && l !== c && W(r.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${r}`, "spring-two-frames"), l !== c && typeof r[0] != "number" && (this.mixKeyframes = $(U, P(r[0], r[1])), r = [0, 100]);
    const o = l({ ...t, keyframes: r });
    n === "mirror" && (this.mirroredGenerator = l({
      ...t,
      keyframes: [...r].reverse(),
      velocity: -m
    })), o.calculatedDuration === null && (o.calculatedDuration = O(o));
    const { calculatedDuration: d } = o;
    this.calculatedDuration = d, this.resolvedDuration = d + e, this.totalDuration = this.resolvedDuration * (s + 1) - e, this.generator = o;
  }
  updateTime(t) {
    const i = Math.round(t - this.startTime) * this.playbackSpeed;
    this.holdTime !== null ? this.currentTime = this.holdTime : this.currentTime = i;
  }
  tick(t, i = !1) {
    const { generator: s, totalDuration: e, mixKeyframes: n, mirroredGenerator: m, resolvedDuration: r, calculatedDuration: l } = this;
    if (this.startTime === null)
      return s.next(0);
    const { delay: o = 0, keyframes: d, repeat: k, repeatType: D, repeatDelay: S, type: F, onUpdate: b, finalKeyframe: G } = this.options;
    this.speed > 0 ? this.startTime = Math.min(this.startTime, t) : this.speed < 0 && (this.startTime = Math.min(t - e / this.speed, this.startTime)), i ? this.currentTime = t : this.updateTime(t);
    const T = this.currentTime - o * (this.playbackSpeed >= 0 ? 1 : -1), y = this.playbackSpeed >= 0 ? T < 0 : T > e;
    this.currentTime = Math.max(T, 0), this.state === "finished" && this.holdTime === null && (this.currentTime = e);
    let x = this.currentTime, w = s;
    if (k) {
      const v = Math.min(this.currentTime, e) / r;
      let p = Math.floor(v), h = v % 1;
      !h && v >= 1 && (h = 1), h === 1 && p--, p = Math.min(p, k + 1), !!(p % 2) && (D === "reverse" ? (h = 1 - h, S && (h -= S / r)) : D === "mirror" && (w = m)), x = J(0, 1, h) * r;
    }
    let a;
    y ? (this.delayState.value = d[0], a = this.delayState) : a = w.next(x), n && !y && (a.value = n(a.value));
    let { done: M } = a;
    !y && l !== null && (M = this.playbackSpeed >= 0 ? this.currentTime >= e : this.currentTime <= 0);
    const A = this.holdTime === null && (this.state === "finished" || this.state === "running" && M);
    return A && F !== K && (a.value = C(d, this.options, G, this.speed)), b && b(a.value), A && this.finish(), a;
  }
  /**
   * Allows the returned animation to be awaited or promise-chained. Currently
   * resolves when the animation finishes at all but in a future update could/should
   * reject if its cancels.
   */
  then(t, i) {
    return this.finished.then(t, i);
  }
  get duration() {
    return f(this.calculatedDuration);
  }
  get iterationDuration() {
    const { delay: t = 0 } = this.options || {};
    return this.duration + f(t);
  }
  get time() {
    return f(this.currentTime);
  }
  set time(t) {
    t = B(t), this.currentTime = t, this.startTime === null || this.holdTime !== null || this.playbackSpeed === 0 ? this.holdTime = t : this.driver && (this.startTime = this.driver.now() - t / this.playbackSpeed), this.driver ? this.driver.start(!1) : (this.startTime = 0, this.state = "paused", this.holdTime = t, this.tick(t));
  }
  /**
   * Returns the generator's velocity at the current time in units/second.
   * Uses the analytical derivative when available (springs), avoiding
   * the MotionValue's frame-dependent velocity estimation.
   */
  getGeneratorVelocity() {
    const t = this.currentTime;
    if (t <= 0)
      return this.options.velocity || 0;
    if (this.generator.velocity)
      return this.generator.velocity(t);
    const i = this.generator.next(t).value;
    return V((s) => this.generator.next(s).value, t, i);
  }
  get speed() {
    return this.playbackSpeed;
  }
  set speed(t) {
    const i = this.playbackSpeed !== t;
    i && this.driver && this.updateTime(u.now()), this.playbackSpeed = t, i && this.driver && (this.time = f(this.currentTime));
  }
  play() {
    var e, n;
    if (this.isStopped)
      return;
    const { driver: t = I, startTime: i } = this.options;
    this.driver || (this.driver = t((m) => this.tick(m))), (n = (e = this.options).onPlay) == null || n.call(e);
    const s = this.driver.now();
    this.state === "finished" ? (this.updateFinished(), this.startTime = s) : this.holdTime !== null ? this.startTime = s - this.holdTime : this.startTime || (this.startTime = i ?? s), this.state === "finished" && this.speed < 0 && (this.startTime += this.calculatedDuration), this.holdTime = null, this.state = "running", this.driver.start();
  }
  pause() {
    this.state = "paused", this.updateTime(u.now()), this.holdTime = this.currentTime;
  }
  complete() {
    this.state !== "running" && this.play(), this.state = "finished", this.holdTime = null;
  }
  finish() {
    var t, i;
    this.notifyFinished(), this.teardown(), this.state = "finished", (i = (t = this.options).onComplete) == null || i.call(t);
  }
  cancel() {
    var t, i;
    this.holdTime = null, this.startTime = 0, this.tick(0), this.teardown(), (i = (t = this.options).onCancel) == null || i.call(t);
  }
  teardown() {
    this.state = "idle", this.stopDriver(), this.startTime = this.holdTime = null;
  }
  stopDriver() {
    this.driver && (this.driver.stop(), this.driver = void 0);
  }
  sample(t) {
    return this.startTime = 0, this.tick(t, !0);
  }
  attachTimeline(t) {
    var i;
    return this.options.allowFlatten && (this.options.type = "keyframes", this.options.ease = "linear", this.initAnimation()), (i = this.driver) == null || i.stop(), t.observe(this);
  }
}
export {
  rt as JSAnimation
};
//# sourceMappingURL=JSAnimation.es.js.map
