use crate::{
    archetype,
    component::Component,
    query::{
        view,
        view::{
            ParViews,
            ParViewsSeal,
            RepeatNone,
        },
    },
    registry,
    registry::{
        contains::{
            Contained,
            NotContained,
            Null,
        },
        Registry,
    },
};
use rayon::{
    iter,
    iter::{
        Either,
        IntoParallelRefIterator,
        IntoParallelRefMutIterator,
        ParallelIterator,
    },
};

pub trait CanonicalParViews<'a, V, P>
where
    V: ParViews<'a>,
{
    /// # Safety
    ///
    /// Each tuple in `columns` must contain the raw parts for a valid `Vec<C>` of size `length`
    /// for components `C`, ordered for the archetype identified by `archetype_identifier`.
    unsafe fn par_view<R>(
        columns: &[(*mut u8, usize)],
        length: usize,
        archetype_identifier: archetype::identifier::Iter<R>,
    ) -> V::ParResults
    where
        R: Registry;
}

impl<'a> CanonicalParViews<'a, view::Null, Null> for registry::Null {
    unsafe fn par_view<R>(
        _columns: &[(*mut u8, usize)],
        length: usize,
        _archetype_identifier: archetype::identifier::Iter<R>,
    ) -> <view::Null as ParViewsSeal<'a>>::ParResults
    where
        R: Registry,
    {
        iter::repeatn(view::Null, length)
    }
}

