// Copyright (c) 2021 The Lutino Projects
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

//! Queries provide efficient iteration and filtering of entity components in a world.
//!
//! Queries are defined by two parts; "views" and "filters".
//! Views declare what data you want to access, and how you want to access it.
//! Filters decide which entities are to be included in the results.
//!
//! To construct a query, we declare our view, and then call `::query()` to convert it into
//! a query with an initial filter which selects entities with all of the component types
//! requested by the view.
//!
//! View types include [`Entity`](super::world::Entity), [`Read`], [`Write`], [`TryRead`]
//! and [`TryWrite`].
//!
//! ```
//! # use nh_ecs::*;
//! # struct Position;
//! # struct Orientation;
//! // a view can be a single view type
//! let mut query = <&Position>::query();
//!
//! // or a tuple of views
//! let mut query = <(&Position, &mut Orientation)>::query();
//! ```
//!
//! You can attach additional filters to a query to further refine which entities you want to
//! access.
//!
//! ```
//! # use nh_ecs::*;
//! # struct Position;
//! # struct Orientation;
//! # struct Model;
//! # struct Static;
//!
//! // filters can be combined with boolean operators
//! // let mut query = <(&Position, &mut Orientation)>::query().filter(
//! //   !component::<Static>() | !component::<Model>());
//! ```
//!
//! Once you have a query, you can use it to pull data out of a world. At its core, a query
//! allows you to iterate over [chunks](ChunkView). Each chunk contains a set of entities which
//! all have exactly the same component types attached, and the chunk provides access to slices
//! of each component. A single index in each slice in a chunk contains the component for the
//! same entity.
//!
//! ```
//! # use nh_ecs::*;
//! # struct Position;
//! # struct Orientation;
//! # let mut world = World::default();
//! let mut query = <(&Position, &mut Orientation)>::query();
//! for mut chunk in query.iter_chunks_mut(&mut world) {
//!     // we can access information about the archetype (shape/component layout) of the entities
//!     println!(
//!         "the entities in the chunk have {:?} components",
//!         chunk.archetype().layout().component_types(),
//!     );
//!
//!     // we can iterate through a tuple of component references
//!     for (position, orientation) in chunk {
//!         // position is a `&Position`
//!         // orientation is a `&mut Orientation`
//!         // they are both attached to the same entity
//!     }
//! }
//! ```
//!
//! There are convenience functions on query which will flatten this loop for us, giving
//! direct access to the entities.
//!
//! ```
//! # use nh_ecs::*;
//! # struct Position;
//! # struct Orientation;
//! # let mut world = World::default();
//! let mut query = <(&Position, &mut Orientation)>::query();
//! for (position, orientation) in query.iter_mut(&mut world) {
//!     // position is a `&Position`
//!     // orientation is a `&mut Orientation`
//!     // they are both attached to the same entity
//! }
//! ```

pub mod chunk;
pub mod filter;
pub mod par_chunk;
pub mod view;

use std::collections::HashMap;
use std::marker::PhantomData;
use std::ops::Range;
use std::slice::Iter;

use filter::{DynamicFilter, EntityFilter, GroupMatcher};
use nh_job_system::iter::{ParallelFlatten, ParallelIterator};
use nh_job_system::JobSystem;
use parking_lot::Mutex;

use crate::entity::Entity;
use crate::iter::indexed::TrustedRandomAccess;
use crate::permissions::Permissions;
use crate::query::chunk::{ChunkIter, ChunkView};
use crate::query::par_chunk::ParChunkIter;
use crate::query::view::{DefaultFilter, Fetch, IntoView, ReadOnlyFetch, View};
use crate::storage::archetype::ArchetypeIndex;
use crate::storage::component::ComponentTypeId;
use crate::storage::group::SubGroup;
use crate::world::{EntityAccessError, EntityStore, StorageAccessor, WorldId};

/// A type (typically a view) which can construct a query.
pub trait IntoQuery: IntoView + Sized {
    /// Constructs a query.
    fn query() -> Query<Self, <Self::View as DefaultFilter>::Filter>;
}

impl<T: IntoView> IntoQuery for T {
    fn query() -> Query<Self, <Self::View as DefaultFilter>::Filter> {
        Self::View::validate();

        Query {
            _view: PhantomData,
            filter: Mutex::new(<<Self::View as DefaultFilter>::Filter as Default>::default()),
            layout_matches: HashMap::new(),
            is_view_filter: true,
        }
    }
}

