use std::fmt::Display;
use std::io::{Error, ErrorKind, Result};

#[derive(Debug)]
pub struct ADSBFrame {
  pub raw_frame: String,
  /// Downlink format (DF, 5 bits)
  pub downlink_format: u8,
  /// Transponder capability (CA, 3 bits)
  pub capability: Capability,
  /// Unique aircraft number (ICAO, 24 bits)
  pub icao: String,
  /// Message (ME, 56 bits)
  pub message: ADSBMessage,
  /// Parity/Interrogator ID/Checksum (PI, 24 bits)
  pub parity: u32,
}

impl ADSBFrame {
  /// Parse exactly 14 bytes (112 bits) of raw ADS-B ES data into its fields
  ///
  /// [ DF:5 ][ CA:3 ][ ICAO:24 ][ ME:56 ][ PI:24 ]
  pub fn decode(frame: &[u8]) -> Result<ADSBFrame> {
    if frame.len() != 14 {
      return Err(Error::new(
        ErrorKind::InvalidInput,
        format!("expected 14 bytes, received {}", frame.len())));
    }

    let mut raw_frame = "".to_string();
    for byte in frame {
      raw_frame.push_str(&format!("{:02x}", byte).to_uppercase());
    }

    // Decode the downlink format by discarding the lower 3 bits
    let downlink_format = &frame[0] >> 3;
    if downlink_format != 17 {
      return Err(Error::new(
        ErrorKind::Unsupported,
        format!("downlink format {} is not currently supported", downlink_format)
      ));
    }

    // Decode the capability by masking off everything but the lower 3 bits
    let capability_value = &frame[0] & 0b0000_0111;
    let capability     = Capability::try_from(capability_value)?;

    let icao = Self::decode_icao(&frame[1..=3])?;
    let message = ADSBMessage::decode(&frame[4..=10])?;
    let parity = Self::decode_parity(&frame[11..])?;

    Ok(Self {
      raw_frame,
      downlink_format,
      capability,
      icao,
      message,
      parity
    })
  }

  fn decode_icao(data: &[u8]) -> Result<String> {
    if data.len() != 3 {
      return Err(Error::new(
        ErrorKind::InvalidInput,
        format!("ICAO must be 3 bytes, received {}", data.len())));
    }
    let s = data.iter()
      .map(|b| format!("{:02X}", b))
      .collect::<String>();
    Ok(s)
  }

  fn decode_parity(data: &[u8]) -> Result<u32> {
    if data.len() != 3 {
      return Err(Error::new(
        ErrorKind::InvalidInput,
        format!("parity must be 3 bytes, received {}", data.len())));
    }
    let p = ((data[0] as u32) << 16)
      | ((data[1] as u32) <<  8)
      |  (data[2] as u32);
    Ok(p)
  }
}

impl Display for ADSBFrame {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "Frame: {}\
      \nDF:   {}\
      \nCA:   {:?}\
      \nICAO: {}\
      \nME:   {:?}\
      \nPI:   {}",
           self.raw_frame,
           self.downlink_format,
           &self.capability,
           self.icao,
           &self.message,
           self.parity)
  }
}

/// Transponder Capability (CA) codes from the ADS‑B spec
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Capability {
  /// 0: Level 1 transponder
  Level1,
  /// 1–3: Reserved
  Reserved(u8),
  /// 4: Level 2+ transponder, on‑ground (can set CA=7)
  Level2OnGround,
  /// 5: Level 2+ transponder, airborne (can set CA=7)
  Level2Airborne,
  /// 6: Level 2+ transponder, either on‑ground or airborne (can set CA=7)
  Level2Either,
  /// 7: Downlink Request = 0, or Flight Status = 2,3,4,5
  DownlinkRequestOrFlightStatus,
}

impl TryFrom<u8> for Capability {
  type Error = Error;

  fn try_from(value: u8) -> Result<Self> {
    let capability = match value {
      0 => Capability::Level1,
      1..=3 => Capability::Reserved(value),
      4 => Capability::Level2OnGround,
      5 => Capability::Level2Airborne,
      6 => Capability::Level2Either,
      7 => Capability::DownlinkRequestOrFlightStatus,
      _ => {
        return Err(Error::new(
          ErrorKind::InvalidData,
          format!("invalid CA value: {}", value),
        ))
      }
    };
    Ok(capability)
  }
}

