mod tcp_rtl;
mod rusb_rtl;
mod adsb;

use std::io::{Error, ErrorKind, Result};
use clap::Parser;
use adsb_lib::run;
use crate::adsb::ADSBFrame;
use crate::rusb_rtl::RusbRtl;
use crate::tcp_rtl::TcpRtl;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct ReceiverArgs {
  #[arg(long)]
  sim: bool,
  #[arg(long, default_value = "127.0.0.1:9999", requires = "sim")]
  addr: String,
  #[arg(long)]
  decode: Option<String>,
}

fn main() -> Result<()> {
  let args = ReceiverArgs::parse();

  if let Some(mut hexString) = args.decode {
    if let Some(stripped) = hexString.strip_prefix("0x") {
      hexString = stripped.to_string();
    }
    let buf = hex_to_bytes(&hexString)?;
    let frame = ADSBFrame::decode(&buf)?;

    println!("{}", frame);
    return Ok(());
  }

  if args.sim {
    println!("Starting in SIMULATION mode, connecting to {}", args.addr);
    let mut device = TcpRtl::connect(&args.addr)?;
    run(&mut device)
  } else {
    println!("Starting in REAL RTL‑SDR mode");
    let mut device = RusbRtl::open()?;
    run(&mut device)
  }
}

fn hex_to_bytes(s: &str) -> Result<Vec<u8>> {
  let bytes = s.as_bytes();
  if bytes.len() % 2 != 0 {
    return Err(Error::new(
      ErrorKind::InvalidInput,
      format!("hex string must have even length, got {}", bytes.len())));
  }

  let mut out = Vec::with_capacity(bytes.len() / 2);
  for chunk in bytes.chunks(2) {
    let hi = match hex_val(chunk[0]) {
      Some(hi) => hi,
      None => return Err(Error::new(
        ErrorKind::InvalidInput,
        format!("invalid hex char '{}'", chunk[0] as char)
      ))
    };
    let lo = match hex_val(chunk[1]) {
      Some(lo) => lo,
      None => return Err(Error::new(
        ErrorKind::InvalidInput,
        format!("invalid hex char '{}'", chunk[1] as char)
      ))
    };
    out.push((hi << 4) | lo);
  }
  Ok(out)
}

fn hex_val(b: u8) -> Option<u8> {
  match b {
    b'0'..=b'9' => Some(b - b'0'),
    b'a'..=b'f' => Some(b - b'a' + 10),
    b'A'..=b'F' => Some(b - b'A' + 10),
    _ => None,
  }
}