impl<'a, C, P, R, V> CanonicalParViews<'a, (&'a C, V), (&'a Contained, P)> for (C, R)
where
    C: Component + Sync,
    R: CanonicalParViews<'a, V, P>,
    V: ParViews<'a>,
{
    unsafe fn par_view<R_>(
        columns: &[(*mut u8, usize)],
        length: usize,
        mut archetype_identifier: archetype::identifier::Iter<R_>,
    ) -> <(&'a C, V) as ParViewsSeal<'a>>::ParResults
    where
        R_: Registry,
    {
        archetype_identifier.next();
        (
            // SAFETY: `columns` is guaranteed to contain raw parts for a valid `Vec<C>` of size
            // `length` for the currently viewed component `C`.
            unsafe {
                core::slice::from_raw_parts::<'a, C>(columns.get_unchecked(0).0.cast::<C>(), length)
            }
            .par_iter(),
            // SAFETY: The remaining components in `columns` are guaranteed to contain raw parts
            // for valid `Vec<C>`s of length `length` for each of the remaining components
            // identified by `archetype_identifier`.
            unsafe { R::par_view(columns.get_unchecked(1..), length, archetype_identifier) },
        )
    }
}

impl<'a, C, P, R, V> CanonicalParViews<'a, (&'a mut C, V), (&'a mut Contained, P)> for (C, R)
where
    C: Component + Send,
    R: CanonicalParViews<'a, V, P>,
    V: ParViews<'a>,
{
    unsafe fn par_view<R_>(
        columns: &[(*mut u8, usize)],
        length: usize,
        mut archetype_identifier: archetype::identifier::Iter<R_>,
    ) -> <(&'a mut C, V) as ParViewsSeal<'a>>::ParResults
    where
        R_: Registry,
    {
        archetype_identifier.next();
        (
            // SAFETY: `columns` is guaranteed to contain raw parts for a valid `Vec<C>` of size
            // `length` for the currently viewed component `C`.
            unsafe {
                core::slice::from_raw_parts_mut::<'a, C>(
                    columns.get_unchecked(0).0.cast::<C>(),
                    length,
                )
            }
            .par_iter_mut(),
            // SAFETY: The remaining components in `columns` are guaranteed to contain raw parts
            // for valid `Vec<C>`s of length `length` for each of the remaining components
            // identified by `archetype_identifier`.
            unsafe { R::par_view(columns.get_unchecked(1..), length, archetype_identifier) },
        )
    }
}

#[allow(clippy::unnecessary_wraps)]
fn wrap_some<T>(val: T) -> Option<T> {
    Some(val)
}

impl<'a, C, P, R, V> CanonicalParViews<'a, (Option<&'a C>, V), (Option<&'a Contained>, P)>
    for (C, R)
where
    C: Component + Sync,
    R: CanonicalParViews<'a, V, P>,
    V: ParViews<'a>,
{
    unsafe fn par_view<R_>(
        mut columns: &[(*mut u8, usize)],
        length: usize,
        mut archetype_identifier: archetype::identifier::Iter<R_>,
    ) -> <(Option<&'a C>, V) as ParViewsSeal<'a>>::ParResults
    where
        R_: Registry,
    {
        (
            // SAFETY: `archetype_identifier` is guaranteed to have at least one element remaining.
            if unsafe { archetype_identifier.next().unwrap_unchecked() } {
                Either::Right(
                    // SAFETY: `columns` is guaranteed to contain raw parts for a valid `Vec<C>` of
                    // size `length`. Since `component_map` contains an entry for the given
                    // component `C`'s entry in `columns`, then the column
                    // obtained here can be interpreted as a slice of type `C`
                    // of size `length`.
                    unsafe {
                        core::slice::from_raw_parts(
                            {
                                let column = columns.get_unchecked(0);
                                columns = columns.get_unchecked(1..);
                                column
                            }
                            .0
                            .cast::<C>(),
                            length,
                        )
                    }
                    .par_iter()
                    .map(wrap_some),
                )
            } else {
                Either::Left(iter::repeat(None).take(length))
            },
            // SAFETY: The remaining components in `columns` are guaranteed to contain raw parts
            // for valid `Vec<C>`s of length `length` for each of the remaining components
            // identified by `archetype_identifier`.
            unsafe { R::par_view(columns, length, archetype_identifier) },
        )
    }
}

impl<'a, C, P, R, V> CanonicalParViews<'a, (Option<&'a mut C>, V), (Option<&'a mut Contained>, P)>
    for (C, R)
where
    C: Component + Send,
    R: CanonicalParViews<'a, V, P>,
    V: ParViews<'a>,
{
    unsafe fn par_view<R_>(
        mut columns: &[(*mut u8, usize)],
        length: usize,
        mut archetype_identifier: archetype::identifier::Iter<R_>,
    ) -> <(Option<&'a mut C>, V) as ParViewsSeal<'a>>::ParResults
    where
        R_: Registry,
    {
        (
            // SAFETY: `archetype_identifier` is guaranteed to have at least one element remaining.
            if unsafe { archetype_identifier.next().unwrap_unchecked() } {
                Either::Right(
                    // SAFETY: `columns` is guaranteed to contain raw parts for a valid `Vec<C>` of
                    // size `length`. Since `component_map` contains an entry for the given
                    // component `C`'s entry in `columns`, then the column
                    // obtained here can be interpreted as a slice of type `C`
                    // of size `length`.
                    unsafe {
                        core::slice::from_raw_parts_mut(
                            {
                                let column = columns.get_unchecked(0);
                                columns = columns.get_unchecked(1..);
                                column
                            }
                            .0
                            .cast::<C>(),
                            length,
                        )
                    }
                    .par_iter_mut()
                    .map(wrap_some),
                )
            } else {
                Either::Left(RepeatNone::new(length))
            },
            // SAFETY: The remaining components in `columns` are guaranteed to contain raw parts
            // for valid `Vec<C>`s of length `length` for each of the remaining components
            // identified by `archetype_identifier`.
            unsafe { R::par_view(columns, length, archetype_identifier) },
        )
    }
}

impl<'a, C, P, R, V> CanonicalParViews<'a, V, (NotContained, P)> for (C, R)
where
    C: Component,
    R: CanonicalParViews<'a, V, P>,
    V: ParViews<'a>,
{
    unsafe fn par_view<R_>(
        mut columns: &[(*mut u8, usize)],
        length: usize,
        mut archetype_identifier: archetype::identifier::Iter<R_>,
    ) -> V::ParResults
    where
        R_: Registry,
    {
        // SAFETY: `archetype_identifier` is guaranteed to have at least one element remaining.
        if unsafe { archetype_identifier.next().unwrap_unchecked() } {
            // SAFETY: Since `archetype_identifier` has this component set, there is guaranteed to
            // be at least one entry in `columns`.
            unsafe {
                columns = columns.get_unchecked(1..);
            }
        }
        // SAFETY: The remaining components in `columns` are guaranteed to contain raw parts
        // for valid `Vec<C>`s of length `length` for each of the remaining components
        // identified by `archetype_identifier`.
        unsafe { R::par_view(columns, length, archetype_identifier) }
    }
}
