use crate::{IdArray, IdSlice, id_array, id_slice, tests::util::BTest, usize_id as id};
use std::{
    array::IntoIter,
    borrow::{Borrow, BorrowMut},
    cmp::Ordering,
    collections::hash_map::DefaultHasher,
    hash::{Hash, Hasher},
    ops::{Index, IndexMut},
    slice::{Iter, IterMut},
};

#[test]
fn as_array_test() {
    let id_array = id_array![BTest; 1];

    let actual: &[i32; 1] = id_array.as_array();
    let expected = &[1];
    assert_eq!(actual, expected);
}

#[test]
fn as_mut_array_test() {
    let mut id_array = id_array![BTest; 1];

    let actual: &mut [i32; 1] = id_array.as_mut_array();
    actual[0] = 2;

    let expected = &[2];
    assert_eq!(actual, expected);
}

#[test]
fn as_mut_id_slice_test() {
    let mut id_array = id_array![1];

    let actual: &mut IdSlice<BTest, i32> = id_array.as_mut_id_slice();
    actual[id!(0)] = 2;

    let expected = id_slice![BTest; i32; 2];
    assert_eq!(actual, expected);
}

#[test]
fn as_id_slice_test() {
    let id_array = &id_array![1];

    let actual: &IdSlice<BTest, i32> = id_array.as_id_slice();
    let expected = id_slice![BTest; i32; 1];
    assert_eq!(actual, expected);
}

#[test]
fn from_array_test() {
    let array = [1];

    let actual: IdArray<BTest, i32, 1> = IdArray::from(array);
    let expected = id_array![BTest; i32; 1];
    assert_eq!(actual, expected);
}

#[test]
fn from_array_ref_test() {
    let array = &[1];

    let actual: &IdArray<BTest, i32, 1> = IdArray::from_array_ref(array);
    let expected = &id_array![BTest; i32; 1];
    assert_eq!(actual, expected);
}

#[test]
fn from_mut_array_test() {
    let array = &mut [1];

    let actual: &mut IdArray<BTest, i32, 1> = IdArray::from_mut_array(array);
    actual[id!(0)] = 2;

    let expected = &id_array![BTest; i32; 2];
    assert_eq!(actual, expected);
}

#[test]
fn into_array_test() {
    let id_array = id_array![BTest; 1];

    let actual: [i32; 1] = id_array.into_array();
    let expected = [1];
    assert_eq!(actual, expected);
}

#[test]
fn as_mut_test() {
    let mut id_array = id_array![1];

    let actual: &mut IdSlice<BTest, i32> = id_array.as_mut();
    actual[id!(0)] = 2;

    let expected = id_slice![BTest; 2];
    assert_eq!(actual, expected);
}

#[test]
fn as_ref_test() {
    let id_array = id_array![1];

    let actual: &IdSlice<BTest, i32> = id_array.as_ref();
    let expected = id_slice![BTest; 1];
    assert_eq!(actual, expected);
}

#[test]
fn borrow_test() {
    let id_array = id_array![1];

    let actual: &IdSlice<BTest, i32> = id_array.borrow();
    let expected = id_slice![BTest; 1];
    assert_eq!(actual, expected);
}

#[test]
fn borrow_mut_test() {
    let mut id_array = id_array![1];

    let actual: &mut IdSlice<BTest, i32> = id_array.borrow_mut();
    actual[id!(0)] = 2;

    let expected = id_slice![BTest; 2];
    assert_eq!(actual, expected);
}

#[test]
#[allow(clippy::clone_on_copy)]
fn clone_test() {
    let id_array = id_array!(BTest; 1);

    let actual: IdArray<BTest, i32, 1> = id_array.clone();
    let expected = id_array![BTest; i32; 1];
    assert_eq!(actual, expected);
}