/// Contains the result of an entity layout filter.
#[derive(Debug, Clone)]
pub struct QueryResult<'a> {
    index: &'a [ArchetypeIndex],
    range: Range<usize>,
    is_ordered: bool,
}

impl<'a> QueryResult<'a> {
    /// The query should access the following archetypes via direct indexing.
    fn unordered(index: &'a [ArchetypeIndex]) -> Self {
        Self {
            range: 0..index.len(),
            index,
            is_ordered: false,
        }
    }

    /// The query should access the following archetypes, and storage can be
    /// assumed to have stored components in the same order as given.
    fn ordered(index: &'a [ArchetypeIndex]) -> Self {
        Self {
            range: 0..index.len(),
            index,
            is_ordered: true,
        }
    }

    /// Gets the archetype index containing the archetypes which match the filter.
    pub fn index(&self) -> &'a [ArchetypeIndex] {
        let (_, slice) = self.index.split_at(self.range.start);
        let (slice, _) = slice.split_at(self.range.len());
        slice
    }

    /// The sub-range of archetypes which should be accessed.
    pub fn range(&self) -> &Range<usize> { &self.range }

    /// Returns `true` if components can be assumed to be stored in the same order as given.
    pub fn is_ordered(&self) -> bool { self.is_ordered }

    /// The number of archetypes that matches the filter.
    pub fn len(&self) -> usize { self.range.len() }

    /// Returns `true` if no archetypes matched the filter.
    pub fn is_empty(&self) -> bool { self.index().is_empty() }

    pub(crate) fn split_at(self, index: usize) -> (Self, Self) {
        let index = self.range.start + index;
        (
            Self {
                range: self.range.start..index,
                index: self.index,
                is_ordered: self.is_ordered,
            },
            Self {
                range: index..self.range.end,
                index: self.index,
                is_ordered: self.is_ordered,
            },
        )
    }
}

#[derive(Debug, Clone)]
enum Cache {
    Unordered {
        archetypes: Vec<ArchetypeIndex>,
        seen: usize,
    },
    Ordered {
        group: usize,
        subgroup: SubGroup,
    },
}

/// Provides efficient means to iterate and filter entities in a world.
///
/// See the [module-level documentation](./index.html) for more details and examples.
pub struct Query<V: IntoView, F: EntityFilter = <<V as IntoView>::View as DefaultFilter>::Filter> {
    _view: PhantomData<V>,
    filter: Mutex<F>,
    layout_matches: HashMap<WorldId, Cache>,
    is_view_filter: bool,
}

impl<V: IntoView, F: EntityFilter> Default for Query<V, F> {
    fn default() -> Self { Self::new() }
}

impl<V: IntoView, F: EntityFilter> Query<V, F> {
    /// Consructs a new Query
    pub fn new() -> Self {
        V::View::validate();

        Self {
            _view: PhantomData,
            filter: Mutex::new(Default::default()),
            layout_matches: HashMap::new(),
            is_view_filter: true,
        }
    }

    pub fn get_permission(&self) -> Permissions<ComponentTypeId> { V::View::requires_permissions() }

    /// Adds an additional filter to the query.
    pub fn filter<T: EntityFilter>(self, filter: T) -> Query<V, <F as std::ops::BitAnd<T>>::Output>
    where
        F: std::ops::BitAnd<T>,
        <F as std::ops::BitAnd<T>>::Output: EntityFilter,
    {
        Query {
            _view: self._view,
            filter: Mutex::new(self.filter.into_inner() & filter),
            layout_matches: HashMap::default(),
            is_view_filter: false,
        }
    }

    // ----------------
    // Query Execution
    // ----------------

    fn validate_archetype_access(storage: &StorageAccessor<'_>, archetypes: &[ArchetypeIndex]) {
        for arch in archetypes {
            if !storage.can_access_archetype(*arch) {
                panic!("query attempted to access archetype which not available in subworld");
            }
        }
    }

