///|
pub struct NodeTraffic {
  node_value : String
  transmitted_messages_value : Int
  received_messages_value : Int
  received_signals_value : Int
} derive(Eq, Debug)

///|
pub struct DbcStatistics {
  node_count_value : Int
  message_count_value : Int
  signal_count_value : Int
  classic_message_count_value : Int
  fd_message_count_value : Int
  intel_signal_count_value : Int
  motorola_signal_count_value : Int
  signed_signal_count_value : Int
  described_value_count_value : Int
  payload_bytes_value : Int
  node_traffic_value : Array[NodeTraffic]
} derive(Eq, Debug)

///|
pub fn NodeTraffic::node(self : NodeTraffic) -> String {
  self.node_value
}

///|
pub fn NodeTraffic::transmitted_messages(self : NodeTraffic) -> Int {
  self.transmitted_messages_value
}

///|
pub fn NodeTraffic::received_messages(self : NodeTraffic) -> Int {
  self.received_messages_value
}

///|
pub fn NodeTraffic::received_signals(self : NodeTraffic) -> Int {
  self.received_signals_value
}

///|
pub fn DbcStatistics::node_count(self : DbcStatistics) -> Int {
  self.node_count_value
}

///|
pub fn DbcStatistics::message_count(self : DbcStatistics) -> Int {
  self.message_count_value
}

///|
pub fn DbcStatistics::signal_count(self : DbcStatistics) -> Int {
  self.signal_count_value
}

///|
pub fn DbcStatistics::classic_message_count(self : DbcStatistics) -> Int {
  self.classic_message_count_value
}

///|
pub fn DbcStatistics::fd_message_count(self : DbcStatistics) -> Int {
  self.fd_message_count_value
}

///|
pub fn DbcStatistics::intel_signal_count(self : DbcStatistics) -> Int {
  self.intel_signal_count_value
}

///|
pub fn DbcStatistics::motorola_signal_count(self : DbcStatistics) -> Int {
  self.motorola_signal_count_value
}

///|
pub fn DbcStatistics::signed_signal_count(self : DbcStatistics) -> Int {
  self.signed_signal_count_value
}

///|
pub fn DbcStatistics::described_value_count(self : DbcStatistics) -> Int {
  self.described_value_count_value
}

///|
pub fn DbcStatistics::payload_bytes(self : DbcStatistics) -> Int {
  self.payload_bytes_value
}

///|
pub fn DbcStatistics::node_traffic(self : DbcStatistics) -> Array[NodeTraffic] {
  self.node_traffic_value.copy()
}

///|
pub fn DbcStatistics::find_node(
  self : DbcStatistics,
  node : String,
) -> NodeTraffic? {
  for traffic in self.node_traffic_value {
    if traffic.node() == node {
      return Some(traffic)
    }
  }
  None
}

///|
fn traffic_for_node(database : DbcDatabase, node : String) -> NodeTraffic {
  let mut transmitted = 0
  let mut received_messages = 0
  let mut received_signals = 0
  for message in database.messages() {
    if message.transmitter() == node {
      transmitted = transmitted + 1
    }
    let mut receives_message = false
    for signal in message.signals() {
      if signal_has_receiver(signal, node) {
        received_signals = received_signals + 1
        receives_message = true
      }
    }
    if receives_message {
      received_messages = received_messages + 1
    }
  }
  {
    node_value: node,
    transmitted_messages_value: transmitted,
    received_messages_value: received_messages,
    received_signals_value: received_signals,
  }
}

///|
pub fn analyze_dbc(database : DbcDatabase) -> DbcStatistics {
  let mut signal_count = 0
  let mut classic_messages = 0
  let mut fd_messages = 0
  let mut intel_signals = 0
  let mut motorola_signals = 0
  let mut signed_signals = 0
  let mut described_values = 0
  let mut payload_bytes = 0
  for message in database.messages() {
    payload_bytes = payload_bytes + message.size()
    if message.size() <= 8 {
      classic_messages = classic_messages + 1
    } else {
      fd_messages = fd_messages + 1
    }
    for signal in message.signals() {
      signal_count = signal_count + 1
      described_values = described_values + signal.values().length()
      match signal.byte_order() {
        Intel => intel_signals = intel_signals + 1
        Motorola => motorola_signals = motorola_signals + 1
      }
      if signal.value_type() == Signed {
        signed_signals = signed_signals + 1
      }
    }
  }
  let traffic : Array[NodeTraffic] = []
  for node in database.nodes() {
    traffic.push(traffic_for_node(database, node.name()))
  }
  {
    node_count_value: database.nodes().length(),
    message_count_value: database.message_count(),
    signal_count_value: signal_count,
    classic_message_count_value: classic_messages,
    fd_message_count_value: fd_messages,
    intel_signal_count_value: intel_signals,
    motorola_signal_count_value: motorola_signals,
    signed_signal_count_value: signed_signals,
    described_value_count_value: described_values,
    payload_bytes_value: payload_bytes,
    node_traffic_value: traffic,
  }
}