use crate::iter::InPlaceIterable;
use crate::num::NonZero;
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};

mod array_chunks;
mod by_ref_sized;
mod chain;
mod cloned;
mod copied;
mod cycle;
mod enumerate;
mod filter;
mod filter_map;
mod flatten;
mod fuse;
mod inspect;
mod intersperse;
mod map;
mod map_while;
mod map_windows;
mod peekable;
mod rev;
mod scan;
mod skip;
mod skip_while;
mod step_by;
mod take;
mod take_while;
mod zip;

#[stable(feature = "rust1", since = "1.0.0")]
pub use self::{
    chain::Chain, cycle::Cycle, enumerate::Enumerate, filter::Filter, filter_map::FilterMap,
    flatten::FlatMap, fuse::Fuse, inspect::Inspect, map::Map, peekable::Peekable, rev::Rev,
    scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip,
};

#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
pub use self::array_chunks::ArrayChunks;

#[unstable(feature = "std_internals", issue = "none")]
pub use self::by_ref_sized::ByRefSized;

#[stable(feature = "iter_cloned", since = "1.1.0")]
pub use self::cloned::Cloned;

#[stable(feature = "iterator_step_by", since = "1.28.0")]
pub use self::step_by::StepBy;

#[stable(feature = "iterator_flatten", since = "1.29.0")]
pub use self::flatten::Flatten;

#[stable(feature = "iter_copied", since = "1.36.0")]
pub use self::copied::Copied;

#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
pub use self::intersperse::{Intersperse, IntersperseWith};

#[stable(feature = "iter_map_while", since = "1.57.0")]
pub use self::map_while::MapWhile;

#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")]
pub use self::map_windows::MapWindows;

#[unstable(feature = "trusted_random_access", issue = "none")]
pub use self::zip::TrustedRandomAccess;

#[unstable(feature = "trusted_random_access", issue = "none")]
pub use self::zip::TrustedRandomAccessNoCoerce;

#[stable(feature = "iter_zip", since = "1.59.0")]
pub use self::zip::zip;

/// This trait provides transitive access to source-stage in an iterator-adapter pipeline
/// under the conditions that
/// * the iterator source `S` itself implements `SourceIter<Source = S>`
/// * there is a delegating implementation of this trait for each adapter in the pipeline between
///   the source and the pipeline consumer.
///
/// When the source is an owning iterator struct (commonly called `IntoIter`) then
/// this can be useful for specializing [`FromIterator`] implementations or recovering the
/// remaining elements after an iterator has been partially exhausted.
///
/// Note that implementations do not necessarily have to provide access to the inner-most
/// source of a pipeline. A stateful intermediate adapter might eagerly evaluate a part
/// of the pipeline and expose its internal storage as source.
///
/// The trait is unsafe because implementers must uphold additional safety properties.
/// See [`as_inner`] for details.
///
/// The primary use of this trait is in-place iteration. Refer to the [`vec::in_place_collect`]
/// module documentation for more information.
///
/// [`vec::in_place_collect`]: ../../../../alloc/vec/in_place_collect/index.html
///
/// # Examples
///
/// Retrieving a partially consumed source:
///
/// ```
/// # #![feature(inplace_iteration)]
/// # use std::iter::SourceIter;
///
/// let mut iter = vec![9, 9, 9].into_iter().map(|i| i * i);
/// let _ = iter.next();
/// let mut remainder = std::mem::replace(unsafe { iter.as_inner() }, Vec::new().into_iter());
/// println!("n = {} elements remaining", remainder.len());
/// ```
///
/// [`FromIterator`]: crate::iter::FromIterator
/// [`as_inner`]: SourceIter::as_inner
#[unstable(issue = "none", feature = "inplace_iteration")]
#[doc(hidden)]
#[rustc_specialization_trait]
pub unsafe trait SourceIter {
    /// A source stage in an iterator pipeline.
    type Source;

