// UDS (ISO 14229) request builders and response decoding.
// Security-access algorithms are intentionally out of scope; the toolkit builds
// seed/key frames and reports NRCs, it does not implement OEM secrets.

pub(all) enum UdsMessage {
  Positive(Int, Array[Int])
  Negative(Int, Int, String)
  Malformed(String)
}

pub fn uds_service_name(service : Int) -> String {
  if service == 0x10 {
    "DiagnosticSessionControl"
  } else if service == 0x11 {
    "ECUReset"
  } else if service == 0x14 {
    "ClearDiagnosticInformation"
  } else if service == 0x19 {
    "ReadDTCInformation"
  } else if service == 0x22 {
    "ReadDataByIdentifier"
  } else if service == 0x27 {
    "SecurityAccess"
  } else if service == 0x28 {
    "CommunicationControl"
  } else if service == 0x2E {
    "WriteDataByIdentifier"
  } else if service == 0x31 {
    "RoutineControl"
  } else if service == 0x34 {
    "RequestDownload"
  } else if service == 0x36 {
    "TransferData"
  } else if service == 0x37 {
    "RequestTransferExit"
  } else if service == 0x3E {
    "TesterPresent"
  } else if service == 0x85 {
    "ControlDTCSetting"
  } else {
    "Unknown"
  }
}

pub fn nrc_description(nrc : Int) -> String {
  if nrc == 0x10 {
    "generalReject"
  } else if nrc == 0x11 {
    "serviceNotSupported"
  } else if nrc == 0x12 {
    "subFunctionNotSupported"
  } else if nrc == 0x13 {
    "incorrectMessageLengthOrInvalidFormat"
  } else if nrc == 0x14 {
    "responseTooLong"
  } else if nrc == 0x21 {
    "busyRepeatRequest"
  } else if nrc == 0x22 {
    "conditionsNotCorrect"
  } else if nrc == 0x24 {
    "requestSequenceError"
  } else if nrc == 0x25 {
    "noResponseFromSubnetComponent"
  } else if nrc == 0x26 {
    "failurePreventsExecutionOfRequestedAction"
  } else if nrc == 0x31 {
    "requestOutOfRange"
  } else if nrc == 0x33 {
    "securityAccessDenied"
  } else if nrc == 0x34 {
    "authenticationRequired"
  } else if nrc == 0x35 {
    "invalidKey"
  } else if nrc == 0x36 {
    "exceedNumberOfAttempts"
  } else if nrc == 0x37 {
    "requiredTimeDelayNotExpired"
  } else if nrc == 0x70 {
    "uploadDownloadNotAccepted"
  } else if nrc == 0x71 {
    "transferDataSuspended"
  } else if nrc == 0x72 {
    "generalProgrammingFailure"
  } else if nrc == 0x73 {
    "wrongBlockSequenceCounter"
  } else if nrc == 0x78 {
    "requestCorrectlyReceivedResponsePending"
  } else if nrc == 0x7e {
    "subFunctionNotSupportedInActiveSession"
  } else if nrc == 0x7f {
    "serviceNotSupportedInActiveSession"
  } else {
    "unknownNrc"
  }
}

pub fn session_name(session : Int) -> String {
  if session == 0x01 {
    "default"
  } else if session == 0x02 {
    "programming"
  } else if session == 0x03 {
    "extendedDiagnostic"
  } else if session == 0x04 {
    "safetySystemDiagnostic"
  } else {
    "unknownSession"
  }
}

fn u16_bytes(value : Int) -> Array[Int] {
  [((value >> 8) & 0xff), value & 0xff]
}

pub fn request_session_control(
  session : Int,
  suppress_positive? : Bool = false,
) -> Array[Int] {
  [0x10, session | (if suppress_positive { 0x80 } else { 0 })]
}

pub fn request_ecu_reset(reset_type : Int) -> Array[Int] {
  [0x11, reset_type]
}