#[test]
fn debug_fmt_test() {
    let empty_id_array = id_array![BTest; i32];
    let actual: String = format!("{:?}", empty_id_array);
    let expected = "BTest[]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:10?}", empty_id_array);
    let expected = "BTest[]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:>10?}", empty_id_array);
    let expected = "BTest[]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:^10?}", empty_id_array);
    let expected = "BTest[]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:.1?}", empty_id_array);
    let expected = "BTest[]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:#?}", empty_id_array);
    let expected = "branded_id::tests::util::b_test::BTest[]";
    assert_eq!(actual, expected);

    let array_0 = ["Hello"];
    let nested_id_array = id_array![BTest; array_0, ["Hi"]];

    let actual: String = format!("{:#?}", nested_id_array);
    let expected = "branded_id::tests::util::b_test::BTest[\n    [\n        \"Hello\",\n    ],\n    [\n        \"Hi\",\n    ],\n]";
    assert_eq!(actual, expected);

    let id_array = id_array![BTest; "Hello", "Hi"];

    let actual: String = format!("{:?}", id_array);
    let expected = "BTest[\"Hello\", \"Hi\"]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:10?}", id_array);
    let expected = "BTest[\"Hello\", \"Hi\"]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:>10?}", id_array);
    let expected = "BTest[\"Hello\", \"Hi\"]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:^10?}", id_array);
    let expected = "BTest[\"Hello\", \"Hi\"]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:.1?}", id_array);
    let expected = "BTest[\"Hello\", \"Hi\"]";
    assert_eq!(actual, expected);

    let actual: String = format!("{:#?}", id_array);
    let expected = "branded_id::tests::util::b_test::BTest[\n    \"Hello\",\n    \"Hi\",\n]";
    assert_eq!(actual, expected);
}

#[test]
fn default_test() {
    let actual: IdArray<BTest, i32, 1> = <IdArray<BTest, i32, 1>>::default();
    let expected = id_array![BTest; i32; 0];
    assert_eq!(actual, expected);
}

#[test]
fn from_test() {
    let array = [1];

    let actual: IdArray<BTest, i32, 1> = From::from(array);
    let expected = id_array![BTest; i32; 1];
    assert_eq!(actual, expected);
}

#[test]
fn hash_test() {
    let id_array = id_array!(BTest; 1);
    let mut hasher_0 = DefaultHasher::new();
    id_array.hash(&mut hasher_0);

    let array = [1];
    let mut hasher_1 = DefaultHasher::new();
    array.hash(&mut hasher_1);

    let actual: u64 = hasher_0.finish();
    let expected = hasher_1.finish();
    assert_eq!(actual, expected);
}

#[test]
fn hash_slice_test() {
    let id_arrays = [id_array!(BTest; 1), id_array!(BTest; 2)];
    let mut hasher_0 = DefaultHasher::new();
    IdArray::hash_slice(&id_arrays, &mut hasher_0);

    let arrays = [[1], [2]];
    let mut hasher_1 = DefaultHasher::new();
    <[i32; 1] as Hash>::hash_slice(&arrays, &mut hasher_1);

    let actual: u64 = hasher_0.finish();
    let expected = hasher_1.finish();
    assert_eq!(actual, expected);
}

#[test]
fn index_test() {
    let id_array = id_array![BTest; 0, 1, 2, 3];

    let actual: &i32 = id_array.index(id!(1));
    let expected = &1;
    assert_eq!(actual, expected);

    let actual: &IdSlice<BTest, i32> = id_array.index(id!(1)..id!(3));
    let expected = id_slice![1, 2];
    assert_eq!(actual, expected);

    let actual: &IdSlice<BTest, i32> = id_array.index(id!(1)..);
    let expected = id_slice![1, 2, 3];
    assert_eq!(actual, expected);

    let actual: &IdSlice<BTest, i32> = id_array.index(..);
    let expected = id_slice![0, 1, 2, 3];
    assert_eq!(actual, expected);

    let actual: &IdSlice<BTest, i32> = id_array.index(id!(1)..=id!(3));
    let expected = id_slice![1, 2, 3];
    assert_eq!(actual, expected);

    let actual: &IdSlice<BTest, i32> = id_array.index(..id!(3));
    let expected = id_slice![0, 1, 2];
    assert_eq!(actual, expected);

    let actual: &IdSlice<BTest, i32> = id_array.index(..=id!(3));
    let expected = id_slice![0, 1, 2, 3];
    assert_eq!(actual, expected);
}

#[test]
fn index_mut_test() {
    let mut id_array = id_array![BTest; 0, 1, 2, 3];

    let actual: &mut i32 = id_array.index_mut(id!(1));
    let expected = &1;
    assert_eq!(actual, expected);

    let actual: &mut IdSlice<BTest, i32> = id_array.index_mut(id!(1)..id!(3));
    let expected = id_slice![1, 2];
    assert_eq!(actual, expected);

    let actual: &mut IdSlice<BTest, i32> = id_array.index_mut(id!(1)..);
    let expected = id_slice![1, 2, 3];
    assert_eq!(actual, expected);

    let actual: &mut IdSlice<BTest, i32> = id_array.index_mut(..);
    let expected = id_slice![0, 1, 2, 3];
    assert_eq!(actual, expected);

    let actual: &mut IdSlice<BTest, i32> = id_array.index_mut(id!(1)..=id!(3));
    let expected = id_slice![1, 2, 3];
    assert_eq!(actual, expected);

    let actual: &mut IdSlice<BTest, i32> = id_array.index_mut(..id!(3));
    let expected = id_slice![0, 1, 2];
    assert_eq!(actual, expected);

    let actual: &mut IdSlice<BTest, i32> = id_array.index_mut(..=id!(3));
    let expected = id_slice![0, 1, 2, 3];
    assert_eq!(actual, expected);
}

