use std::io::{Error, ErrorKind, Result};
use std::time::Duration;
use rusb::{request_type, Context, DeviceHandle, Direction, Recipient, RequestType, UsbContext};
use adsb_lib::RtlDevice;

// USB identifiers for RTL-SDR
const VENDOR_ID: u16 = 0x0BDA;
const PRODUCT_ID: u16 = 0x2832;
// Bulk-IN endpoint (0x80 | 0x01)
const DATA_ENDPOINT_ADDRESS: u8 = 0x81;
const USB_TRANSFER_TIMEOUT: Duration = Duration::from_secs(1);

/// rusb/libusb implementation of `RtlDevice`
pub struct RusbRtl {
  handle: DeviceHandle<Context>,
}

impl RusbRtl {
  /// Open the USB device, claim interface 0, and return a wrapper
  pub fn open() -> Result<Self> {
    // Create a new libusb context
    let mut ctx = Context::new().map_err(|err| Error::new(ErrorKind::Other, err))?;

    // Find and open the RTL-SDR by VID/PID
    let mut handle = ctx
      .open_device_with_vid_pid(VENDOR_ID, PRODUCT_ID)
      .ok_or_else(|| Error::new(ErrorKind::NotFound, "Device not found"))?;

    // Claim interface 0
    handle.claim_interface(0).map_err(|err| Error::new(ErrorKind::Other, err))?;

    Ok(Self { handle })
  }

  /// Send a CONTROL-OUT request to the device
  ///
  /// # Arguments
  /// * `request` - the bRequest byte identifying the operation
  /// * `data_payload` - a slice of bytes to send in the data stage
  fn control_out(&mut self, request: u8, data: &[u8]) -> rusb::Result<()> {
    // bmRequestType: OUT | VENDOR | DEVICE
    let bm_request_type = request_type(Direction::Out, RequestType::Vendor, Recipient::Device);

    // Perform the control transfer.
    self.handle
      .write_control(
        bm_request_type,
        request,
        0, // wValue
        0, // wIndex
        data,
        Duration::from_secs(1))
      .map(|_bytes_written| ())
  }

  /// Perform a CONTROL_IN request and read back up to `response_length` bytes
  ///
  /// # Arguments
  /// * `request` - the bRequest byte identifying the operation
  /// * `response_length` - the maximum number of bytes to read
  fn control_in(&mut self, request: u8, response_length: usize) -> rusb::Result<Vec<u8>> {
    // bmRequestType: IN | VENDOR | DEVICE
    let bm_request_type = request_type(Direction::In, RequestType::Vendor, Recipient::Device);

    // Allocate a buffer for the incoming data
    let mut buffer = vec![0u8; response_length];

    // Read the control response into the buffer
    let n = self.handle
      .read_control(
        bm_request_type,
        request,
        0, // wValue
        0, // wIndex
        &mut buffer,
        Duration::from_secs(1))?;

    // Truncate to the actual length returned by the device
    buffer.truncate(n);
    Ok(buffer)
  }
}

impl RtlDevice for RusbRtl {
  fn control_send(&mut self, b_request: u8, data: &[u8]) -> Result<()> {
    self.control_out(b_request, data)
      .map_err(|err| Error::new(ErrorKind::Other, err))
  }

  fn control_recv(&mut self, b_request: u8, length: usize) -> Result<Vec<u8>> {
    self.control_in(b_request, length)
      .map_err(|err| Error::new(ErrorKind::Other, err))
  }

  fn read_bulk(&mut self, buffer: &mut [u8]) -> Result<usize> {
    self.handle
      .read_bulk(DATA_ENDPOINT_ADDRESS, buffer, Duration::from_secs(1))
      .map_err(|err| Error::new(ErrorKind::Other, err))
  }
}
