///|
/// Read-only commands exposed by the intended high-level client.
/// No arbitrary command dispatch is exposed by this enum.
#derive(Eq, @debug.Debug)
pub(all) enum ReadCommand {
  GetDeviceId
  GetChassisStatus
  GetSensorReading(Byte)
}

///|
pub extend ReadCommand with Eq::{equal, not_equal}

///|
pub extend ReadCommand with @debug.Debug::{to_repr}

///|
pub fn ReadCommand::encode(
  self : ReadCommand,
  sequence : Int32,
) -> Bytes raise ProtocolError {
  match self {
    GetDeviceId => encode_request(6, 1, sequence, b"")
    GetChassisStatus => encode_request(0, 1, sequence, b"")
    GetSensorReading(sensor) =>
      encode_request(4, 0x2d, sequence, Bytes::from_array([sensor]))
  }
}

///|
pub fn ReadCommand::decode(
  self : ReadCommand,
  data : Bytes,
  sequence : Int32,
) -> Response raise ProtocolError {
  match self {
    GetDeviceId => decode_response(data, 6, 1, sequence)
    GetChassisStatus => decode_response(data, 0, 1, sequence)
    GetSensorReading(_) => decode_response(data, 4, 0x2d, sequence)
  }
}

///|
#derive(Eq, @debug.Debug)
pub struct ChassisStatus {
  power_on : Bool
  power_overload : Bool
  power_interlock : Bool
  power_fault : Bool
  power_control_fault : Bool
  restore_policy : Int32
  last_power_event : Int32
  misc_status : Int32
  front_panel_buttons : Int32?
}

///|
pub extend ChassisStatus with Eq::{equal, not_equal}

///|
pub extend ChassisStatus with @debug.Debug::{to_repr}

///|
pub fn Response::chassis_status(
  self : Response,
) -> ChassisStatus raise ProtocolError {
  guard self.completion_code == 0 else {
    raise invalid("Get Chassis Status returned a failure completion code")
  }
  let d = self.data
  guard d.length() == 3 || d.length() == 4 else {
    raise invalid("Get Chassis Status payload must contain 3 or 4 bytes")
  }
  let flags = d[0].to_int()
  {
    power_on: (flags & 1) != 0,
    power_overload: (flags & 2) != 0,
    power_interlock: (flags & 4) != 0,
    power_fault: (flags & 8) != 0,
    power_control_fault: (flags & 16) != 0,
    restore_policy: (flags >> 5) & 3,
    last_power_event: d[1].to_int(),
    misc_status: d[2].to_int(),
    front_panel_buttons: if d.length() == 4 {
      Some(d[3].to_int())
    } else {
      None
    },
  }
}

///|
/// Raw reading only. Converting it to physical units requires an SDR record.
#derive(Eq, @debug.Debug)
pub struct SensorReading {
  raw_reading : Int32
  reading_available : Bool
  scanning_enabled : Bool
  events_enabled : Bool
  status_bits : Bytes
}

///|
pub extend SensorReading with Eq::{equal, not_equal}

///|
pub extend SensorReading with @debug.Debug::{to_repr}

///|
pub fn Response::sensor_reading(
  self : Response,
) -> SensorReading raise ProtocolError {
  guard self.completion_code == 0 else {
    raise invalid("Get Sensor Reading returned a failure completion code")
  }
  let d = self.data
  guard d.length() >= 2 && d.length() <= 4 else {
    raise invalid("Get Sensor Reading payload must contain 2..4 bytes")
  }
  let flags = d[1].to_int()
  {
    raw_reading: d[0].to_int(),
    reading_available: (flags & 0x20) == 0,
    scanning_enabled: (flags & 0x40) != 0,
    events_enabled: (flags & 0x80) != 0,
    status_bits: slice(d, 2, d.length()),
  }
}