#[test]
#[allow(clippy::into_iter_on_ref)]
fn into_ref_iter_test() {
    let id_array = &id_array![BTest; 0, 1, 2];

    let actual: Iter<'_, i32> = id_array.into_iter();
    let expected = (&[0, 1, 2]).into_iter();
    assert!(actual.eq(expected));
}

#[test]
#[allow(clippy::into_iter_on_ref)]
fn into_mut_iter_test() {
    let id_array = &mut id_array![BTest; 0, 1, 2];

    let mut expected = [0, 1, 2];

    let actual: IterMut<'_, i32> = id_array.into_iter();
    let expected = (&mut expected).into_iter();
    assert!(actual.eq(expected));
}

#[test]
fn into_iter_test() {
    let id_array = id_array![BTest; 0, 1, 2];

    let actual: IntoIter<i32, 3> = id_array.into_iter();
    let expected = [0, 1, 2].into_iter();
    assert!(actual.eq(expected));
}

#[test]
fn iter_test() {
    let id_array = id_array![BTest; 0, 1, 2];

    let actual: Iter<i32> = id_array.iter();
    let expected = [0, 1, 2].as_slice().iter();
    assert!(actual.eq(expected));
}

#[test]
fn iter_mut_test() {
    let mut id_array = id_array![BTest; 0, 1, 2];

    let actual: IterMut<i32> = id_array.iter_mut();
    let expected = [0, 1, 2].as_slice().iter();
    assert!(actual.eq(expected));
}

#[test]
fn cmp_test() {
    let array_0 = id_array![BTest; 1];
    let array_1 = id_array![BTest; 2];

    let actual: Ordering = array_0.cmp(&array_0);
    let expected = Ordering::Equal;
    assert_eq!(actual, expected);

    let actual: Ordering = array_0.cmp(&array_1);
    let expected = Ordering::Less;
    assert_eq!(actual, expected);

    let actual: Ordering = array_1.cmp(&array_0);
    let expected = Ordering::Greater;
    assert_eq!(actual, expected);
}

#[test]
fn max_test() {
    let array_0 = id_array![BTest; 1];
    let array_1 = id_array![BTest; 2];

    let actual: IdArray<BTest, i32, 1> = array_0.max(array_1);
    let expected = id_array![BTest; 2];
    assert_eq!(actual, expected);
}

#[test]
fn min_test() {
    let array_0 = id_array![BTest; 1];
    let array_1 = id_array![BTest; 2];

    let actual: IdArray<BTest, i32, 1> = array_0.min(array_1);
    let expected = id_array![BTest; 1];
    assert_eq!(actual, expected);
}

#[test]
fn clamp_test() {
    let array_0 = id_array![BTest; 1];
    let array_1 = id_array![BTest; 2];
    let array_2 = id_array![BTest; 3];

    let actual: IdArray<BTest, i32, 1> = array_2.clamp(array_0, array_1);
    let expected = id_array![BTest; 2];
    assert_eq!(actual, expected);

    let array_0 = id_array![BTest; 1];
    let array_1 = id_array![BTest; 2];
    let array_2 = id_array![BTest; 3];

    let actual: IdArray<BTest, i32, 1> = array_0.clamp(array_1, array_2);
    let expected = id_array![BTest; 2];
    assert_eq!(actual, expected);
}

#[test]
fn eq_ref_id_slice_test() {
    let id_array = id_array![BTest; 1];
    let id_slice_0 = id_slice![1];
    let id_slice_1 = id_slice![2];

    let actual: bool = id_array.eq(&id_slice_0);
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array.eq(&id_slice_1);
    let expected = false;
    assert_eq!(actual, expected);
}

#[test]
fn ne_ref_id_slice_test() {
    let id_array = id_array![BTest; 1];
    let id_slice_0 = id_slice![1];
    let id_slice_1 = id_slice![2];

    let actual: bool = id_array.ne(&id_slice_0);
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array.ne(&id_slice_1);
    let expected = true;
    assert_eq!(actual, expected);
}

