///|
pub struct DbcBatchInput {
  label_value : String
  text_value : String
} derive(Eq, Debug)

///|
pub struct DbcBatchItem {
  label_value : String
  database_value : DbcDatabase?
  error_value : DbcError?
} derive(Eq, Debug)

///|
pub struct DbcBatchResult {
  items_value : Array[DbcBatchItem]
  accepted_value : Int
  rejected_value : Int
  total_input_bytes_value : Int
  total_messages_value : Int
  total_signals_value : Int
} derive(Eq, Debug)

///|
pub fn DbcBatchInput::new(
  label : String,
  text : String,
) -> Result[DbcBatchInput, DbcError] {
  if label.trim().is_empty() {
    Err(
      DbcError::new(
        InvalidIdentifier,
        "batch input label cannot be empty",
        1,
        1,
      ),
    )
  } else {
    Ok({ label_value: label, text_value: text })
  }
}

///|
pub fn DbcBatchInput::label(self : DbcBatchInput) -> String {
  self.label_value
}

///|
pub fn DbcBatchInput::text(self : DbcBatchInput) -> String {
  self.text_value
}

///|
pub fn DbcBatchItem::label(self : DbcBatchItem) -> String {
  self.label_value
}

///|
pub fn DbcBatchItem::accepted(self : DbcBatchItem) -> Bool {
  self.database_value is Some(_)
}

///|
pub fn DbcBatchItem::database(self : DbcBatchItem) -> DbcDatabase? {
  self.database_value
}

///|
pub fn DbcBatchItem::error(self : DbcBatchItem) -> DbcError? {
  self.error_value
}

///|
pub fn DbcBatchResult::items(self : DbcBatchResult) -> Array[DbcBatchItem] {
  self.items_value.copy()
}

///|
pub fn DbcBatchResult::accepted(self : DbcBatchResult) -> Int {
  self.accepted_value
}

///|
pub fn DbcBatchResult::rejected(self : DbcBatchResult) -> Int {
  self.rejected_value
}

///|
pub fn DbcBatchResult::total_input_bytes(self : DbcBatchResult) -> Int {
  self.total_input_bytes_value
}

///|
pub fn DbcBatchResult::total_messages(self : DbcBatchResult) -> Int {
  self.total_messages_value
}

///|
pub fn DbcBatchResult::total_signals(self : DbcBatchResult) -> Int {
  self.total_signals_value
}

///|
pub fn DbcBatchResult::find(
  self : DbcBatchResult,
  label : String,
) -> DbcBatchItem? {
  for item in self.items_value {
    if item.label() == label {
      return Some(item)
    }
  }
  None
}

///|
fn duplicate_batch_label(inputs : Array[DbcBatchInput], index : Int) -> Bool {
  for previous in 0.. Result[DbcBatchResult, DbcError] {
  if max_items < 0 || max_total_bytes < 0 {
    return Err(
      DbcError::new(InputLimitExceeded, "batch limits cannot be negative", 1, 1),
    )
  }
  if inputs.length() > max_items {
    return Err(
      DbcError::new(
        InputLimitExceeded,
        "batch item count exceeds configured limit",
        1,
        1,
      ),
    )
  }
  let mut total_bytes = 0
  for index, input in inputs {
    if duplicate_batch_label(inputs, index) {
      return Err(
        DbcError::new(
          InvalidIdentifier,
          "batch labels must be unique",
          1,
          1,
          context=input.label(),
        ),
      )
    }
    total_bytes = total_bytes + input.text().length()
    if total_bytes > max_total_bytes {
      return Err(
        DbcError::new(
          InputLimitExceeded,
          "batch bytes exceed configured limit",
          1,
          1,
        ),
      )
    }
  }
  let items : Array[DbcBatchItem] = []
  let mut accepted = 0
  let mut rejected = 0
  let mut total_messages = 0
  let mut total_signals = 0
  for input in inputs {
    match parse_dbc(input.text(), limits~) {
      Ok(database) => {
        accepted = accepted + 1
        let statistics = analyze_dbc(database)
        total_messages = total_messages + statistics.message_count()
        total_signals = total_signals + statistics.signal_count()
        items.push({
          label_value: input.label(),
          database_value: Some(database),
          error_value: None,
        })
      }
      Err(error) => {
        rejected = rejected + 1
        items.push({
          label_value: input.label(),
          database_value: None,
          error_value: Some(error),
        })
      }
    }
  }
  Ok({
    items_value: items,
    accepted_value: accepted,
    rejected_value: rejected,
    total_input_bytes_value: total_bytes,
    total_messages_value: total_messages,
    total_signals_value: total_signals,
  })
}