///|
/// A CAN acceptance filter.
pub struct Filter {
  id : UInt
  mask : UInt
  extended : Bool?
}

///|
/// Match an exact identifier.
pub fn exact_filter(id : UInt, extended? : Bool = false) -> Filter {
  { id, mask: 0x1FFFFFFF, extended: Some(extended) }
}

///|
/// Match identifier bits selected by a mask.
pub fn mask_filter(id : UInt, mask : UInt, extended? : Bool? = None) -> Filter {
  { id, mask, extended }
}

///|
/// Return whether a frame passes this filter.
pub fn Filter::matches(self : Filter, frame : Frame) -> Bool {
  let kind_matches = match self.extended {
    None => true
    Some(wanted) => wanted == frame.is_extended()
  }
  kind_matches && (frame.id() & self.mask) == (self.id & self.mask)
}

///|
/// A group of filters evaluated in insertion order.
pub struct FilterBank {
  filters : Array[Filter]
}

///|
/// Create an empty filter bank.
pub fn new_filter_bank() -> FilterBank {
  { filters: [] }
}

///|
/// Add a filter to the bank.
pub fn FilterBank::add(self : FilterBank, filter : Filter) -> Unit {
  self.filters.push(filter)
}

///|
/// Test whether any filter accepts a frame. An empty bank rejects all frames.
pub fn FilterBank::accepts(self : FilterBank, frame : Frame) -> Bool {
  for filter in self.filters {
    if filter.matches(frame) {
      return true
    }
  }
  false
}

///|
/// A deterministic in-memory CAN bus.
pub struct VirtualBus {
  capacity : Int
  queue : Array[Frame]
  mut filters : FilterBank
  mut dropped : Int
}

///|
/// Create a bus. A zero capacity means an unbounded queue.
pub fn new_bus(capacity : Int) -> VirtualBus {
  { capacity, queue: [], filters: new_filter_bank(), dropped: 0 }
}

///|
/// Configure the receiver acceptance filters.
pub fn VirtualBus::filters(self : VirtualBus) -> FilterBank {
  self.filters
}

///|
/// Replace all acceptance filters.
pub fn VirtualBus::set_filters(self : VirtualBus, filters : FilterBank) -> Unit {
  self.filters = filters
}

///|
/// Publish a frame. The queue is ordered by arbitration priority.
pub fn VirtualBus::publish(self : VirtualBus, frame : Frame) -> Bool {
  if !self.filters.accepts(frame) {
    return false
  }
  if self.capacity > 0 && self.queue.length() >= self.capacity {
    self.dropped += 1
    return false
  }
  self.queue.push(frame)
  sort_queue(self.queue)
  true
}

///|
fn sort_queue(queue : Array[Frame]) -> Unit {
  for i in 1.. 0 && queue[j - 1].id() > current.id() {
      queue[j] = queue[j - 1]
      j -= 1
    }
    queue[j] = current
  }
}

///|
/// Receive and remove the highest-priority pending frame.
pub fn VirtualBus::receive(self : VirtualBus) -> Frame? {
  if self.queue.is_empty() {
    None
  } else {
    Some(self.queue.remove(0))
  }
}

///|
/// Number of pending frames.
pub fn VirtualBus::pending(self : VirtualBus) -> Int {
  self.queue.length()
}

///|
/// Number of dropped frames.
pub fn VirtualBus::dropped(self : VirtualBus) -> Int {
  self.dropped
}