#![cfg(feature = "derive")]
// Various structs/fields that we are deriving `Arbitrary` for aren't actually
// used except to exercise the derive.
#![allow(dead_code)]
// Various assert_eq! are used to compare result of bool amongst other data types
// In this case, using assert! is less explicit and readable
#![allow(clippy::bool_assert_comparison)]

use arbitrary::*;

fn arbitrary_from<'a, T: Arbitrary<'a>>(input: &'a [u8]) -> T {
    let mut buf = Unstructured::new(input);
    T::arbitrary(&mut buf).expect("can create arbitrary instance OK")
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Arbitrary)]
pub struct Rgb {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

#[test]
fn struct_with_named_fields() {
    let rgb: Rgb = arbitrary_from(&[4, 5, 6]);
    assert_eq!(rgb.r, 4);
    assert_eq!(rgb.g, 5);
    assert_eq!(rgb.b, 6);

    assert_eq!((3, Some(3)), <Rgb as Arbitrary>::size_hint(0));
}

#[derive(Copy, Clone, Debug, Arbitrary)]
struct MyTupleStruct(u8, bool);

#[test]
fn tuple_struct() {
    let s: MyTupleStruct = arbitrary_from(&[43, 42]);
    assert_eq!(s.0, 43);
    assert_eq!(s.1, false);

    let s: MyTupleStruct = arbitrary_from(&[42, 43]);
    assert_eq!(s.0, 42);
    assert_eq!(s.1, true);

    assert_eq!((2, Some(2)), <MyTupleStruct as Arbitrary>::size_hint(0));
}

#[derive(Clone, Debug, Arbitrary)]
struct EndingInVec(u8, bool, u32, Vec<u16>);
#[derive(Clone, Debug, Arbitrary)]
struct EndingInString(u8, bool, u32, String);

#[test]
fn test_take_rest() {
    let bytes = [1, 1, 1, 2, 3, 4, 5, 6, 7, 8];
    let s1 = EndingInVec::arbitrary_take_rest(Unstructured::new(&bytes)).unwrap();
    let s2 = EndingInString::arbitrary_take_rest(Unstructured::new(&bytes)).unwrap();
    assert_eq!(s1.0, 1);
    assert_eq!(s2.0, 1);
    assert_eq!(s1.1, true);
    assert_eq!(s2.1, true);
    assert_eq!(s1.2, 0x4030201);
    assert_eq!(s2.2, 0x4030201);
    assert_eq!(s1.3, vec![0x0706]);
    assert_eq!(s2.3, "\x05\x06\x07\x08");
}

#[derive(Copy, Clone, Debug, Arbitrary)]
enum MyEnum {
    Unit,
    Tuple(u8, u16),
    Struct { a: u32, b: (bool, u64) },
}

#[test]
fn derive_enum() {
    let mut raw = vec![
        // The choice of which enum variant takes 4 bytes.
        1, 2, 3, 4,
        // And then we need up to 13 bytes for creating `MyEnum::Struct`, the
        // largest variant.
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
    ];

    let mut saw_unit = false;
    let mut saw_tuple = false;
    let mut saw_struct = false;

    for i in 0..=255 {
        // Choose different variants each iteration.
        for el in &mut raw[..4] {
            *el = i;
        }

        let e: MyEnum = arbitrary_from(&raw);

        match e {
            MyEnum::Unit => {
                saw_unit = true;
            }
            MyEnum::Tuple(a, b) => {
                saw_tuple = true;
                assert_eq!(a, arbitrary_from(&raw[4..5]));
                assert_eq!(b, arbitrary_from(&raw[5..]));
            }
            MyEnum::Struct { a, b } => {
                saw_struct = true;
                assert_eq!(a, arbitrary_from(&raw[4..8]));
                assert_eq!(b, arbitrary_from(&raw[8..]));
            }
        }
    }

    assert!(saw_unit);
    assert!(saw_tuple);
    assert!(saw_struct);

    assert_eq!((4, Some(17)), <MyEnum as Arbitrary>::size_hint(0));
}

// This should result in a compiler-error:
// #[derive(Arbitrary, Debug)]
// enum Never {
//     #[arbitrary(skip)]
//     Nope,
// }

#[derive(Arbitrary, Debug)]
enum SkipVariant {
    Always,
    #[arbitrary(skip)]
    Never,
}

#[test]
fn test_skip_variant() {
    (0..=u8::MAX).for_each(|byte| {
        let buffer = [byte];
        let unstructured = Unstructured::new(&buffer);
        let skip_variant = SkipVariant::arbitrary_take_rest(unstructured).unwrap();
        assert!(!matches!(skip_variant, SkipVariant::Never));
    })
}

#[derive(Arbitrary, Debug)]
enum RecursiveTree {
    Leaf,
    Node {
        left: Box<RecursiveTree>,
        right: Box<RecursiveTree>,
    },
}

#[derive(Arbitrary, Debug)]
struct WideRecursiveStruct {
    a: Option<Box<WideRecursiveStruct>>,
    b: Option<Box<WideRecursiveStruct>>,
    c: Option<Box<WideRecursiveStruct>>,
    d: Option<Box<WideRecursiveStruct>>,
    e: Option<Box<WideRecursiveStruct>>,
    f: Option<Box<WideRecursiveStruct>>,
    g: Option<Box<WideRecursiveStruct>>,
    h: Option<Box<WideRecursiveStruct>>,
    i: Option<Box<WideRecursiveStruct>>,
    k: Option<Box<WideRecursiveStruct>>,
}

#[derive(Arbitrary, Debug)]
enum WideRecursiveEnum {
    None,
    A(Box<WideRecursiveStruct>),
    B(Box<WideRecursiveStruct>),
    C(Box<WideRecursiveStruct>),
    D(Box<WideRecursiveStruct>),
    E(Box<WideRecursiveStruct>),
    F(Box<WideRecursiveStruct>),
    G(Box<WideRecursiveStruct>),
    H(Box<WideRecursiveStruct>),
    I(Box<WideRecursiveStruct>),
    K(Box<WideRecursiveStruct>),
}

#[derive(Arbitrary, Debug)]
enum WideRecursiveMixedEnum {
    None,
    A(Box<WideRecursiveMixedEnum>),
    B(Box<WideRecursiveMixedEnum>),
    C(Box<WideRecursiveMixedEnum>),
    D(Box<WideRecursiveMixedEnum>),
    E(Box<WideRecursiveMixedEnum>),
    F(Box<WideRecursiveMixedStruct>),
    G(Box<WideRecursiveMixedStruct>),
    H(Box<WideRecursiveMixedStruct>),
    I(Box<WideRecursiveMixedStruct>),
    K(Box<WideRecursiveMixedStruct>),
}

#[derive(Arbitrary, Debug)]
struct WideRecursiveMixedStruct {
    a: Option<Box<WideRecursiveMixedEnum>>,
    b: Option<Box<WideRecursiveMixedEnum>>,
    c: Option<Box<WideRecursiveMixedEnum>>,
    d: Option<Box<WideRecursiveMixedEnum>>,
    e: Option<Box<WideRecursiveMixedEnum>>,
    f: Option<Box<WideRecursiveMixedStruct>>,
    g: Option<Box<WideRecursiveMixedStruct>>,
    h: Option<Box<WideRecursiveMixedStruct>>,
    i: Option<Box<WideRecursiveMixedStruct>>,
    k: Option<Box<WideRecursiveMixedStruct>>,
}

#[test]
fn recursive() {
    let raw = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
    let _rec: RecursiveTree = arbitrary_from(&raw);
    let _rec: WideRecursiveStruct = arbitrary_from(&raw);
    let _rec: WideRecursiveEnum = arbitrary_from(&raw);
    let _rec: WideRecursiveMixedStruct = arbitrary_from(&raw);
    let _rec: WideRecursiveMixedEnum = arbitrary_from(&raw);

    assert_eq!((0, None), <WideRecursiveStruct as Arbitrary>::size_hint(0));
    assert_eq!((0, None), <WideRecursiveEnum as Arbitrary>::size_hint(0));
    assert_eq!(
        (0, None),
        <WideRecursiveMixedStruct as Arbitrary>::size_hint(0)
    );
    assert_eq!(
        (0, None),
        <WideRecursiveMixedEnum as Arbitrary>::size_hint(0)
    );

    let (lower, upper) = <RecursiveTree as Arbitrary>::size_hint(0);
    assert_eq!(lower, 0, "Cannot compute size hint of recursive structure");
    assert!(
        upper.is_none(),
        "potentially infinitely recursive, so no upper bound"
    );
}

#[derive(Arbitrary, Debug)]
struct Generic<T> {
    inner: T,
}

#[test]
fn generics() {
    let raw = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
    let gen: Generic<bool> = arbitrary_from(&raw);
    assert!(gen.inner);

    let (lower, upper) = <Generic<u32> as Arbitrary>::size_hint(0);
    assert_eq!(lower, 4);
    assert_eq!(upper, Some(4));
}

#[derive(Arbitrary, Debug)]
struct OneLifetime<'a> {
    alpha: &'a str,
}

