Documentation
use psdk_fruit_emapi::{
  chunker::{chunk_print_data, file_transfer_payload_capacity, print_transfer_payload_capacity},
  constants::*,
  crc32::compute,
  payload::EmapiPayload,
  tlv::{Tlv, TlvEntry},
  EmapiCommand, FrameCodec, FrameParser,
};

#[test]
fn test_dart_parity_constants_match_command_md() {
  assert_eq!(FRAME_HEAD, 0x02);
  assert_eq!(FRAME_TAIL, 0x03);
  assert_eq!(RESERVED, 0x00);

  assert_eq!(TYPE_REQUEST, 0x01);
  assert_eq!(TYPE_RESPONSE, 0x02);
  assert_eq!(TYPE_ERROR, 0x03);
  assert_eq!(TYPE_PASSTHROUGH_REQUEST, 0x41);
  assert_eq!(TYPE_PASSTHROUGH_RESPONSE, 0x42);
  assert_eq!(TYPE_PASSTHROUGH_ERROR, 0x43);

  assert_eq!(PARENT_SYSTEM, 0x01);
  assert_eq!(PARENT_PRINTER, 0x02);
  assert_eq!(PARENT_RFID, 0x05);
  assert_eq!(PARENT_WIFI, 0x06);
  assert_eq!(PARENT_REPORT, 0x09);
  assert_eq!(PARENT_FILE, 0x0A);

  assert_eq!(CHILD_DEVICE_INFO, 0x01);
  assert_eq!(CHILD_SLEEP_SHUTDOWN, 0x04);
  assert_eq!(CHILD_SET_SHUTDOWN_TIME, 0x05);
  assert_eq!(CHILD_RFID_UID, 0x01);
  assert_eq!(CHILD_RFID_CARD_INFO, 0x02);
  assert_eq!(CHILD_RFID_PAPER_LENGTH, 0x03);
  assert_eq!(CHILD_RFID_AUTH_FAILURE_HANDLING, 0x04);
  assert_eq!(CHILD_WIFI_CONFIG, 0x01);
  assert_eq!(CHILD_WIFI_CONNECTION_STATE, 0x02);
  assert_eq!(CHILD_WIFI_HOTSPOT_INFO, 0x03);
  assert_eq!(CHILD_WIFI_CONFIG_STATUS, 0x04);
  assert_eq!(CHILD_WIFI_FILE_START, 0x05);
  assert_eq!(CHILD_WIFI_FILE_TRANSFER, 0x06);
  assert_eq!(CHILD_WIFI_FILE_END, 0x07);
  assert_eq!(CHILD_PRINTER_PARAMS, 0x01);
  assert_eq!(CHILD_PRINT_STATUS, 0x04);
  assert_eq!(CHILD_TRANSFER_DATA, 0x06);
  assert_eq!(CHILD_PRINT_SELF_TEST_PAGE, 0x0C);
  assert_eq!(CHILD_REPORT_PRINT_RESULT, 0x01);
  assert_eq!(CHILD_REPORT_PRINTER_STATUS, 0x02);
  assert_eq!(CHILD_REPORT_FLOW_CONTROL, 0x03);
  assert_eq!(CHILD_REPORT_UPGRADE_STATUS, 0x04);
  assert_eq!(CHILD_REPORT_BLUETOOTH_CONNECTION, 0x05);
  assert_eq!(CHILD_FILE_START, 0x01);
  assert_eq!(CHILD_FILE_TRANSFER, 0x02);
  assert_eq!(CHILD_FILE_END, 0x03);
  assert_eq!(CHILD_FILE_UPGRADE, 0x04);
  assert_eq!(TAG_MTU, 0x08);
  assert_eq!(COMMAND_HEADER_LENGTH, 5);
  assert_eq!(FRAME_OVERHEAD_LENGTH, 9);
  assert_eq!(PRINT_TRANSFER_FRAME_OVERHEAD_LENGTH, 14);
  assert_eq!(CRC_SEED, 0x35769521);
}

#[test]
fn test_dart_parity_crc32_vectors() {
  assert_eq!(compute(&[0x01, 0x02, 0x06, 0x00, 0x00]), 0xD1B4ABE2);
  assert_eq!(compute(&[0x01, 0x01, 0x01, 0x00, 0x00]), 0xC64E1289);
  assert_eq!(
    compute(&[0x01, 0x02, 0x06, 0x03, 0x00, 0x10, 0xFF, 0xFE]),
    0x467F3473
  );
}

