Documentation
use crate::constants::{TYPE_ERROR, TYPE_PASSTHROUGH_ERROR, TYPE_RESPONSE};
use crate::error::{EmapiError, Result};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmapiCommand {
  pub command_type: u8,
  pub parent: u8,
  pub child: u8,
  pub payload: Vec<u8>,
}

impl EmapiCommand {
  pub fn new(command_type: u8, parent: u8, child: u8, payload: impl Into<Vec<u8>>) -> Self {
    Self {
      command_type,
      parent,
      child,
      payload: payload.into(),
    }
  }

  pub fn try_new(
    command_type: u16,
    parent: u16,
    child: u16,
    payload: impl Into<Vec<u8>>,
  ) -> Result<Self> {
    Ok(Self::new(
      check_byte(command_type, "type")?,
      check_byte(parent, "parent")?,
      check_byte(child, "child")?,
      payload,
    ))
  }

  pub fn is_ack_for(&self, parent: u8, child: u8) -> bool {
    self.is_response_for(TYPE_RESPONSE, parent, child) && self.payload.is_empty()
  }

  pub fn is_response_for(&self, command_type: u8, parent: u8, child: u8) -> bool {
    self.command_type == command_type && self.parent == parent && self.child == child
  }

  pub fn is_error(&self) -> bool {
    self.command_type == TYPE_ERROR || self.command_type == TYPE_PASSTHROUGH_ERROR
  }

  pub fn is_protocol_error(&self) -> bool {
    self.command_type == TYPE_ERROR
  }

  pub fn is_passthrough_error(&self) -> bool {
    self.command_type == TYPE_PASSTHROUGH_ERROR
  }
}

fn check_byte(value: u16, name: &'static str) -> Result<u8> {
  if value > 0xFF {
    return Err(EmapiError::Range {
      name,
      min: 0,
      max: 0xFF,
      value: u64::from(value),
    });
  }
  Ok(value as u8)
}