#[test]
fn one_lifetime() {
    // Last byte is used for length
    let raw: Vec<u8> = vec![97, 98, 99, 100, 3];
    let lifetime: OneLifetime = arbitrary_from(&raw);
    assert_eq!("abc", lifetime.alpha);

    let (lower, upper) = <OneLifetime as Arbitrary>::size_hint(0);
    assert_eq!(lower, 0);
    assert_eq!(upper, None);
}

#[derive(Arbitrary, Debug)]
struct TwoLifetimes<'a, 'b> {
    alpha: &'a str,
    beta: &'b str,
}

#[test]
fn two_lifetimes() {
    // Last byte is used for length
    let raw: Vec<u8> = vec![97, 98, 99, 100, 101, 102, 103, 3];
    let lifetime: TwoLifetimes = arbitrary_from(&raw);
    assert_eq!("abc", lifetime.alpha);
    assert_eq!("def", lifetime.beta);

    let (lower, upper) = <TwoLifetimes as Arbitrary>::size_hint(0);
    assert_eq!(lower, 0);
    assert_eq!(upper, None);
}

#[test]
fn recursive_and_empty_input() {
    // None of the following derives should result in a stack overflow. See
    // https://github.com/rust-fuzz/arbitrary/issues/107 for details.

    #[derive(Debug, Arbitrary)]
    enum Nat {
        Succ(Box<Nat>),
        Zero,
    }

    let _ = Nat::arbitrary(&mut Unstructured::new(&[]));

    #[derive(Debug, Arbitrary)]
    enum Nat2 {
        Zero,
        Succ(Box<Nat2>),
    }

    let _ = Nat2::arbitrary(&mut Unstructured::new(&[]));

    #[derive(Debug, Arbitrary)]
    struct Nat3 {
        f: Option<Box<Nat3>>,
    }

    let _ = Nat3::arbitrary(&mut Unstructured::new(&[]));

    #[derive(Debug, Arbitrary)]
    struct Nat4(Option<Box<Nat4>>);

    let _ = Nat4::arbitrary(&mut Unstructured::new(&[]));

    #[derive(Debug, Arbitrary)]
    enum Nat5 {
        Zero,
        Succ { f: Box<Nat5> },
    }

    let _ = Nat5::arbitrary(&mut Unstructured::new(&[]));
}

