use std::io::{Error, ErrorKind, Read, Result, Write};
use std::net::TcpStream;
use adsb_lib::RtlDevice;

// Tags for framing requests/responses over the TCP socket
const TAG_CTRL_OUT: u8 = 0x10;
const TAG_CTRL_IN: u8 = 0x11;
const TAG_BULK: u8 = 0x20;

/// A TCP-based implementation of `RtlDevice`
pub struct TcpRtl {
  socket: TcpStream,
}

impl TcpRtl {
  /// Connect to a remote RTL-SDR server at the given address
  pub fn connect(addr: &str) -> Result<Self> {
    let socket = TcpStream::connect(addr)?;
    Ok(TcpRtl { socket })
  }

  /// Send a framed message
  ///
  /// 1 byte: tag
  /// 1 byte: bRequest
  /// 2 bytes: payload length (big endian)
  /// N bytes: optional payload data
  fn send_message(&mut self, message_tag: u8, b_request: u8, data: &[u8]) -> Result<()> {
    let payload_length = data.len() as u16;

    // Build the 4-byte header
    let mut header = [0u8; 4];
    header[0] = message_tag;
    header[1] = b_request;
    header[2..4].copy_from_slice(&payload_length.to_be_bytes());

    // Send header + payload
    self.socket.write_all(&header)?;
    if payload_length > 0 {
      self.socket.write_all(&data)?
    }
    Ok(())
  }

  /// Read one status byte. Expect 0 => OK, non-zero => error
  fn receive_status_ok(&mut self) -> Result<()> {
    let mut status_byte = [0u8; 1];
    self.socket.read_exact(&mut status_byte)?;
    if status_byte[0] != 0 {
      Err(Error::new(ErrorKind::Other, "Remote reported error" ))
    } else {
      Ok(())
    }
  }

  /// Read a length-prefixed payload
  ///
  /// 1 byte: tag (ignored)
  /// 2 bytes: length (little-endian)
  /// N bytes: payload data
  fn receive_length_prefixed_payload(&mut self) -> Result<Vec<u8>> {
    // Discard tag
    let mut tag_bytes = [0u8; 1];
    self.socket.read_exact(&mut tag_bytes)?;

    // Read 2-byte little-endian length
    let mut length_bytes = [0u8; 2];
    self.socket.read_exact(&mut length_bytes)?;
    let payload_length = u16::from_le_bytes(length_bytes) as usize;

    // Read exactly payload_length bytes
    let mut buffer = vec![0u8; payload_length];
    self.socket.read_exact(&mut buffer)?;
    Ok(buffer)
  }
}

impl RtlDevice for TcpRtl {
  fn control_send(&mut self, b_request: u8, data: &[u8]) -> Result<()> {
    self.send_message(TAG_CTRL_OUT, b_request, data)?;
    self.receive_status_ok()
  }

  fn control_recv(&mut self, b_request: u8, _length: usize) -> Result<Vec<u8>> {
    self.send_message(TAG_CTRL_IN, b_request, &[])?;
    self.receive_length_prefixed_payload()
  }

  fn read_bulk(&mut self, buffer: &mut [u8]) -> Result<usize> {
    // Number of bytes expected
    let requested_byte_count = buffer.len() as u16;

    // Prepare a 4-byte header slot
    let mut header = [0u8; 4];
    header[0] = TAG_BULK;
    header[1] = 0; // bRequest=0 for bulk
    header[2..4].copy_from_slice(&requested_byte_count.to_le_bytes());

    // Write the 4-byte header from the peer
    let _ = self.socket.write_all(&header)?;

    // Read and check the status byte
    let mut status_byte = [0u8];
    self.socket.read_exact(&mut status_byte)?;
    if status_byte[0] != 0 {
      return Err(Error::new(ErrorKind::Other, "Remote reported error"));
    }

    // Read the 4-byte payload length
    let mut length_bytes = [0u8; 4];
    self.socket.read_exact(&mut length_bytes)?;
    let actual_payload_length = u32::from_le_bytes(length_bytes) as usize;

    // Read the payload
    self.socket.read_exact(&mut buffer[..actual_payload_length])?;
    Ok(actual_payload_length)
  }
}