///|
/// Atomic long-table version collector. One active and one candidate version, at most 512 sections.
/// Table-specific semantics must be checked with parse_pat/parse_pmt before accept.
pub struct TableCollector {
  priv table_id : Int
  priv mut active_sections : Array[LongSection]
  priv pending : FixedArray[LongSection?]
  priv mut key : (Int, Int, Int)?
  priv mut count : Int
}

///|
pub fn TableCollector::new(table_id : Int) -> TableCollector {
  {
    table_id,
    active_sections: [],
    pending: FixedArray::make(256, None),
    key: None,
    count: 0,
  }
}

///|
pub fn TableCollector::active(self : TableCollector) -> Array[LongSection] {
  self.active_sections.copy()
}

///|
pub fn TableCollector::discard_pending(self : TableCollector) -> Unit {
  for i = 0; i < 256; i = i + 1 {
    self.pending[i] = None
  }
  self.count = 0
  self.key = None
}

///|
/// A new version is activated only after all its numbered sections arrive; next tables are ignored.
pub fn TableCollector::accept(
  self : TableCollector,
  section : LongSection,
) -> Result[Array[LongSection]?, Diagnostic] {
  if section.table_id != self.table_id {
    return Err(fault("table_id", section.offset, None))
  }
  if !section.current {
    return Ok(None)
  }
  if !self.active_sections.is_empty() {
    let a = self.active_sections[0]
    if a.extension == section.extension && a.version == section.version {
      if a.last_number != section.last_number ||
        self.active_sections[section.number].bytes != section.bytes {
        return Err(fault("table_version_conflict", section.offset, None))
      }
      return Ok(None)
    }
  }
  let key = (section.extension, section.version, section.last_number)
  match self.key {
    Some((ext, ver, last)) => {
      if ext == section.extension &&
        ver == section.version &&
        last != section.last_number {
        return Err(fault("table_version_conflict", section.offset, None))
      }
      if self.key != Some(key) {
        self.discard_pending()
      }
    }
    None => ()
  }
  self.key = Some(key)
  match self.pending[section.number] {
    Some(old) => {
      if old.bytes != section.bytes {
        return Err(fault("table_version_conflict", section.offset, None))
      }
      return Ok(None)
    }
    None => {
      self.pending[section.number] = Some(section)
      self.count = self.count + 1
    }
  }
  if self.count != section.last_number + 1 {
    return Ok(None)
  }
  let result = []
  for i = 0; i <= section.last_number; i = i + 1 {
    match self.pending[i] {
      Some(s) => result.push(s)
      None => return Ok(None)
    }
  }
  self.active_sections = result.copy()
  self.discard_pending()
  Ok(Some(result))
}