// fn get_bits(data: &[u8], from: usize, len: usize) -> u32 {
//   let mut val = 0;
//   for bit in 0..len {
//     let idx = from + bit;
//     let byte = data[idx / 8];
//     let shift = 7 - (idx % 8);
//     let bit_val = ((byte >> shift) & 0x01) as u32;
//     val = (val << 1) | bit_val;
//   }
//   val
// }

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ADSBMessage {
  AircraftIdentification(AircraftIdentification),
  SurfacePosition(SurfacePosition),
  AirbornePositionBaro(AirbornePositionBaro),
  AirborneVelocities(AirborneVelocities),
  AirbornePositionGNSS(AirbornePositionGNSS),
  Reserved(u8),
  AircraftStatus(AircraftStatus),
  TargetState(TargetState),
  AircraftOperationStatus(AircraftOperationStatus),
}

impl ADSBMessage {
  pub fn decode(data: &[u8]) -> Result<ADSBMessage> {
    if data.len() != 7 {
      return Err(Error::new(
        ErrorKind::InvalidInput,
        format!("ME field must be 7 bytes, received {}", data.len()),
      ));
    }
    // First 5 bits is the type code
    let type_code = data[0] >> 3;
    let message = match type_code {
      1..=4 => ADSBMessage::AircraftIdentification(AircraftIdentification::decode(type_code, data)?),
      5..=8 => ADSBMessage::SurfacePosition(SurfacePosition::decode(data)?),
      9..=18 => ADSBMessage::AirbornePositionBaro(AirbornePositionBaro::decode(data)?),
      19 => ADSBMessage::AirborneVelocities(AirborneVelocities::decode(data)?),
      20..=22 => ADSBMessage::AirbornePositionGNSS(AirbornePositionGNSS::decode(data)?),
      23..=27 => ADSBMessage::Reserved(type_code),
      28 => ADSBMessage::AircraftStatus(AircraftStatus::decode(data)?),
      29 => ADSBMessage::TargetState(TargetState::decode(data)?),
      31 => ADSBMessage::AircraftOperationStatus(AircraftOperationStatus::decode(data)?),
      _ => return Err(Error::new(
        ErrorKind::InvalidData,
        format!("unsupported ADS‑B type_code {}", type_code),
      ))
    };

    Ok(message)
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AircraftIdentification {
  type_code: u8,
  emitter_category: u8,
  wake_vortex_category: WakeVortexCategory,
  callsign: String,
}

impl AircraftIdentification {
  pub fn decode(type_code: u8, data: &[u8]) -> Result<Self> {
    // Byte 0: [ TC(5 bits) | emitter_category (3 bits) ]
    let emitter_category = data[0] & 0x07;

    // 56 bit buffer for message
    let mut bits: u64 = 0;
    for &b in data {
      bits = (bits << 8) | b as u64;
    }

    let mut callsign = String::with_capacity(8);
    for i in 0 .. 8 {
      let shift = 48 - 6 * (i + 1);
      let raw6 = ((bits >> shift) & 0x3F) as u8;
      let ch = match raw6 {
        1 ..= 26 => (b'A' + (raw6 - 1)) as char,
        48 ..= 57 => (b'0' + (raw6 - 48)) as char,
        32 => ' ',
        _ => continue,
      };
      callsign.push(ch);
    }

    // trim any trailing spaces
    let callsign = callsign.trim_end().to_string();

    Ok(Self {
      type_code,
      emitter_category,
      wake_vortex_category: WakeVortexCategory::from_tc_ca(type_code, emitter_category),
      callsign,
    })
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WakeVortexCategory {
  NoInfo,
  SurfaceEmergencyVehicle,
  SurfaceServiceVehicle,
  GroundObstruction,
  Glider,
  LighterThanAir,
  Parachutist,
  Ultralight,
  Reserved,
  UnmannedAerialVehicle,
  SpaceVehicle,
  Light,
  Medium1,
  Medium2,
  HighVortex,
  Heavy,
  HighPerformance,
  Rotorcraft,
  Unknown,
}

impl WakeVortexCategory {
  pub fn from_tc_ca(type_code: u8, emitter_category: u8) -> Self {
    match (type_code, emitter_category) {
      (_, 0) => WakeVortexCategory::NoInfo,
      (2, 1) => WakeVortexCategory::SurfaceEmergencyVehicle,
      (2, 3) => WakeVortexCategory::SurfaceServiceVehicle,
      (2, 4..=7) => WakeVortexCategory::GroundObstruction,
      (3, 1) => WakeVortexCategory::Glider,
      (3, 2) => WakeVortexCategory::LighterThanAir,
      (3, 3) => WakeVortexCategory::Parachutist,
      (3, 4) => WakeVortexCategory::Ultralight,
      (3, 5) => WakeVortexCategory::Reserved,
      (3, 6) => WakeVortexCategory::UnmannedAerialVehicle,
      (3, 7) => WakeVortexCategory::SpaceVehicle,
      (4, 1) => WakeVortexCategory::Light,
      (4, 2) => WakeVortexCategory::Medium1,
      (4, 3) => WakeVortexCategory::Medium2,
      (4, 4) => WakeVortexCategory::HighVortex,
      (4, 5) => WakeVortexCategory::Heavy,
      (4, 6) => WakeVortexCategory::HighPerformance,
      (4, 7) => WakeVortexCategory::Rotorcraft,
      _ => WakeVortexCategory::Unknown,
    }
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SurfacePosition {

}

impl SurfacePosition {
  pub fn decode(data: &[u8]) -> Result<Self> {
    Ok(Self {})
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AirbornePositionBaro {

}

impl AirbornePositionBaro {
  pub fn decode(data: &[u8]) -> Result<Self> {
    Ok(Self {})
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AirborneVelocities {

}

impl AirborneVelocities {
  pub fn decode(data: &[u8]) -> Result<Self> {
    Ok(Self {})
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AirbornePositionGNSS {

}

impl AirbornePositionGNSS {
  pub fn decode(data: &[u8]) -> Result<Self> {
    Ok(Self {})
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AircraftStatus {

}

impl AircraftStatus {
  pub fn decode(data: &[u8]) -> Result<Self> {
    Ok(Self {})
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TargetState {

}

impl TargetState {
  pub fn decode(data: &[u8]) -> Result<Self> {
    Ok(Self {})
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AircraftOperationStatus {

}

impl AircraftOperationStatus {
  pub fn decode(data: &[u8]) -> Result<Self> {
    Ok(Self {})
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_decode_df_17_aircraft_information() {
    let input = [
      0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71,
      0xC3, 0x1C, 0x32, 0xCE, 0x05, 0x76,
    ];
    let frame = ADSBFrame::decode(&input).unwrap();
    assert_eq!(frame.downlink_format, 17);
    assert_eq!(frame.capability, Capability::Level2Airborne);
    assert_eq!(frame.icao, "4840D6");
    match frame.message {
      ADSBMessage::AircraftIdentification(ref id) => {
        assert_eq!(id.type_code, 4);
        assert_eq!(id.emitter_category, 0);
        assert_eq!(id.wake_vortex_category, WakeVortexCategory::NoInfo);
        assert_eq!(id.callsign, "KLM10102");
      }
      _ => panic!("expected AircraftIdentification"),
    }
    assert_eq!(frame.parity, 13501814);

    let input = [
      0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71,
      0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98
    ];
    let frame = ADSBFrame::decode(&input).unwrap();
    assert_eq!(frame.downlink_format, 17);
    assert_eq!(frame.capability, Capability::Level2Airborne);
    assert_eq!(frame.icao, "4840D6");
    match frame.message {
      ADSBMessage::AircraftIdentification(ref id) => {
        assert_eq!(id.type_code, 4);
        assert_eq!(id.emitter_category, 0);
        assert_eq!(id.wake_vortex_category, WakeVortexCategory::NoInfo);
        assert_eq!(id.callsign, "KLM1023");
      }
      _ => panic!("expected AircraftIdentification"),
    }
    assert_eq!(frame.parity, 5726360);

    let input = [
      0x8D, 0x7C, 0x71, 0x81, 0x21, 0x5D, 0x01, 0xA0,
      0x82, 0x08, 0x20, 0x4D, 0x8B, 0xF1
    ];
    let frame = ADSBFrame::decode(&input).unwrap();
    assert_eq!(frame.downlink_format, 17);
    assert_eq!(frame.capability, Capability::Level2Airborne);
    assert_eq!(frame.icao, "7C7181");
    match frame.message {
      ADSBMessage::AircraftIdentification(ref id) => {
        assert_eq!(id.type_code, 4);
        assert_eq!(id.emitter_category, 1);
        assert_eq!(id.wake_vortex_category, WakeVortexCategory::Light);
        assert_eq!(id.callsign, "WPF");
      }
      _ => panic!("expected AircraftIdentification"),
    }
    assert_eq!(frame.parity, 5082097);

    let input = [
      0x8D, 0x7C, 0x77, 0x45, 0x22, 0x61, 0x51, 0xA0,
      0x82, 0x08, 0x20, 0x5C, 0xE9, 0xC2
    ];
    let frame = ADSBFrame::decode(&input).unwrap();
    assert_eq!(frame.downlink_format, 17);
    assert_eq!(frame.capability, Capability::Level2Airborne);
    assert_eq!(frame.icao, "7C7745");
    match frame.message {
      ADSBMessage::AircraftIdentification(ref id) => {
        assert_eq!(id.type_code, 4);
        assert_eq!(id.emitter_category, 2);
        assert_eq!(id.wake_vortex_category, WakeVortexCategory::Medium1);
        assert_eq!(id.callsign, "XUF");
      }
      _ => panic!("expected AircraftIdentification"),
    }
    assert_eq!(frame.parity, 6089154);

    let input = [
      0x8D, 0x7C, 0x80, 0xAD, 0x23, 0x58, 0xF6, 0xB1,
      0xE3, 0x5C, 0x60, 0xFF, 0x19, 0x25
    ];
    let frame = ADSBFrame::decode(&input).unwrap();
    assert_eq!(frame.downlink_format, 17);
    assert_eq!(frame.capability, Capability::Level2Airborne);
    assert_eq!(frame.icao, "7C80AD");
    match frame.message {
      ADSBMessage::AircraftIdentification(ref id) => {
        assert_eq!(id.type_code, 4);
        assert_eq!(id.emitter_category, 3);
        assert_eq!(id.wake_vortex_category, WakeVortexCategory::Medium2);
        assert_eq!(id.callsign, "VOZ1851");
      }
      _ => panic!("expected AircraftIdentification"),
    }
    assert_eq!(frame.parity, 16718117);

    let input = [
      0x8D, 0x7C, 0x14, 0x65, 0x25, 0x44, 0x60, 0x74,
      0xDF, 0x58, 0x20, 0x73, 0x8E, 0x90
    ];
    let frame = ADSBFrame::decode(&input).unwrap();
    assert_eq!(frame.downlink_format, 17);
    assert_eq!(frame.capability, Capability::Level2Airborne);
    assert_eq!(frame.icao, "7C1465");
    match frame.message {
      ADSBMessage::AircraftIdentification(ref id) => {
        assert_eq!(id.type_code, 4);
        assert_eq!(id.emitter_category, 5);
        assert_eq!(id.wake_vortex_category, WakeVortexCategory::Heavy);
        assert_eq!(id.callsign, "QFA475");
      }
      _ => panic!("expected AircraftIdentification"),
    }
    assert_eq!(frame.parity, 7573136);
  }

  #[test]
  fn test_decode_df_17_operation_status() {
    let input = [
      0x8D, 0x89, 0x65, 0xD2, 0xF8, 0x21, 0x00, 0x02,
      0x00, 0x49, 0xB8, 0x94, 0xA4, 0x5F,
    ];
    let frame = ADSBFrame::decode(&input).unwrap();
    dbg!(frame);
  }
}