///|
/// Deterministic frame clock for export and offline previews.
pub struct FrameSchedule2 {
  start : Double
  end : Double
  frame_rate : Double
  first_frame : Int
} derive(Debug)

///|
pub fn frame_schedule2(
  start : Double,
  end : Double,
  frame_rate : Double,
  first_frame? : Int = 0,
) -> FrameSchedule2 {
  { start, end: end.max(start), frame_rate: frame_rate.max(0.001), first_frame }
}

///|
pub fn FrameSchedule2::start(self : FrameSchedule2) -> Double {
  self.start
}

///|
pub fn FrameSchedule2::end(self : FrameSchedule2) -> Double {
  self.end
}

///|
pub fn FrameSchedule2::frame_rate(self : FrameSchedule2) -> Double {
  self.frame_rate
}

///|
pub fn FrameSchedule2::duration(self : FrameSchedule2) -> Double {
  self.end - self.start
}

///|
pub fn FrameSchedule2::frame_duration(self : FrameSchedule2) -> Double {
  1.0 / self.frame_rate
}

///|
pub fn FrameSchedule2::frame_count(self : FrameSchedule2) -> Int {
  @math.floor(self.duration() * self.frame_rate + 0.000001).to_int() + 1
}

///|
pub fn FrameSchedule2::time_at(self : FrameSchedule2, index : Int) -> Double {
  self.start + index.to_double() * self.frame_duration()
}

///|
pub fn FrameSchedule2::index_at(self : FrameSchedule2, time : Double) -> Int {
  @math.floor((time - self.start) * self.frame_rate + 0.5).to_int() +
  self.first_frame
}

///|
pub fn FrameSchedule2::contains(self : FrameSchedule2, time : Double) -> Bool {
  time >= self.start && time <= self.end
}