#[test]
fn test_dart_parity_encodes_known_request_frames() {
  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_REQUEST,
      PARENT_PRINTER,
      CHILD_TRANSFER_DATA,
      []
    ))
    .unwrap(),
    vec![0x02, 0x00, 0x05, 0x00, 0x01, 0x02, 0x06, 0x00, 0x00, 0xE2, 0xAB, 0xB4, 0xD1, 0x03,]
  );

  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_REQUEST,
      PARENT_SYSTEM,
      CHILD_DEVICE_INFO,
      []
    ))
    .unwrap(),
    vec![0x02, 0x00, 0x05, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x89, 0x12, 0x4E, 0xC6, 0x03,]
  );

  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_REQUEST,
      PARENT_PRINTER,
      CHILD_TRANSFER_DATA,
      [0x10, 0xFF, 0xFE]
    ))
    .unwrap(),
    vec![
      0x02, 0x00, 0x08, 0x00, 0x01, 0x02, 0x06, 0x03, 0x00, 0x10, 0xFF, 0xFE, 0x73, 0x34, 0x7F,
      0x46, 0x03,
    ]
  );
}

#[test]
fn test_dart_parity_encodes_representative_full_protocol_frames() {
  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_REQUEST,
      PARENT_SYSTEM,
      CHILD_SLEEP_SHUTDOWN,
      []
    ))
    .unwrap(),
    vec![0x02, 0x00, 0x05, 0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x62, 0xD0, 0x85, 0xC0, 0x03,]
  );

  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_REQUEST,
      PARENT_SYSTEM,
      CHILD_SET_SHUTDOWN_TIME,
      [0x1E, 0x00]
    ))
    .unwrap(),
    vec![
      0x02, 0x00, 0x07, 0x00, 0x01, 0x01, 0x05, 0x02, 0x00, 0x1E, 0x00, 0x6F, 0xD7, 0xE9, 0xD3,
      0x03,
    ]
  );

  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_REQUEST,
      PARENT_RFID,
      CHILD_RFID_AUTH_FAILURE_HANDLING,
      [0x01]
    ))
    .unwrap(),
    vec![0x02, 0x00, 0x06, 0x00, 0x01, 0x05, 0x04, 0x01, 0x00, 0x01, 0x48, 0x96, 0x3B, 0xF2, 0x03,]
  );

  let wifi_payload = Tlv::encode(&[
    TlvEntry::string(0x01, "demo").unwrap(),
    TlvEntry::string(0x02, "secret").unwrap(),
  ])
  .unwrap();
  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_PASSTHROUGH_REQUEST,
      PARENT_WIFI,
      CHILD_WIFI_CONFIG,
      wifi_payload
    ))
    .unwrap(),
    vec![
      0x02, 0x00, 0x17, 0x00, 0x41, 0x06, 0x01, 0x12, 0x00, 0x01, 0x05, 0x00, 0x64, 0x65, 0x6D,
      0x6F, 0x00, 0x02, 0x07, 0x00, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x00, 0xCF, 0x76, 0x60,
      0x27, 0x03,
    ]
  );

  let file_start_payload = Tlv::encode(&[
    TlvEntry::uint16(0x01, 0x0001).unwrap(),
    TlvEntry::uint32(0x02, 9).unwrap(),
  ])
  .unwrap();
  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_REQUEST,
      PARENT_FILE,
      CHILD_FILE_START,
      file_start_payload
    ))
    .unwrap(),
    vec![
      0x02, 0x00, 0x11, 0x00, 0x01, 0x0A, 0x01, 0x0C, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x02,
      0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0xE4, 0x15, 0xEE, 0x03,
    ]
  );

  assert_eq!(
    FrameCodec::encode(&EmapiCommand::new(
      TYPE_RESPONSE,
      PARENT_REPORT,
      CHILD_REPORT_FLOW_CONTROL,
      []
    ))
    .unwrap(),
    vec![0x02, 0x00, 0x05, 0x00, 0x02, 0x09, 0x03, 0x00, 0x00, 0xD8, 0x94, 0xDE, 0x47, 0x03,]
  );
}

#[test]
fn test_dart_parity_decodes_frames_and_rejects_bad_crc() {
  let frame = vec![
    0x02, 0x00, 0x08, 0x00, 0x01, 0x02, 0x06, 0x03, 0x00, 0x10, 0xFF, 0xFE, 0x73, 0x34, 0x7F, 0x46,
    0x03,
  ];
  let command = FrameCodec::decode(&frame).unwrap();
  assert_eq!(command.command_type, TYPE_REQUEST);
  assert_eq!(command.parent, PARENT_PRINTER);
  assert_eq!(command.child, CHILD_TRANSFER_DATA);
  assert_eq!(command.payload, vec![0x10, 0xFF, 0xFE]);

  let mut bad = frame;
  bad[6] ^= 0x01;
  assert!(FrameCodec::decode(&bad).is_err());
}