    fn evaluate_query<'a>(
        &'a mut self,
        world: &StorageAccessor<'a>,
    ) -> (&mut Mutex<F>, QueryResult<'a>) {
        // pull layout matches out of the cache
        let cache = self.layout_matches.entry(world.id()).or_insert_with(|| {
            // if the query can match a group, look to see if there is a subgroup we can use
            let cache = if F::can_match_group() {
                let components = F::group_components();
                components
                    .get(0)
                    .and_then(|t| world.group(*t))
                    .map(|(i, g)| (i, g.exact_match(&components)))
                    .and_then(|(group, subgroup)| {
                        subgroup.map(|subgroup| Cache::Ordered { group, subgroup })
                    })
            } else {
                None
            };

            // else use an unordered result
            cache.unwrap_or_else(|| Cache::Unordered {
                archetypes: Vec::new(),
                seen: 0,
            })
        });

        // iteratively update our layout matches
        let filter = self.filter.get_mut();
        let result = match cache {
            Cache::Unordered { archetypes, seen } => {
                // resume index search from where we last left off
                for archetype in world.layout_index().search_from(&*filter, *seen) {
                    archetypes.push(archetype);
                }
                *seen = world.archetypes().len();
                QueryResult::unordered(archetypes.as_slice())
            }
            Cache::Ordered { group, subgroup } => {
                // grab the archetype slice from the group
                let archetypes = &world.groups()[*group][*subgroup];
                QueryResult::ordered(archetypes)
            }
        };

        Self::validate_archetype_access(world, result.index());

        (&mut self.filter, result)
    }

    pub(crate) fn find_archetypes<'a, T: EntityStore + 'a>(
        &'a mut self,
        world: &'a T,
    ) -> &'a [ArchetypeIndex] {
        let accessor = world.get_component_storage::<V::View>().unwrap();
        let (_, result) = self.evaluate_query(&accessor);
        result.index()
    }

    // ----------------
    // Entity Indexing
    // ----------------

    /// Returns the components for a single entity.
    ///
    /// This function will not evaluate the query's dynamic filters. This means, for example, that
    /// calling `get` on all entities in an archetype will not prevent `maybe_changed` from
    /// returning those entities the next time the query is iterated.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    pub unsafe fn get_unchecked<'query, 'world, T>(
        &'query mut self,
        world: &'world T,
        entity: Entity,
    ) -> Result<<V::View as View<'world>>::Element, EntityAccessError>
    where
        T: EntityStore,
    {
        let location = world.entry_ref(entity)?.location();
        let accessor = world.get_component_storage::<V::View>().unwrap();

        // safety:
        // This is much like the similar usage of transmute inside iter_chunks_unchecked, see
        // there for more details. In this case, the situation is simpler; we know the return
        // value of this function references only data from the world and View::fetch can't
        // put the reference away somewhere invalid (because the param is still bound to 'world not
        // 'static), so the arch_slice on the stack won't stil be in use after it falls out of
        // scope.

        let arch_slice = [location.archetype()];
        let arch_slice_ref: &[ArchetypeIndex] = &arch_slice;
        let arch_slice_ref = std::mem::transmute(arch_slice_ref);
        let result = QueryResult::unordered(arch_slice_ref);
        let mut fetch = if let Some(Some(fetch)) =
            <V::View as View<'world>>::fetch(accessor.components(), accessor.archetypes(), result)
                .next()
        {
            fetch
        } else {
            return Err(EntityAccessError::EntityNotFound);
        };

        // if our filter has conditions beyond that of the view, then we need to evaluate the query
        if !self.is_view_filter {
            let (_, result) = self.evaluate_query(&accessor);
            if !result.index().contains(&location.archetype()) {
                return Err(EntityAccessError::AccessDenied);
            }
        }

        // accept the fetch to trigger version increments
        fetch.accepted();

        // construct a chunk view for the archetype, then index the entity we want
        let view = ChunkView::new(&accessor.archetypes()[location.archetype()], fetch);
        let mut iter = view.into_iter();
        Ok(iter.get_unchecked(location.component().0))
    }

    /// Returns the components for a single entity.
    pub fn get_mut<'query, 'world, T>(
        &'query mut self,
        world: &'world mut T,
        entity: Entity,
    ) -> Result<<V::View as View<'world>>::Element, EntityAccessError>
    where
        T: EntityStore,
    {
        // safety: we have exclusive access to world
        unsafe { self.get_unchecked(world, entity) }
    }

    /// Returns the components for a single entity.
    pub fn get<'query, 'world, T>(
        &'query mut self,
        world: &'world T,
        entity: Entity,
    ) -> Result<<V::View as View<'world>>::Element, EntityAccessError>
    where
        T: EntityStore,
        <V::View as View<'world>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.get_unchecked(world, entity) }
    }

    // ----------------
    // Chunk Iteration
    // ----------------

    /// Returns an iterator which will yield all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    pub unsafe fn iter_chunks_unchecked<'query, 'world, T: EntityStore>(
        &'query mut self,
        world: &'world T,
    ) -> ChunkIter<'world, 'query, V::View, F> {
        let accessor = world.get_component_storage::<V::View>().unwrap();
        let (_, result) = self.evaluate_query(&accessor);

        // What we want:
        // Iterators returned from this function should yield component references
        // which are bound to the world, not the query, but the iterator itself
        // borrows data from both; while the iterator is alive, both the query and
        // world are borrowed, but it should be possible to drop the iterator and query
        // and keep those component references around as long as the world is alive.
        //
        // The ChunkIter keeps these two lifetimes separate. It yields component references
        // with the 'world lifetime, and stores internal state with 'query.
        //
        // The problem:
        // The index used by the iterator, depending on the state of the query, might be
        // borrowing data from *either* the query or the world. Therefore the two lifetimes
        // "flow into" each other. This confuses rustc as it can't pick a lifetime.
        //
        // Solving this with safe code would require two lifetime HRTBs on View/Fetch with
        // constraints between them. You can't do this.
        //
        // The workaround:
        // We transmute the lifetime of the index data into either 'world or 'query
        // depending on what is needed. This essentially creates a virtual 'index lifetime,
        // which is coerced into whatever is needed. This virtual lifetime is valid where
        // *both* 'query and 'world lifetimes are valid.
        //
        // We know 'query and 'world are both valid in this fn, as they are input lifetimes.
        //
        // The ChunkIter contains both lifetimes, so the returned iterator struct will extend
        // those lifetimes for at least as long as it is in scope.
        //
        // The ChunkIter only returns (unmolested) 'world references. Our virtual lifetime
        // cannot leak out of it. When the iterator is dropped, our virtual lifetime dies with it.

        let result = std::mem::transmute::<QueryResult<'_>, QueryResult<'world>>(result);
        let indices = std::mem::transmute::<Iter<'_, ArchetypeIndex>, Iter<'query, ArchetypeIndex>>(
            result.index.iter(),
        );

        let fetch =
            <V::View as View<'world>>::fetch(accessor.components(), accessor.archetypes(), result);
        let filter = self.filter.get_mut();
        filter.prepare(world.id());

        ChunkIter {
            inner: fetch,
            filter,
            archetypes: accessor.archetypes(),
            max_count: indices.len(),
            indices,
        }
    }

    /// Returns a parallel iterator which will yield all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    pub unsafe fn par_iter_chunks_unchecked<'a, 'env, T: EntityStore>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
    ) -> ParChunkIter<'a, 'env, V::View, F> {
        let accessor = world.get_component_storage::<V::View>().unwrap();
        let (filter, result) = self.evaluate_query(&accessor);

        ParChunkIter::new(env, accessor, result, filter)
    }

    /// Returns an iterator which will yield all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    #[inline]
    pub fn iter_chunks_mut<'query, 'world, T: EntityStore>(
        &'query mut self,
        world: &'world mut T,
    ) -> ChunkIter<'world, 'query, V::View, F> {
        // safety: we have exclusive access to world
        unsafe { self.iter_chunks_unchecked(world) }
    }

    /// Returns a parallel iterator which will yield all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    #[inline]
    pub fn par_iter_chunks_mut<'a, 'env, T: EntityStore>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a mut T,
    ) -> ParChunkIter<'a, 'env, V::View, F> {
        // safety: we have exclusive access to world
        unsafe { self.par_iter_chunks_unchecked(env, world) }
    }

    /// Returns an iterator which will yield all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout. Only usable with queries whose views are read-only.
    #[inline]
    pub fn iter_chunks<'query, 'world, T: EntityStore>(
        &'query mut self,
        world: &'world T,
    ) -> ChunkIter<'world, 'query, V::View, F>
    where
        <V::View as View<'query>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.iter_chunks_unchecked(world) }
    }

    /// Returns a parallel iterator which will yield all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout. Only usable with queries whose views are read-only.
    #[inline]
    pub fn par_iter_chunks<'a, 'env, T: EntityStore>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
    ) -> ParChunkIter<'a, 'env, V::View, F>
    where
        <V::View as View<'a>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.par_iter_chunks_unchecked(env, world) }
    }

    // ----------------
    // Entity Iteration
    // ----------------

    /// Returns an iterator which will yield all components which match the query.
    ///
    /// Prefer `for_each_unchecked` as it offers better performance.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    #[inline]
    pub unsafe fn iter_unchecked<'query, 'world, T: EntityStore>(
        &'query mut self,
        world: &'world T,
    ) -> std::iter::Flatten<ChunkIter<'world, 'query, V::View, F>> {
        self.iter_chunks_unchecked(world).flatten()
    }

    /// Returns a parallel iterator which will yield all components which match the query.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    #[inline]
    pub unsafe fn par_iter_unchecked<'a, 'env, T: EntityStore>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
    ) -> ParallelFlatten<'env, ParChunkIter<'a, 'env, V::View, F>> {
        self.par_iter_chunks_unchecked(env, world).flatten()
    }

    /// Returns an iterator which will yield all components which match the query.
    ///
    /// Prefer `for_each_mut` as it yields better performance.
    #[inline]
    pub fn iter_mut<'query, 'world, T: EntityStore>(
        &'query mut self,
        world: &'world mut T,
    ) -> std::iter::Flatten<ChunkIter<'world, 'query, V::View, F>> {
        // safety: we have exclusive access to world
        unsafe { self.iter_unchecked(world) }
    }

    /// Returns a parallel iterator which will yield all components which match the query.
    #[inline]
    pub fn par_iter_mut<'a, 'env, T: EntityStore>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a mut T,
    ) -> ParallelFlatten<'env, ParChunkIter<'a, 'env, V::View, F>> {
        // safety: we have exclusive access to world
        unsafe { self.par_iter_unchecked(env, world) }
    }

    /// Returns an iterator which will yield all components which match the query.
    ///
    /// Prefer `for_each` as it yields better performance.  
    /// Only usable with queries whose views are read-only.
    #[inline]
    pub fn iter<'query, 'world, T: EntityStore>(
        &'query mut self,
        world: &'world T,
    ) -> std::iter::Flatten<ChunkIter<'world, 'query, V::View, F>>
    where
        <V::View as View<'world>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.iter_unchecked(world) }
    }

    /// Returns a parallel iterator which will yield all components which match the query.
    ///
    /// Only usable with queries whose views are read-only.
    #[inline]
    pub fn par_iter<'a, 'env, T: EntityStore>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
    ) -> ParallelFlatten<'env, ParChunkIter<'a, 'env, V::View, F>>
    where
        <V::View as View<'a>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.par_iter_unchecked(env, world) }
    }

    // ----------------
    // Chunk for-each
    // ----------------

    /// Iterates through all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    #[inline]
    pub unsafe fn for_each_chunk_unchecked<'query, 'world, T: EntityStore, Body>(
        &'query mut self,
        world: &'world T,
        mut f: Body,
    ) where
        Body: FnMut(ChunkView<'_, <V::View as View<'world>>::Fetch>),
    {
        for chunk in self.iter_chunks_unchecked(world) {
            f(chunk);
        }
    }

    /// Iterates in parallel through all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    #[inline]
    pub unsafe fn par_for_each_chunk_unchecked<'a, 'env, T: EntityStore, Body>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
        f: Body,
    ) where
        Body: Fn(ChunkView<'_, <V::View as View<'a>>::Fetch>) + Send + Sync,
    {
        self.par_iter_chunks_unchecked(env, world).for_each(f);
    }

    /// Iterates through all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    #[inline]
    pub fn for_each_chunk_mut<'query, 'world, T: EntityStore, Body>(
        &'query mut self,
        world: &'world mut T,
        f: Body,
    ) where
        Body: FnMut(ChunkView<'_, <V::View as View<'world>>::Fetch>),
    {
        // safety: we have exclusive access to world
        unsafe { self.for_each_chunk_unchecked(world, f) };
    }

    /// Iterates in parallel through all entity chunks which match the query.
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout.
    #[inline]
    pub fn par_for_each_chunk_mut<'a, 'env, T: EntityStore, Body>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a mut T,
        f: Body,
    ) where
        Body: Fn(ChunkView<'_, <V::View as View<'a>>::Fetch>) + Send + Sync,
    {
        // safety: we have exclusive access to world
        unsafe { self.par_for_each_chunk_unchecked(env, world, f) };
    }

    /// Iterates through all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout. Only usable with queries whose views are read-only.
    #[inline]
    pub fn for_each_chunk<'query, 'world, T: EntityStore, Body>(
        &'query mut self,
        world: &'world T,
        f: Body,
    ) where
        Body: FnMut(ChunkView<'_, <V::View as View<'world>>::Fetch>),
        <V::View as View<'world>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.for_each_chunk_unchecked(world, f) };
    }

    /// Iterates in parallel through all entity chunks which match the query.
    ///
    /// Each chunk contains slices of components for entities which all have the same component
    /// layout. Only usable with queries whose views are read-only.
    #[inline]
    pub fn par_for_each_chunk<'a, 'env, T: EntityStore, Body>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
        f: Body,
    ) where
        Body: Fn(ChunkView<'_, <V::View as View<'a>>::Fetch>) + Send + Sync,
        <V::View as View<'a>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.par_for_each_chunk_unchecked(env, world, f) };
    }

    // ----------------
    // Entity for-each
    // ----------------

    /// Iterates through all components which match the query.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    #[inline]
    pub unsafe fn for_each_unchecked<'query, 'world, T: EntityStore, Body>(
        &'query mut self,
        world: &'world T,
        mut f: Body,
    ) where
        Body: FnMut(<V::View as View<'world>>::Element),
    {
        // we use a nested loop because it is significantly faster than .flatten()
        for chunk in self.iter_chunks_unchecked(world) {
            for entities in chunk {
                f(entities);
            }
        }
    }

    /// Iterates in parallel through all components which match the query.
    ///
    /// # Safety
    /// This function allows mutable access via a shared world reference. The caller is responsible
    /// for ensuring that no component accesses may create mutable aliases.
    #[inline]
    pub unsafe fn par_for_each_unchecked<'a, 'env, T: EntityStore, Body>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
        f: Body,
    ) where
        Body: Fn(<V::View as View<'a>>::Element) + Send + Sync,
    {
        self.par_iter_unchecked(env, world).for_each(&f);
    }

    /// Iterates through all components which match the query.
    #[inline]
    pub fn for_each_mut<'query, 'world, T: EntityStore, Body>(
        &'query mut self,
        world: &'world mut T,
        f: Body,
    ) where
        Body: FnMut(<V::View as View<'world>>::Element),
    {
        // safety: we have exclusive access to world
        unsafe { self.for_each_unchecked(world, f) };
    }

    /// Iterates in parallel through all components which match the query.
    #[inline]
    pub fn par_for_each_mut<'a, 'env, T: EntityStore, Body>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a mut T,
        f: Body,
    ) where
        Body: Fn(<V::View as View<'a>>::Element) + Send + Sync,
    {
        // safety: we have exclusive access to world
        unsafe { self.par_for_each_unchecked(env, world, f) };
    }

    /// Iterates through all components which match the query.
    ///
    /// Only usable with queries whose views are read-only.
    #[inline]
    pub fn for_each<'query, 'world, T: EntityStore, Body>(
        &'query mut self,
        world: &'world T,
        f: Body,
    ) where
        Body: FnMut(<V::View as View<'world>>::Element),
        <V::View as View<'world>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.for_each_unchecked(world, f) };
    }

    /// Iterates in parallel through all components which match the query.
    ///
    /// Only usable with queries whose views are read-only.
    #[inline]
    pub fn par_for_each<'a, 'env, T: EntityStore, Body>(
        &'a mut self,
        env: &'env JobSystem,
        world: &'a T,
        f: Body,
    ) where
        Body: Fn(<V::View as View<'a>>::Element) + Send + Sync,
        <V::View as View<'a>>::Fetch: ReadOnlyFetch,
    {
        // safety: the view is readonly - it cannot create mutable aliases
        unsafe { self.par_for_each_unchecked(env, world, f) };
    }
}