///|
pub fn FrameSchedule2::timestamps(self : FrameSchedule2) -> Array[Double] {
  let values : Array[Double] = []
  for index in 0.. Int {
  self.index
}

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

///|
pub fn FrameStamp2::duration(self : FrameStamp2) -> Double {
  self.duration
}

///|
pub fn FrameSchedule2::stamp(self : FrameSchedule2, index : Int) -> FrameStamp2 {
  {
    index: self.first_frame + index,
    time: self.time_at(index),
    duration: self.frame_duration(),
  }
}

///|
pub enum FrameChannel2 {
  ScalarChannel
  Point2Channel
  Point3Channel
  ColorChannel
  Transform3Channel
} derive(Debug)

///|
pub fn frame_channels2() -> Array[FrameChannel2] {
  [ScalarChannel, Point2Channel, Point3Channel, ColorChannel, Transform3Channel]
}

///|
pub struct FramePacket2 {
  stamp : FrameStamp2
  scalar_values : Array[Double]
  point_values : Array[Point2]
  point3_values : Array[Point3]
  color_values : Array[Rgba]
  transform_values : Array[Transform3D]
} derive(Debug)

///|
pub fn FramePacket2::stamp(self : FramePacket2) -> FrameStamp2 {
  self.stamp
}

///|
pub fn FramePacket2::scalar_values(self : FramePacket2) -> Array[Double] {
  self.scalar_values.copy()
}

///|
pub fn FramePacket2::point_values(self : FramePacket2) -> Array[Point2] {
  self.point_values.copy()
}

///|
pub fn FramePacket2::point3_values(self : FramePacket2) -> Array[Point3] {
  self.point3_values.copy()
}

///|
pub fn FramePacket2::color_values(self : FramePacket2) -> Array[Rgba] {
  self.color_values.copy()
}

///|
pub fn FramePacket2::transform_values(
  self : FramePacket2,
) -> Array[Transform3D] {
  self.transform_values.copy()
}

///|
fn empty_frame_packet(stamp : FrameStamp2) -> FramePacket2 {
  {
    stamp,
    scalar_values: [],
    point_values: [],
    point3_values: [],
    color_values: [],
    transform_values: [],
  }
}

///|
pub fn FramePacket2::with_scalar(
  self : FramePacket2,
  value : Double,
) -> FramePacket2 {
  let values = self.scalar_values.copy()
  values.push(value)
  {
    stamp: self.stamp,
    scalar_values: values,
    point_values: self.point_values,
    point3_values: self.point3_values,
    color_values: self.color_values,
    transform_values: self.transform_values,
  }
}

///|
pub fn FramePacket2::with_point(
  self : FramePacket2,
  value : Point2,
) -> FramePacket2 {
  let values = self.point_values.copy()
  values.push(value)
  {
    stamp: self.stamp,
    scalar_values: self.scalar_values,
    point_values: values,
    point3_values: self.point3_values,
    color_values: self.color_values,
    transform_values: self.transform_values,
  }
}

///|
pub fn FramePacket2::with_point3(
  self : FramePacket2,
  value : Point3,
) -> FramePacket2 {
  let values = self.point3_values.copy()
  values.push(value)
  {
    stamp: self.stamp,
    scalar_values: self.scalar_values,
    point_values: self.point_values,
    point3_values: values,
    color_values: self.color_values,
    transform_values: self.transform_values,
  }
}

///|
pub fn FramePacket2::with_color(
  self : FramePacket2,
  value : Rgba,
) -> FramePacket2 {
  let values = self.color_values.copy()
  values.push(value)
  {
    stamp: self.stamp,
    scalar_values: self.scalar_values,
    point_values: self.point_values,
    point3_values: self.point3_values,
    color_values: values,
    transform_values: self.transform_values,
  }
}

///|
pub fn FramePacket2::with_transform(
  self : FramePacket2,
  value : Transform3D,
) -> FramePacket2 {
  let values = self.transform_values.copy()
  values.push(value)
  {
    stamp: self.stamp,
    scalar_values: self.scalar_values,
    point_values: self.point_values,
    point3_values: self.point3_values,
    color_values: self.color_values,
    transform_values: values,
  }
}

///|
pub struct FrameTable2 {
  schedule : FrameSchedule2
  packets : Array[FramePacket2]
} derive(Debug)

///|
pub fn FrameTable2::empty(schedule : FrameSchedule2) -> FrameTable2 {
  { schedule, packets: [] }
}

///|
pub fn FrameTable2::schedule(self : FrameTable2) -> FrameSchedule2 {
  self.schedule
}

///|
pub fn FrameTable2::packets(self : FrameTable2) -> Array[FramePacket2] {
  self.packets.copy()
}

///|
pub fn FrameTable2::length(self : FrameTable2) -> Int {
  self.packets.length()
}

///|
pub fn FrameTable2::packet(self : FrameTable2, index : Int) -> FramePacket2 {
  self.packets[index]
}

///|
pub fn FrameTable2::from_scalar(
  schedule : FrameSchedule2,
  track : ScalarTrack,
) -> FrameTable2 {
  let packets : Array[FramePacket2] = []
  for index in 0.. FrameTable2 {
  let packets : Array[FramePacket2] = []
  for index in 0.. FrameTable2 {
  let packets : Array[FramePacket2] = []
  for index in 0.. FrameTable2 {
  let packets : Array[FramePacket2] = []
  for index in 0.. FrameTable2 {
  let packets : Array[FramePacket2] = []
  for index in 0.. Rgba,
) -> FrameTable2 {
  let packets : Array[FramePacket2] = []
  for packet in self.packets {
    let colors : Array[Rgba] = []
    for color in packet.color_values {
      colors.push(mapper(color))
    }
    packets.push({
      stamp: packet.stamp,
      scalar_values: packet.scalar_values,
      point_values: packet.point_values,
      point3_values: packet.point3_values,
      color_values: colors,
      transform_values: packet.transform_values,
    })
  }
  { schedule: self.schedule, packets }
}

///|
pub struct FrameDropReport2 {
  expected : Int
  actual : Int
  missing : Int
  duplicate : Int
  maximum_gap : Int
} derive(Debug)

///|
pub fn FrameDropReport2::expected(self : FrameDropReport2) -> Int {
  self.expected
}

///|
pub fn FrameDropReport2::actual(self : FrameDropReport2) -> Int {
  self.actual
}

///|
pub fn FrameDropReport2::missing(self : FrameDropReport2) -> Int {
  self.missing
}

///|
pub fn FrameDropReport2::duplicate(self : FrameDropReport2) -> Int {
  self.duplicate
}

///|
pub fn FrameDropReport2::maximum_gap(self : FrameDropReport2) -> Int {
  self.maximum_gap
}

///|
pub fn inspect_frame_indices(
  schedule : FrameSchedule2,
  indices : Array[Int],
) -> FrameDropReport2 {
  let expected = schedule.frame_count()
  if indices.length() == 0 {
    return {
      expected,
      actual: 0,
      missing: expected,
      duplicate: 0,
      maximum_gap: expected,
    }
  }
  let mut duplicate = 0
  let mut maximum_gap = 0
  for index in 1.. (Double, Double) {
  if self.packets.length() == 0 {
    return (self.schedule.start, self.schedule.end)
  }
  (
    self.packets[0].stamp.time,
    self.packets[self.packets.length() - 1].stamp.time,
  )
}