#[test]
fn test_dart_parity_parser_buffers_split_frames_and_multiple_frames() {
  let first = FrameCodec::encode(&EmapiCommand::new(
    TYPE_RESPONSE,
    PARENT_PRINTER,
    CHILD_TRANSFER_DATA,
    [],
  ))
  .unwrap();
  let second = FrameCodec::encode(&EmapiCommand::new(
    TYPE_RESPONSE,
    PARENT_SYSTEM,
    CHILD_DEVICE_INFO,
    [],
  ))
  .unwrap();

  let mut parser = FrameParser::new();
  let mut first_start = vec![0x99, 0x88];
  first_start.extend_from_slice(&first[..2]);
  assert!(parser.add(&first_start).unwrap().is_empty());
  assert!(parser.add(&first[2..7]).unwrap().is_empty());

  let parsed = parser.add(&first[7..]).unwrap();
  assert_eq!(parsed.len(), 1);
  assert_eq!(parsed[0].command_type, TYPE_RESPONSE);
  assert_eq!(parsed[0].parent, PARENT_PRINTER);
  assert_eq!(parsed[0].child, CHILD_TRANSFER_DATA);
  assert!(parsed[0].payload.is_empty());

  let mut parser = FrameParser::new();
  let mut combined = first;
  combined.extend_from_slice(&second);
  let parsed = parser.add(&combined).unwrap();
  assert_eq!(parsed.len(), 2);
  assert_eq!(parsed[0].parent, PARENT_PRINTER);
  assert_eq!(parsed[1].parent, PARENT_SYSTEM);
}

#[test]
fn test_dart_parity_tlv_encodes_and_decodes_helper_value_types() {
  let encoded = Tlv::encode(&[
    TlvEntry::uint8(0x01, 0x7F).unwrap(),
    TlvEntry::uint16(TAG_MTU, 0x1234).unwrap(),
    TlvEntry::uint32(0x09, 0x12345678).unwrap(),
    TlvEntry::string(0x02, "Q3").unwrap(),
  ])
  .unwrap();
  assert_eq!(
    encoded,
    vec![
      0x01, 0x01, 0x00, 0x7F, 0x08, 0x02, 0x00, 0x34, 0x12, 0x09, 0x04, 0x00, 0x78, 0x56, 0x34,
      0x12, 0x02, 0x03, 0x00, 0x51, 0x33, 0x00,
    ]
  );

  let tlv = Tlv::decode(&encoded).unwrap();
  assert_eq!(tlv.uint8(0x01).unwrap(), Some(0x7F));
  assert_eq!(tlv.uint16(TAG_MTU).unwrap(), Some(0x1234));
  assert_eq!(tlv.uint32(0x09).unwrap(), Some(0x12345678));
  assert_eq!(tlv.string(0x02).unwrap(), Some("Q3".to_string()));
  assert_eq!(tlv.get(0x02).unwrap().value, vec![0x51, 0x33, 0x00]);

  assert!(Tlv::decode(&[0x01, 0x02]).is_err());
  assert!(Tlv::decode(&[0x01, 0x02, 0x00, 0xAA]).is_err());
}

#[test]
fn test_dart_parity_chunking_and_file_payload_helpers() {
  assert_eq!(print_transfer_payload_capacity(17).unwrap(), 3);
  assert_eq!(
    chunk_print_data(&[1, 2, 3, 4], 17).unwrap(),
    vec![vec![1, 2, 3], vec![4]]
  );
  assert!(chunk_print_data(&[], 17).unwrap().is_empty());
  assert!(chunk_print_data(&[1], 14).is_err());

  assert_eq!(file_transfer_payload_capacity(4096).unwrap(), 4064);
  assert_eq!(file_transfer_payload_capacity(512).unwrap(), 480);
  assert_eq!(file_transfer_payload_capacity(50).unwrap(), 32);
  assert!(file_transfer_payload_capacity(49).is_err());

  assert_eq!(
    EmapiPayload::file_chunk(2, &[1, 2, 3]).unwrap(),
    vec![0x02, 0x00, 0x03, 0x00, 1, 2, 3]
  );
  assert!(EmapiPayload::file_chunk(0, &[]).is_err());
  assert!(EmapiPayload::file_chunk(0x10000, &[]).is_err());
}