    /// Retrieve the source of an iterator pipeline.
    ///
    /// # Safety
    ///
    /// Implementations must return the same mutable reference for their lifetime, unless
    /// replaced by a caller.
    ///
    /// Callers may only replace the reference when they stopped iteration and drop the
    /// iterator pipeline after extracting the source.
    ///
    /// This means iterator adapters can rely on the source not changing during
    /// iteration but they cannot rely on it in their Drop implementations.
    ///
    /// Implementing this method means adapters relinquish private-only access to their
    /// source and can only rely on guarantees made based on method receiver types.
    /// The lack of restricted access also requires that adapters must uphold the source's
    /// public API even when they have access to its internals.
    ///
    /// Callers in turn must expect the source to be in any state that is consistent with
    /// its public API since adapters sitting between it and the source have the same
    /// access. In particular an adapter may have consumed more elements than strictly necessary.
    ///
    /// The overall goal of these requirements is to let the consumer of a pipeline use
    /// * whatever remains in the source after iteration has stopped
    /// * the memory that has become unused by advancing a consuming iterator
    ///
    /// [`next()`]: Iterator::next()
    unsafe fn as_inner(&mut self) -> &mut Self::Source;
}

/// An iterator adapter that produces output as long as the underlying
/// iterator produces values where `Try::branch` says to `ControlFlow::Continue`.
///
/// If a `ControlFlow::Break` is encountered, the iterator stops and the
/// residual is stored.
pub(crate) struct GenericShunt<'a, I, R> {
    iter: I,
    residual: &'a mut Option<R>,
}

/// Process the given iterator as if it yielded a the item's `Try::Output`
/// type instead. Any `Try::Residual`s encountered will stop the inner iterator
/// and be propagated back to the overall result.
pub(crate) fn try_process<I, T, R, F, U>(iter: I, mut f: F) -> ChangeOutputType<I::Item, U>
where
    I: Iterator<Item: Try<Output = T, Residual = R>>,
    for<'a> F: FnMut(GenericShunt<'a, I, R>) -> U,
    R: Residual<U>,
{
    let mut residual = None;
    let shunt = GenericShunt { iter, residual: &mut residual };
    let value = f(shunt);
    match residual {
        Some(r) => FromResidual::from_residual(r),
        None => Try::from_output(value),
    }
}

impl<I, R> Iterator for GenericShunt<'_, I, R>
where
    I: Iterator<Item: Try<Residual = R>>,
{
    type Item = <I::Item as Try>::Output;

    fn next(&mut self) -> Option<Self::Item> {
        self.try_for_each(ControlFlow::Break).break_value()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.residual.is_some() {
            (0, Some(0))
        } else {
            let (_, upper) = self.iter.size_hint();
            (0, upper)
        }
    }

    fn try_fold<B, F, T>(&mut self, init: B, mut f: F) -> T
    where
        F: FnMut(B, Self::Item) -> T,
        T: Try<Output = B>,
    {
        self.iter
            .try_fold(init, |acc, x| match Try::branch(x) {
                ControlFlow::Continue(x) => ControlFlow::from_try(f(acc, x)),
                ControlFlow::Break(r) => {
                    *self.residual = Some(r);
                    ControlFlow::Break(try { acc })
                }
            })
            .into_try()
    }

    impl_fold_via_try_fold! { fold -> try_fold }
}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, R> SourceIter for GenericShunt<'_, I, R>
where
    I: SourceIter,
{
    type Source = I::Source;

    #[inline]
    unsafe fn as_inner(&mut self) -> &mut Self::Source {
        // SAFETY: unsafe function forwarding to unsafe function with the same requirements
        unsafe { SourceIter::as_inner(&mut self.iter) }
    }
}

// SAFETY: GenericShunt::next calls `I::try_for_each`, which has to advance `iter`
// in order to return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's
// guaranteed that at least one item will be moved out from the underlying source.
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, R> InPlaceIterable for GenericShunt<'_, I, R>
where
    I: InPlaceIterable,
{
    const EXPAND_BY: Option<NonZero<usize>> = I::EXPAND_BY;
    const MERGE_BY: Option<NonZero<usize>> = I::MERGE_BY;
}