pub fn request_clear_dtc(group : Int) -> Array[Int] {
  [0x14, (group >> 16) & 0xff, (group >> 8) & 0xff, group & 0xff]
}

pub fn request_read_dtc_by_status(mask : Int) -> Array[Int] {
  [0x19, 0x02, mask]
}

pub fn request_read_dtc_supported() -> Array[Int] {
  [0x19, 0x0a]
}

pub fn request_read_did(did : Int) -> Array[Int] {
  let out : Array[Int] = [0x22]
  let bytes = u16_bytes(did)
  out.push(bytes[0])
  out.push(bytes[1])
  out
}

pub fn request_write_did(did : Int, data : Array[Int]) -> Array[Int] {
  let out = request_read_did(did)
  out[0] = 0x2e
  for b in data {
    out.push(b)
  }
  out
}

pub fn request_security_access(level : Int, key? : Array[Int]) -> Array[Int] {
  let out : Array[Int] = [0x27]
  match key {
    None => out.push(level)
    Some(bytes) => {
      out.push(level + 1)
      for b in bytes {
        out.push(b)
      }
    }
  }
  out
}

pub fn request_tester_present(suppress_positive? : Bool = true) -> Array[Int] {
  [0x3e, if suppress_positive { 0x80 } else { 0x00 }]
}

pub fn request_routine_control(
  control_type : Int,
  routine_id : Int,
  data? : Array[Int],
) -> Array[Int] {
  let out : Array[Int] = [0x31, control_type]
  let bytes = u16_bytes(routine_id)
  out.push(bytes[0])
  out.push(bytes[1])
  match data {
    Some(values) =>
      for b in values {
        out.push(b)
      }
    None => ()
  }
  out
}

pub fn request_download(address : Int, size : Int) -> Array[Int] {
  let out : Array[Int] = [0x34, 0x00, 0x44]
  for shift in [24, 16, 8, 0] {
    out.push((address >> shift) & 0xff)
  }
  for shift in [24, 16, 8, 0] {
    out.push((size >> shift) & 0xff)
  }
  out
}

pub fn request_transfer_data(block_sequence : Int, data : Array[Int]) -> Array[Int] {
  let out : Array[Int] = [0x36, block_sequence & 0xff]
  for b in data {
    out.push(b)
  }
  out
}

pub fn request_transfer_exit() -> Array[Int] {
  [0x37]
}

pub fn parse_uds_message(bytes : Array[Int]) -> UdsMessage {
  if bytes.length() == 0 {
    return Malformed("empty message")
  }
  if bytes[0] == 0x7f {
    if bytes.length() < 3 {
      return Malformed("negative response shorter than 3 bytes")
    }
    Negative(bytes[1], bytes[2], nrc_description(bytes[2]))
  } else if bytes[0] >= 0x40 && bytes[0] <= 0x7e {
    let data : Array[Int] = Array::new()
    for i in 1.. (Int, Array[Int])? {
  if bytes.length() < 3 || bytes[0] != 0x62 {
    return None
  }
  let did = (bytes[1] << 8) | bytes[2]
  let data : Array[Int] = Array::new()
  for i in 3.. Array[Dtc]? {
  if bytes.length() < 3 || bytes[0] != 0x59 {
    return None
  }
  let out : Array[Dtc] = Array::new()
  let mut i = 3
  while i + 3 < bytes.length() {
    let high = bytes[i]
    let middle = bytes[i + 1]
    let failure_type = bytes[i + 2]
    let status = bytes[i + 3]
    if is_present_uds_record(high, middle, failure_type, status) {
      out.push(decode_uds_dtc(high, middle, failure_type, status))
    }
    i = i + 4
  }
  Some(out)
}

pub fn parse_security_seed(bytes : Array[Int]) -> (Int, Array[Int])? {
  if bytes.length() < 2 || bytes[0] != 0x67 {
    return None
  }
  let data : Array[Int] = Array::new()
  for i in 2..