///|
pub(all) struct DbcParseLimits {
  max_input_bytes : Int
  max_lines : Int
  max_messages : Int
  max_signals_per_message : Int
  max_nodes : Int
}

///|
pub fn DbcParseLimits::standard() -> DbcParseLimits {
  {
    max_input_bytes: 4 * 1024 * 1024,
    max_lines: 100000,
    max_messages: 10000,
    max_signals_per_message: 1000,
    max_nodes: 10000,
  }
}

///|
fn parse_error(
  code : DbcErrorCode,
  message : String,
  line : Int,
  context : String,
) -> DbcError {
  DbcError::new(code, message, line, 1, context~)
}

///|
fn parse_version_line(line : String, number : Int) -> Result[String, DbcError] {
  let cursor = TextCursor::new(line, number)
  if !cursor.consume_text("VERSION") {
    return Err(parse_error(InvalidSyntax, "expected VERSION", number, line))
  }
  let version = match cursor.quoted() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.finish() {
    Ok(_) => Ok(version)
    Err(error) => Err(error)
  }
}

///|
fn parse_nodes_line(
  line : String,
  number : Int,
) -> Result[Array[DbcNode], DbcError] {
  let cursor = TextCursor::new(line, number)
  if !cursor.consume_text("BU_") {
    return Err(parse_error(InvalidSyntax, "expected BU_", number, line))
  }
  match cursor.expect_char(58, "colon after BU_") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let nodes : Array[DbcNode] = []
  cursor.skip_spaces()
  while !cursor.at_end() {
    let name = match cursor.identifier() {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    nodes.push(DbcNode::new(name))
    cursor.skip_spaces()
  }
  Ok(nodes)
}

///|
fn parse_message_line(
  line : String,
  number : Int,
) -> Result[DbcMessage, DbcError] {
  let cursor = TextCursor::new(line, number)
  if !cursor.consume_text("BO_") {
    return Err(parse_error(InvalidMessage, "expected BO_", number, line))
  }
  let id = match cursor.integer() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let name = match cursor.identifier() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.expect_char(58, "colon after message name") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let size = match cursor.integer() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let transmitter = match cursor.identifier() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.finish() {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  if id < 0 {
    return Err(
      parse_error(InvalidMessage, "message id cannot be negative", number, line),
    )
  }
  if size < 0 || size > 64 {
    return Err(
      parse_error(
        InvalidMessage,
        "message size must be between 0 and 64 bytes",
        number,
        line,
      ),
    )
  }
  Ok(DbcMessage::new(id, name, size, transmitter, [], source_line=number))
}

///|
fn parse_receivers(cursor : TextCursor) -> Result[Array[String], DbcError] {
  let receivers : Array[String] = []
  cursor.skip_spaces()
  if cursor.at_end() {
    return Ok(receivers)
  }
  while !cursor.at_end() {
    let receiver = match cursor.identifier() {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    receivers.push(receiver)
    cursor.skip_spaces()
    if cursor.at_end() {
      break
    }
    if !cursor.consume_char(44) {
      return Err(
        cursor_error(cursor, InvalidSignal, "expected comma between receivers"),
      )
    }
  }
  Ok(receivers)
}

///|
fn parse_signal_line(
  line : String,
  number : Int,
) -> Result[DbcSignal, DbcError] {
  let cursor = TextCursor::new(line, number)
  if !cursor.consume_text("SG_") {
    return Err(parse_error(InvalidSignal, "expected SG_", number, line))
  }
  let name = match cursor.identifier() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  cursor.skip_spaces()
  if cursor.peek() == 77 || cursor.peek() == 109 {
    return Err(
      cursor_error(
        cursor,
        UnsupportedConstruct,
        "multiplexed signals are not supported by version 0.1.0",
      ),
    )
  }
  match cursor.expect_char(58, "colon after signal name") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let start_bit = match cursor.integer() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.expect_char(124, "vertical bar after signal start bit") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let bit_length = match cursor.integer() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.expect_char(64, "at sign before byte order") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let order_code = match cursor.integer() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let byte_order = match order_code {
    0 => Motorola
    1 => Intel
    _ =>
      return Err(
        cursor_error(cursor, InvalidSignal, "byte order must be 0 or 1"),
      )
  }
  cursor.skip_spaces()
  let value_type = if cursor.consume_char(43) {
    Unsigned
  } else if cursor.consume_char(45) {
    Signed
  } else {
    return Err(
      cursor_error(cursor, InvalidSignal, "signal value type must be + or -"),
    )
  }
  match cursor.expect_char(40, "opening parenthesis before scaling") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let factor = match cursor.number() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.expect_char(44, "comma between factor and offset") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let offset = match cursor.number() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.expect_char(41, "closing parenthesis after scaling") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  match cursor.expect_char(91, "opening bracket before range") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let minimum = match cursor.number() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.expect_char(124, "vertical bar between range bounds") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let maximum = match cursor.number() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match cursor.expect_char(93, "closing bracket after range") {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let unit = match cursor.quoted() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let receivers = match parse_receivers(cursor) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if start_bit < 0 || bit_length <= 0 || bit_length > 64 {
    return Err(
      parse_error(
        InvalidSignal,
        "signal bit layout is outside supported bounds",
        number,
        line,
      ),
    )
  }
  if factor == 0.0 {
    return Err(
      parse_error(InvalidSignal, "signal factor cannot be zero", number, line),
    )
  }
  if minimum > maximum {
    return Err(
      parse_error(InvalidSignal, "signal minimum exceeds maximum", number, line),
    )
  }
  Ok(
    DbcSignal::new(
      name,
      start_bit,
      bit_length,
      byte_order,
      value_type,
      factor,
      offset,
      minimum,
      maximum,
      unit,
      receivers,
      source_line=number,
    ),
  )
}

///|
fn is_ignored_dbc_statement(line : String) -> Bool {
  line == "BS_:" ||
  line == "BS_ :" ||
  line.has_prefix("NS_") ||
  line.has_prefix("NS_DESC_") ||
  line.has_prefix("BA_DEF_") ||
  line.has_prefix("BA_DEF_DEF_") ||
  line.has_prefix("BA_") ||
  line.has_prefix("SIG_VALTYPE_") ||
  line.has_prefix("EV_") ||
  line.has_prefix("ENVVAR_DATA_") ||
  line.has_prefix("SGTYPE_") ||
  line.has_prefix("SIG_TYPE_REF_") ||
  line.has_prefix("BO_TX_BU_") ||
  line.has_prefix("SG_MUL_VAL_") ||
  line.has_prefix("CAT_") ||
  line.has_prefix("FILTER")
}

///|
pub fn parse_dbc(
  text : String,
  limits? : DbcParseLimits = DbcParseLimits::standard(),
) -> Result[DbcDatabase, DbcError] {
  if text.trim().is_empty() {
    return Err(DbcError::new(EmptyInput, "DBC input is empty", 1, 1))
  }
  if limits.max_input_bytes < 0 ||
    limits.max_lines < 0 ||
    limits.max_messages < 0 ||
    limits.max_signals_per_message < 0 ||
    limits.max_nodes < 0 {
    return Err(
      DbcError::new(InputLimitExceeded, "parse limits cannot be negative", 1, 1),
    )
  }
  if text.length() > limits.max_input_bytes {
    return Err(
      DbcError::new(
        InputLimitExceeded,
        "DBC input exceeds configured byte limit",
        1,
        1,
      ),
    )
  }
  let mut version = ""
  let nodes : Array[DbcNode] = []
  let messages : Array[DbcMessage] = []
  let mut current_message = -1
  let mut ignored = 0
  let mut line_number = 0
  for line_view in text.split("\n") {
    line_number = line_number + 1
    if line_number > limits.max_lines {
      return Err(
        DbcError::new(
          LineLimitExceeded,
          "DBC input exceeds configured line limit",
          line_number,
          1,
        ),
      )
    }
    let line = line_view.trim().to_owned()
    if line == "" {
      continue
    }
    if line.has_prefix("VERSION") {
      version = match parse_version_line(line, line_number) {
        Ok(value) => value
        Err(error) => return Err(error)
      }
    } else if line.has_prefix("BU_") {
      let parsed = match parse_nodes_line(line, line_number) {
        Ok(value) => value
        Err(error) => return Err(error)
      }
      if nodes.length() + parsed.length() > limits.max_nodes {
        return Err(
          parse_error(
            InputLimitExceeded,
            "node count exceeds configured limit",
            line_number,
            line,
          ),
        )
      }
      for node in parsed {
        nodes.push(node)
      }
    } else if line.has_prefix("BO_") && !line.has_prefix("BO_TX_BU_") {
      if messages.length() >= limits.max_messages {
        return Err(
          parse_error(
            InputLimitExceeded,
            "message count exceeds configured limit",
            line_number,
            line,
          ),
        )
      }
      let message = match parse_message_line(line, line_number) {
        Ok(value) => value
        Err(error) => return Err(error)
      }
      messages.push(message)
      current_message = messages.length() - 1
    } else if line.has_prefix("SG_") &&
      !line.has_prefix("SGTYPE_") &&
      !line.has_prefix("SG_MUL_VAL_") {
      if current_message < 0 {
        return Err(
          parse_error(
            InvalidSignal,
            "signal appears before any message",
            line_number,
            line,
          ),
        )
      }
      let signal = match parse_signal_line(line, line_number) {
        Ok(value) => value
        Err(error) => return Err(error)
      }
      let message = messages[current_message]
      let signals = message.signals()
      if signals.length() >= limits.max_signals_per_message {
        return Err(
          parse_error(
            InputLimitExceeded,
            "signal count exceeds configured limit",
            line_number,
            line,
          ),
        )
      }
      signals.push(signal)
      messages[current_message] = message.with_signals(signals)
    } else if line.has_prefix("CM_") {
      let handled = match
        parse_comment_annotation(line, line_number, messages) {
        Ok(value) => value
        Err(error) => return Err(error)
      }
      if !handled {
        ignored = ignored + 1
      }
    } else if line.has_prefix("VAL_") {
      match parse_value_annotation(line, line_number, messages) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
    } else if is_ignored_dbc_statement(line) {
      ignored = ignored + 1
    } else {
      return Err(
        parse_error(
          UnsupportedConstruct,
          "unsupported DBC statement",
          line_number,
          line,
        ),
      )
    }
  }
  Ok(
    DbcDatabase::new(version, nodes, messages, ignored_statement_count=ignored),
  )
}