///|
pub(all) enum DbcErrorCode {
  EmptyInput
  InputLimitExceeded
  LineLimitExceeded
  InvalidSyntax
  InvalidInteger
  InvalidNumber
  InvalidIdentifier
  InvalidMessage
  InvalidSignal
  InvalidValueTable
  DuplicateMessageId
  DuplicateMessageName
  DuplicateSignalName
  UnknownMessage
  UnknownSignal
  InvalidBitRange
  InvalidDataLength
  ValueOutOfRange
  OverlappingSignal
  UnsupportedConstruct
} derive(Eq, Debug)

///|
pub(all) struct SourceLocation {
  line_value : Int
  column_value : Int
} derive(Eq, Debug)

///|
pub struct DbcError {
  code_value : String
  message_value : String
  location_value : SourceLocation
  context_value : String
} derive(Eq, Debug)

///|
pub fn DbcErrorCode::code(self : DbcErrorCode) -> String {
  match self {
    EmptyInput => "input.empty"
    InputLimitExceeded => "input.limit_exceeded"
    LineLimitExceeded => "input.line_limit_exceeded"
    InvalidSyntax => "parse.invalid_syntax"
    InvalidInteger => "parse.invalid_integer"
    InvalidNumber => "parse.invalid_number"
    InvalidIdentifier => "parse.invalid_identifier"
    InvalidMessage => "parse.invalid_message"
    InvalidSignal => "parse.invalid_signal"
    InvalidValueTable => "parse.invalid_value_table"
    DuplicateMessageId => "database.duplicate_message_id"
    DuplicateMessageName => "database.duplicate_message_name"
    DuplicateSignalName => "message.duplicate_signal_name"
    UnknownMessage => "database.unknown_message"
    UnknownSignal => "message.unknown_signal"
    InvalidBitRange => "signal.invalid_bit_range"
    InvalidDataLength => "frame.invalid_data_length"
    ValueOutOfRange => "signal.value_out_of_range"
    OverlappingSignal => "signal.overlap"
    UnsupportedConstruct => "parse.unsupported_construct"
  }
}

///|
pub fn SourceLocation::new(line : Int, column : Int) -> SourceLocation {
  { line_value: line, column_value: column }
}

///|
pub fn SourceLocation::line(self : SourceLocation) -> Int {
  self.line_value
}

///|
pub fn SourceLocation::column(self : SourceLocation) -> Int {
  self.column_value
}

///|
pub fn DbcError::new(
  code : DbcErrorCode,
  message : String,
  line : Int,
  column : Int,
  context? : String = "",
) -> DbcError {
  {
    code_value: code.code(),
    message_value: message,
    location_value: SourceLocation::new(line, column),
    context_value: context,
  }
}

///|
pub fn DbcError::code(self : DbcError) -> String {
  self.code_value
}

///|
pub fn DbcError::message(self : DbcError) -> String {
  self.message_value
}

///|
pub fn DbcError::location(self : DbcError) -> SourceLocation {
  self.location_value
}

///|
pub fn DbcError::context(self : DbcError) -> String {
  self.context_value
}

///|
pub fn DbcError::format(self : DbcError) -> String {
  let position = "\{self.location_value.line()}:\{self.location_value.column()}"
  if self.context_value == "" {
    "\{self.code_value} at \{position}: \{self.message_value}"
  } else {
    "\{self.code_value} at \{position}: \{self.message_value} [\{self.context_value}]"
  }
}