#![allow(clippy::expect_used, clippy::panic)]

use super::*;

fn bbox(sw: (f64, f64), ne: (f64, f64)) -> Area {
    Area::BoundingBox {
        south_west: GeoPoint {
            lat: sw.0,
            lon: sw.1,
        },
        north_east: GeoPoint {
            lat: ne.0,
            lon: ne.1,
        },
    }
}

#[test]
fn builders_set_every_field() {
    let airspace = Airspace::new(AirspaceKind::Controlled(ControlledClass::B), "KBOS")
        .with_name(Some("BOSTON AREA A".to_owned()))
        .with_center_ident(Some("KBOS".to_owned()))
        .with_lower(AltitudeLimit::ground())
        .with_upper(AltitudeLimit::new(Some(7000.0), AltitudeDatum::Msl))
        .with_bounds(Some(bbox((42.0, -71.2), (42.6, -70.8))));
    assert_eq!(airspace.kind, AirspaceKind::Controlled(ControlledClass::B));
    assert_eq!(airspace.center_ident.as_deref(), Some("KBOS"));
    assert_eq!(airspace.lower.datum, AltitudeDatum::Ground);
    assert_eq!(airspace.lower.value_ft, None);
    assert_eq!(airspace.upper.value_ft, Some(7000.0));
    assert_eq!(airspace.upper.datum, AltitudeDatum::Msl);
}

#[test]
fn default_limits_are_ground_to_unlimited() {
    let airspace = Airspace::new(AirspaceKind::Restrictive(RestrictiveKind::Moa), "BOARDMAN");
    assert_eq!(airspace.lower.datum, AltitudeDatum::Ground);
    assert_eq!(airspace.upper.datum, AltitudeDatum::Unlimited);
    assert_eq!(airspace.center_ident, None, "SUA has no airport center");
}

#[test]
fn bounds_may_contain_is_bbox_level() {
    let airspace = Airspace::new(AirspaceKind::Controlled(ControlledClass::C), "KBIL")
        .with_bounds(Some(bbox((45.0, -109.0), (46.0, -108.0))));
    // A point inside the box: "possibly inside" the true airspace.
    assert_eq!(
        airspace.bounds_may_contain(GeoPoint {
            lat: 45.5,
            lon: -108.5
        }),
        Some(true)
    );
    // A point outside the box: definitively not in the airspace.
    assert_eq!(
        airspace.bounds_may_contain(GeoPoint {
            lat: 44.0,
            lon: -108.5
        }),
        Some(false)
    );
}

#[test]
fn bounds_may_contain_is_none_without_bounds() {
    let airspace = Airspace::new(AirspaceKind::Controlled(ControlledClass::D), "KAUO");
    assert_eq!(
        airspace.bounds_may_contain(GeoPoint { lat: 0.0, lon: 0.0 }),
        None
    );
}

#[test]
fn round_trips_through_serde() {
    let airspace = Airspace::new(
        AirspaceKind::Restrictive(RestrictiveKind::Restricted),
        "R2304",
    )
    .with_lower(AltitudeLimit::new(Some(0.0), AltitudeDatum::Agl))
    .with_upper(AltitudeLimit::new(
        Some(18000.0),
        AltitudeDatum::FlightLevel,
    ))
    .with_bounds(Some(bbox((32.0, -114.0), (33.0, -113.0))));
    let json = serde_json::to_string(&airspace).expect("serialize");
    let back: Airspace = serde_json::from_str(&json).expect("deserialize");
    assert_eq!(airspace, back);
}