#[test]
fn test_field_attributes() {
    // A type that DOES NOT implement Arbitrary
    #[derive(Debug)]
    struct Weight(u8);

    #[derive(Debug, Arbitrary)]
    struct Parcel {
        #[arbitrary(with = arbitrary_weight)]
        weight: Weight,

        #[arbitrary(default)]
        width: u8,

        #[arbitrary(value = 2 + 2)]
        length: u8,

        height: u8,

        #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=100))]
        price: u8,
    }

    fn arbitrary_weight(u: &mut Unstructured) -> arbitrary::Result<Weight> {
        u.int_in_range(45..=56).map(Weight)
    }

    let parcel: Parcel = arbitrary_from(&[6, 199, 17]);

    // 45 + 6 = 51
    assert_eq!(parcel.weight.0, 51);

    // u8::default()
    assert_eq!(parcel.width, 0);

    // 2 + 2 = 4
    assert_eq!(parcel.length, 4);

    // 199 is the 2nd byte used by arbitrary
    assert_eq!(parcel.height, 199);

    // 17 is the 3rd byte used by arbitrary
    assert_eq!(parcel.price, 17);
}

#[test]
fn derive_structs_named_same_as_core() {
    #[derive(Debug, Arbitrary)]
    struct Option {
        f: core::option::Option<u32>,
    }

    let _ = Option::arbitrary(&mut Unstructured::new(&[]));

    #[derive(Debug, Default, Arbitrary)]
    struct Default {
        f: u32,
    }

    let _ = Default::arbitrary(&mut Unstructured::new(&[]));
}
