///|
/// PROFIdrive record index for parameter access base.
pub let record_index_base : Int = 0xB02E

///|
/// PROFIdrive profile identification.
pub let profile_id : Int = 0x3A00

///|
/// Parameter request ID: Read.
pub let param_request_read : Byte = 1

///|
/// Parameter request ID: Write.
pub let param_request_write : Byte = 2

///|
/// Standard PROFIdrive parameters.
pub let pnu_control_word_1 : Int = 967

///|
pub let pnu_status_word_1 : Int = 968

///|
pub let pnu_speed_setpoint : Int = 1

///|
pub let pnu_speed_actual : Int = 2

///|
pub let pnu_station_name : Int = 924

///|
pub let pnu_vendor_id : Int = 964

///|
pub let pnu_device_id : Int = 965

///|
fn push_u16_be(output : Array[Byte], value : Int) -> Unit {
  output.push(((value >> 8) & 0xFF).to_byte())
  output.push((value & 0xFF).to_byte())
}

///|
fn read_u16_be(data : Bytes, offset : Int) -> Int {
  (data[offset].to_int() << 8) + data[offset + 1].to_int()
}

///|
/// PROFIdrive parameter request (single parameter).
pub(all) struct ParamRequest {
  request_id : Byte
  axis : Int
  pnu : Int
  subindex : Int
  num_elements : Int
} derive(Eq, Debug)

///|
/// Encode a PROFIdrive parameter request block.
pub fn encode_param_request(req : ParamRequest) -> Bytes {
  let output : Array[Byte] = []
  output.push(req.request_id)
  output.push(req.axis.to_byte())
  push_u16_be(output, req.pnu)
  push_u16_be(output, req.subindex)
  push_u16_be(output, req.num_elements)
  Bytes::from_array(output)
}

///|
/// PROFIdrive parameter response (single parameter).
pub(all) struct ParamResponse {
  format : Byte
  num_values : Int
  values : Bytes
} derive(Eq, Debug)

///|
/// Parse a PROFIdrive parameter response.
pub fn parse_param_response(
  data : Bytes,
  offset : Int,
) -> ParamResponse raise @frame.FrameError {
  guard data.length() >= offset + 4 else {
    raise @frame.FrameError::InvalidMacLength(data.length())
  }
  let format = data[offset]
  let num_values = read_u16_be(data, offset + 1)
  let value_start = offset + 4
  let value_bytes : Array[Byte] = []
  for i = value_start; i < data.length(); i = i + 1 {
    value_bytes.push(data[i])
  }
  ParamResponse::{ format, num_values, values: Bytes::from_array(value_bytes) }
}

///|
pub fn pnu_label(pnu : Int) -> String {
  match pnu {
    967 => "ControlWord1"
    968 => "StatusWord1"
    1 => "SpeedSetpoint"
    2 => "SpeedActual"
    924 => "StationName"
    964 => "VendorID"
    965 => "DeviceID"
    _ => "PNU(" + pnu.to_string() + ")"
  }
}

///|
/// Format parameter request summary.
pub fn format_param_request(req : ParamRequest) -> String {
  let lines : Array[String] = []
  let req_label = if req.request_id == param_request_read {
    "Read"
  } else {
    "Write"
  }
  lines.push("request=" + req_label)
  lines.push("axis=" + req.axis.to_string())
  lines.push("pnu=" + pnu_label(req.pnu))
  lines.push("subindex=" + req.subindex.to_string())
  lines.push("num_elements=" + req.num_elements.to_string())
  lines.join("\n")
}