/**
   * Increments each track entry {@link TrackEntry#getTrackTime()}, setting queued animations as
   * current if needed.
   */
  public void update(float delta) {
    delta *= timeScale;
    for (int i = 0, n = tracks.size; i < n; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null) continue;

      current.animationLast = current.nextAnimationLast;
      current.trackLast = current.nextTrackLast;

      float currentDelta = delta * current.timeScale;

      if (current.delay > 0) {
        current.delay -= currentDelta;
        if (current.delay > 0) continue;
        currentDelta = -current.delay;
        current.delay = 0;
      }

      TrackEntry next = current.next;
      if (next != null) {
        // When the next entry's delay is passed, change to the next entry, preserving leftover
        // time.
        float nextTime = current.trackLast - next.delay;
        if (nextTime >= 0) {
          next.delay = 0;
          next.trackTime = nextTime + delta * next.timeScale;
          current.trackTime += currentDelta;
          setCurrent(i, next, true);
          while (next.mixingFrom != null) {
            next.mixTime += currentDelta;
            next = next.mixingFrom;
          }
          continue;
        }
      } else {
        // Clear the track when there is no next entry, the track end time is reached, and there is
        // no mixingFrom.
        if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
          tracks.set(i, null);
          queue.end(current);
          disposeNext(current);
          continue;
        }
      }
      updateMixingFrom(current, delta);

      current.trackTime += currentDelta;
    }

    queue.drain();
  }
  /**
   * Adds an animation to be played after the current or last queued animation for a track. If the
   * track is empty, it is equivalent to calling {@link #setAnimation(int, Animation, boolean)}.
   *
   * @param delay Seconds to begin this animation after the start of the previous animation. May be
   *     <= 0 to use the animation duration of the previous track minus any mix duration plus the
   *     <code>delay</code>.
   * @return A track entry to allow further customization of animation playback. References to the
   *     track entry must not be kept after the {@link AnimationStateListener#dispose(TrackEntry)}
   *     event occurs.
   */
  public TrackEntry addAnimation(int trackIndex, Animation animation, boolean loop, float delay) {
    if (animation == null) throw new IllegalArgumentException("animation cannot be null.");

    TrackEntry last = expandToIndex(trackIndex);
    if (last != null) {
      while (last.next != null) last = last.next;
    }

    TrackEntry entry = trackEntry(trackIndex, animation, loop, last);

    if (last == null) {
      setCurrent(trackIndex, entry, true);
      queue.drain();
    } else {
      last.next = entry;
      if (delay <= 0) {
        float duration = last.animationEnd - last.animationStart;
        if (duration != 0)
          delay +=
              duration * (1 + (int) (last.trackTime / duration))
                  - data.getMix(last.animation, animation);
        else delay = 0;
      }
    }

    entry.delay = delay;
    return entry;
  }
  /** @param last May be null. */
  private TrackEntry trackEntry(
      int trackIndex, Animation animation, boolean loop, TrackEntry last) {
    TrackEntry entry = trackEntryPool.obtain();
    entry.trackIndex = trackIndex;
    entry.animation = animation;
    entry.loop = loop;

    entry.eventThreshold = 0;
    entry.attachmentThreshold = 0;
    entry.drawOrderThreshold = 0;

    entry.animationStart = 0;
    entry.animationEnd = animation.getDuration();
    entry.animationLast = -1;
    entry.nextAnimationLast = -1;

    entry.delay = 0;
    entry.trackTime = 0;
    entry.trackLast = -1;
    entry.nextTrackLast = -1;
    entry.trackEnd = Float.MAX_VALUE;
    entry.timeScale = 1;

    entry.alpha = 1;
    entry.mixAlpha = 1;
    entry.mixTime = 0;
    entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
    return entry;
  }
  /**
   * Adds an animation to be played delay seconds after the current or last queued animation.
   *
   * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the
   *     negative delay.
   */
  public TrackEntry addAnimation(int trackIndex, Animation animation, boolean loop, float delay) {
    TrackEntry entry = Pools.obtain(TrackEntry.class);
    entry.animation = animation;
    entry.loop = loop;
    entry.endTime = animation.getDuration();

    TrackEntry last = expandToIndex(trackIndex);
    if (last != null) {
      while (last.next != null) last = last.next;
      last.next = entry;
    } else tracks.set(trackIndex, entry);

    if (delay <= 0) {
      if (last != null) delay += last.endTime - data.getMix(last.animation, animation);
      else delay = 0;
    }
    entry.delay = delay;

    return entry;
  }