///|
/// Lifecycle state for a serial composition of scalar tweens.
pub(all) enum SequenceState {
  Before
  Active
  Gap
  After
} derive(Eq, Debug)

///|
/// A sample from a TweenSequence, including the active segment index.
pub(all) struct SequenceSample {
  time : Double
  value : Double
  index : Int
  progress : Double
  state : SequenceState
} derive(Debug)

///|
pub fn SequenceSample::time(self : SequenceSample) -> Double {
  self.time
}

///|
pub fn SequenceSample::value(self : SequenceSample) -> Double {
  self.value
}

///|
pub fn SequenceSample::index(self : SequenceSample) -> Int {
  self.index
}

///|
pub fn SequenceSample::progress(self : SequenceSample) -> Double {
  self.progress
}

///|
pub fn SequenceSample::state(self : SequenceSample) -> SequenceState {
  self.state
}

///|
/// A serial composition of Tweens with optional gaps between segments.
pub struct TweenSequence {
  tweens : Array[Tween]
  gap : Double
} derive(Debug)

///|
pub fn TweenSequence::new(
  tweens : Array[Tween],
  gap? : Double = 0.0,
) -> TweenSequence raise MotionError {
  if tweens.length() == 0 {
    raise MotionError::EmptyTrack
  }
  ensure_finite(gap)
  if gap < 0.0 {
    raise MotionError::InvalidDuration(gap)
  }
  { tweens: tweens.copy(), gap }
}

///|
pub fn TweenSequence::tweens(self : TweenSequence) -> Array[Tween] {
  self.tweens.copy()
}

///|
pub fn TweenSequence::length(self : TweenSequence) -> Int {
  self.tweens.length()
}

///|
pub fn TweenSequence::gap(self : TweenSequence) -> Double {
  self.gap
}

///|
pub fn TweenSequence::duration(self : TweenSequence) -> Double {
  let mut total = 0.0
  for index in 0.. Double {
  let mut start = 0.0
  for cursor in 0.. SequenceSample {
  let first = self.tweens[0].sample(0.0)
  { time, value: first.value(), index: 0, progress: 0.0, state: Before }
}

///|
fn TweenSequence::sample_after(
  self : TweenSequence,
  time : Double,
) -> SequenceSample {
  let last_index = self.tweens.length() - 1
  let last = self.tweens[last_index]
  let sample = last.sample(last.total_duration())
  {
    time,
    value: sample.value(),
    index: last_index,
    progress: 1.0,
    state: After,
  }
}

///|
/// Sample the sequence in wall-clock time. A gap holds the previous value.
pub fn TweenSequence::sample(
  self : TweenSequence,
  time : Double,
) -> SequenceSample {
  if time < 0.0 {
    return self.sample_before(time)
  }
  if time >= self.duration() {
    return self.sample_after(time)
  }
  for index in 0.. Double {
  self.sample(time).value()
}

///|
pub fn TweenSequence::sample_many(
  self : TweenSequence,
  count : Int,
) -> Array[SequenceSample] raise MotionError {
  if count < 2 {
    raise MotionError::InvalidSampleCount(count)
  }
  let values : Array[SequenceSample] = []
  let duration = self.duration()
  for index in 0.. SequenceBuilder {
  { tweens: [], gap }
}

///|
pub fn SequenceBuilder::add(self : SequenceBuilder, tween : Tween) -> Unit {
  self.tweens.push(tween)
}

///|
pub fn SequenceBuilder::remove_at(
  self : SequenceBuilder,
  index : Int,
) -> Tween? {
  if index < 0 || index >= self.tweens.length() {
    None
  } else {
    Some(self.tweens.remove(index))
  }
}

///|
pub fn SequenceBuilder::build(
  self : SequenceBuilder,
) -> TweenSequence raise MotionError {
  TweenSequence::new(self.tweens, gap=self.gap)
}