#[test]
fn eq_mut_id_slice_test() {
    let id_array = id_array![BTest; 1];
    let slice_0 = &mut [1];
    let id_slice_0 = IdSlice::<BTest, _>::from_mut_slice(slice_0);
    let slice_1 = &mut [2];
    let id_slice_1 = IdSlice::<BTest, _>::from_mut_slice(slice_1);

    let actual: bool = id_array.eq(&id_slice_0);
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array.eq(&id_slice_1);
    let expected = false;
    assert_eq!(actual, expected);
}

#[test]
fn ne_mut_id_slice_test() {
    let id_array = id_array![BTest; 1];
    let slice_0 = &mut [1];
    let id_slice_0 = IdSlice::<BTest, _>::from_mut_slice(slice_0);
    let slice_1 = &mut [2];
    let id_slice_1 = IdSlice::<BTest, _>::from_mut_slice(slice_1);

    let actual: bool = id_array.ne(&id_slice_0);
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array.ne(&id_slice_1);
    let expected = true;
    assert_eq!(actual, expected);
}

#[test]
fn eq_id_slice_test() {
    let id_array = id_array![BTest; 1];
    let id_slice_0 = id_slice![1];
    let id_slice_1 = id_slice![2];

    let actual: bool = id_array.eq(id_slice_0);
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array.eq(id_slice_1);
    let expected = false;
    assert_eq!(actual, expected);
}

#[test]
fn ne_id_slice_test() {
    let id_array = id_array![BTest; 1];
    let id_slice_0 = id_slice![1];
    let id_slice_1 = id_slice![2];

    let actual: bool = id_array.ne(id_slice_0);
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array.ne(id_slice_1);
    let expected = true;
    assert_eq!(actual, expected);
}

#[test]
fn eq_test() {
    let id_array_0 = id_array![BTest; 1];
    let id_array_1 = id_array![2];

    let actual: bool = id_array_0.eq(&id_array_0);
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array_0.eq(&id_array_1);
    let expected = false;
    assert_eq!(actual, expected);
}

#[test]
fn ne_test() {
    let id_array_0 = id_array![BTest; 1];
    let id_array_1 = id_array![2];

    let actual: bool = id_array_0.ne(&id_array_0);
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array_0.ne(&id_array_1);
    let expected = true;
    assert_eq!(actual, expected);
}

#[test]
fn partial_cmp_test() {
    let id_array_0 = id_array!(BTest; 1);
    let id_array_1 = id_array!(BTest; 2);

    let actual: Option<Ordering> = id_array_0.partial_cmp(&id_array_0);
    let expected = Some(Ordering::Equal);
    assert_eq!(actual, expected);

    let actual: Option<Ordering> = id_array_0.partial_cmp(&id_array_1);
    let expected = Some(Ordering::Less);
    assert_eq!(actual, expected);

    let actual: Option<Ordering> = id_array_1.partial_cmp(&id_array_0);
    let expected = Some(Ordering::Greater);
    assert_eq!(actual, expected);
}

#[test]
fn lt_test() {
    let id_array_0 = id_array!(BTest; 1);
    let id_array_1 = id_array!(BTest; 2);

    let actual: bool = id_array_0 < id_array_0;
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array_0 < id_array_1;
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array_1 < id_array_0;
    let expected = false;
    assert_eq!(actual, expected);
}

#[test]
fn le_test() {
    let id_array_0 = id_array!(BTest; 1);
    let id_array_1 = id_array!(BTest; 2);

    let actual: bool = id_array_0 <= id_array_0;
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array_0 <= id_array_1;
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array_1 <= id_array_0;
    let expected = false;
    assert_eq!(actual, expected);
}

#[test]
fn gt_test() {
    let id_array_0 = id_array!(BTest; 1);
    let id_array_1 = id_array!(BTest; 2);

    let actual: bool = id_array_0 > id_array_0;
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array_0 > id_array_1;
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array_1 > id_array_0;
    let expected = true;
    assert_eq!(actual, expected);
}

#[test]
fn ge_test() {
    let id_array_0 = id_array!(BTest; 1);
    let id_array_1 = id_array!(BTest; 2);

    let actual: bool = id_array_0 >= id_array_0;
    let expected = true;
    assert_eq!(actual, expected);

    let actual: bool = id_array_0 >= id_array_1;
    let expected = false;
    assert_eq!(actual, expected);

    let actual: bool = id_array_1 >= id_array_0;
    let expected = true;
    assert_eq!(actual, expected);
}
