///|
pub(all) struct SegmentInfo {
  offset : Int32
  raw_offset : Int32
  end_offset : Int32
  toc : UInt32
  big_endian : Bool
} derive(Eq, Debug)

///|
fn flag(toc : UInt32, bit : UInt32) -> Bool {
  (toc & bit) != 0U
}

///|
fn segment_header(
  bytes : Bytes,
  pos : Int32,
  limits : Limits,
) -> SegmentInfo raise TdmsError {
  demand(pos >= 0 && bytes.length() - pos >= 28, "truncated TDMS lead-in")
  let tag = Reader::new(bytes, pos, pos + 8, false, limits)
  demand(tag.take(4) == b"TDSm", "expected TDSm segment tag")
  let toc = tag.u32()
  if (toc & 0xffffff11U) != 0U {
    raise Unsupported("unknown table-of-contents flags")
  }
  if flag(toc, 128U) {
    raise Unsupported("DAQmx raw data")
  }
  let big = flag(toc, 64U)
  let r = Reader::new(bytes, pos + 8, pos + 28, big, limits)
  let version = r.u32()
  if version != 4713U {
    raise Unsupported("only TDMS 2.0 version 4713 is supported")
  }
  let size = r.uint(8)
  if size == 18446744073709551615UL {
    raise Unsupported("unfinished segment sentinel")
  }
  let raw = r.uint(8)
  demand(
    size <= (bytes.length() - pos - 28).to_uint64(),
    "segment extends beyond input",
  )
  demand(raw <= size, "raw offset exceeds segment")
  demand(flag(toc, 2U) || raw == 0UL, "metadata bytes without metadata flag")
  demand(!flag(toc, 4U) || flag(toc, 2U), "new object list without metadata")
  demand(flag(toc, 8U) || raw == size, "raw bytes without raw-data flag")
  {
    offset: pos,
    raw_offset: pos + 28 + raw.to_int(),
    end_offset: pos + 28 + size.to_int(),
    toc,
    big_endian: big,
  }
}

///|
pub fn inspect(
  bytes : Bytes,
  limits~ : Limits = Limits::default(),
) -> Array[SegmentInfo] raise TdmsError {
  limits.validate()
  if bytes.length() > limits.max_bytes {
    raise Budget("input bytes")
  }
  demand(bytes.length() > 0, "empty TDMS input")
  let result : Array[SegmentInfo] = []
  let mut pos = 0
  while pos < bytes.length() {
    if result.length() >= limits.max_segments {
      raise Budget("segments")
    }
    let s = segment_header(bytes, pos, limits)
    result.push(s)
    pos = s.end_offset
  }
  result
}

///|
pub extend SegmentInfo with Eq::{equal, not_equal}

///|
pub extend SegmentInfo with @debug.Debug::{to_repr}