// ISO 15765-2 (ISO-TP) reassembly and segmentation for classic CAN.
// Flow-control frames are recognised but this offline toolkit does not simulate
// a live transport; segmentation emits the data frames only.

pub(all) suberror IsotpError {
  EmptyFrame
  EmptyPayload
  InvalidFrameSize(Int)
  InvalidLength(Int)
  TooLarge(Int)
  UnexpectedFirstFrame
  UnexpectedConsecutiveFrame
  UnexpectedSingleFrame
  BadSequence(Int, Int)
  Incomplete(Int, Int)
}

pub(all) struct IsotpReassembler {
  max_len : Int
  mut expected : Int
  mut payload : Array[Int]
  mut next_seq : Int
  mut active : Bool
  mut flow_control : Int
}

pub fn IsotpReassembler::new(max_len? : Int = 4095) -> IsotpReassembler {
  {
    max_len,
    expected: 0,
    payload: Array::new(),
    next_seq: 0,
    active: false,
    flow_control: 0,
  }
}

pub fn IsotpReassembler::is_active(self : IsotpReassembler) -> Bool {
  self.active
}

pub fn IsotpReassembler::flow_control_count(self : IsotpReassembler) -> Int {
  self.flow_control
}

fn take_payload(self : IsotpReassembler) -> Array[Int] {
  let out = self.payload
  self.payload = Array::new()
  self.expected = 0
  self.next_seq = 0
  self.active = false
  out
}

pub fn IsotpReassembler::push(
  self : IsotpReassembler,
  frame : Array[Int],
) -> Array[Int]? raise IsotpError {
  if frame.length() == 0 {
    raise EmptyFrame
  }
  let kind = (frame[0] >> 4) & 0x0f
  if kind == 0 {
    // Single frame.
    if self.active {
      raise UnexpectedSingleFrame
    }
    let mut len = frame[0] & 0x0f
    let mut offset = 1
    if len == 0 {
      if frame.length() < 2 {
        raise InvalidLength(0)
      }
      len = frame[1]
      offset = 2
    }
    if len < 0 || len > self.max_len {
      raise TooLarge(len)
    }
    if frame.length() < offset + len {
      raise Incomplete(frame.length() - offset, len)
    }
    let out : Array[Int] = Array::new()
    for i in 0.. self.max_len {
      raise TooLarge(len)
    }
    self.expected = len
    self.payload = Array::new()
    self.next_seq = 1
    self.active = true
    for i in 2.. Unit raise IsotpError {
  if self.active {
    let got = self.payload.length()
    let want = self.expected
    ignore(self.take_payload())
    raise Incomplete(got, want)
  }
}

pub fn segment(
  payload : Array[Int],
  frame_size? : Int = 8,
) -> Array[Array[Int]] raise IsotpError {
  if frame_size < 2 || frame_size > 64 {
    raise InvalidFrameSize(frame_size)
  }
  if payload.length() == 0 {
    raise EmptyPayload
  }
  if payload.length() > 4095 {
    raise TooLarge(payload.length())
  }
  let frames : Array[Array[Int]] = Array::new()
  if payload.length() <= frame_size - 1 {
    let frame : Array[Int] = Array::new()
    frame.push(payload.length())
    for b in payload {
      frame.push(b)
    }
    while frame.length() < frame_size {
      frame.push(0)
    }
    frames.push(frame)
    return frames
  }
  let first : Array[Int] = Array::new()
  first.push(0x10 | ((payload.length() >> 8) & 0x0f))
  first.push(payload.length() & 0xff)
  let mut pos = 0
  while pos < payload.length() && first.length() < frame_size {
    first.push(payload[pos])
    pos = pos + 1
  }
  while first.length() < frame_size {
    first.push(0)
  }
  frames.push(first)
  let mut seq = 1
  while pos < payload.length() {
    let frame : Array[Int] = Array::new()
    frame.push(0x20 | seq)
    while pos < payload.length() && frame.length() < frame_size {
      frame.push(payload[pos])
      pos = pos + 1
    }
    while frame.length() < frame_size {
      frame.push(0)
    }
    frames.push(frame)
    seq = seq + 1
    if seq > 15 {
      seq = 1
    }
  }
  frames
}