use core::future::Future;
cfg_time!(
  use core::time::Duration;
);
use crate::Yielder;
#[cfg(any(feature = "smol", feature = "async-std"))]
macro_rules! join_handle {
  ($handle:ty) => {
    pin_project_lite::pin_project! {
      pub struct JoinHandle<T> {
        #[pin]
        handle: $handle,
      }
    }
    impl<T> From<$handle> for JoinHandle<T> {
      fn from(handle: $handle) -> Self {
        Self { handle }
      }
    }
    impl<T> core::future::Future for JoinHandle<T> {
      type Output = core::result::Result<T, $crate::spawner::handle::JoinError>;
      fn poll(
        self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
      ) -> core::task::Poll<Self::Output> {
        let this = self.project();
        match this.handle.poll(cx) {
          core::task::Poll::Ready(v) => core::task::Poll::Ready(Ok(v)),
          core::task::Poll::Pending => core::task::Poll::Pending,
        }
      }
    }
  };
}
pub(crate) mod handle {
  #[derive(Debug, Clone, PartialEq, Eq)]
  pub struct JoinError(());
  impl JoinError {
    #[inline]
    #[cfg(feature = "wasm")]
    pub(crate) const fn new() -> Self {
      Self(())
    }
  }
  impl core::fmt::Display for JoinError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
      write!(f, "task failed to execute to completion")
    }
  }
  impl core::error::Error for JoinError {}
  #[cfg(feature = "std")]
  impl From<JoinError> for std::io::Error {
    fn from(_: JoinError) -> Self {
      std::io::Error::new(std::io::ErrorKind::Other, "join error")
    }
  }
}
pub trait JoinHandle<O>: Future<Output = Result<O, Self::JoinError>> + Unpin {
  #[cfg(feature = "std")]
  type JoinError: core::error::Error + Into<std::io::Error> + Send + Sync + 'static;
  #[cfg(not(feature = "std"))]
  type JoinError: core::error::Error + Send + Sync + 'static;
  fn detach(self)
  where
    Self: Sized,
  {
    drop(self)
  }
}
pub trait LocalJoinHandle<O>: Future<Output = Result<O, Self::JoinError>> + Unpin {
  #[cfg(feature = "std")]
  type JoinError: core::error::Error + Into<std::io::Error> + 'static;
  #[cfg(not(feature = "std"))]
  type JoinError: core::error::Error + 'static;
  fn detach(self)
  where
    Self: Sized,
  {
    drop(self)
  }
}
pub trait AsyncSpawner: Yielder + Copy + Send + Sync + 'static {
  type JoinHandle<O>: JoinHandle<O> + Send + Sync + 'static
  where
    O: Send + 'static;
  fn spawn<F>(future: F) -> Self::JoinHandle<F::Output>
  where
    F::Output: Send + 'static,
    F: Future + Send + 'static;
  fn spawn_detach<F>(future: F)
  where
    F::Output: Send + 'static,
    F: Future + Send + 'static,
  {
    core::mem::drop(Self::spawn(future));
  }
}
pub trait AsyncLocalSpawner: Yielder + Copy + 'static {
  type JoinHandle<O>: LocalJoinHandle<O> + 'static
  where
    O: 'static;
  fn spawn_local<F>(future: F) -> Self::JoinHandle<F::Output>
  where
    F::Output: 'static,
    F: Future + 'static;
  fn spawn_local_detach<F>(future: F)
  where
    F::Output: 'static,
    F: Future + 'static,
  {
    core::mem::drop(Self::spawn_local(future));
  }
}
pub trait AsyncBlockingSpawner: Yielder + Copy + 'static {
  type JoinHandle<R>: JoinHandle<R> + Send + 'static
  where
    R: Send + 'static;
  fn spawn_blocking<F, R>(f: F) -> Self::JoinHandle<R>
  where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static;
  fn spawn_blocking_detach<F, R>(f: F)
  where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
  {
    Self::spawn_blocking(f).detach();
  }
}
#[derive(Debug, Clone, Copy)]
#[cfg(all(
  feature = "time",
  any(
    feature = "async-std",
    feature = "tokio",
    feature = "smol",
    feature = "wasm"
  )
))]
pub(crate) struct Canceled;
#[cfg(all(
  feature = "time",
  any(
    feature = "async-std",
    feature = "tokio",
    feature = "smol",
    feature = "wasm"
  )
))]
impl core::fmt::Display for Canceled {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    write!(f, "after canceled")
  }
}
#[cfg(all(
  feature = "time",
  any(
    feature = "async-std",
    feature = "tokio",
    feature = "smol",
    feature = "wasm"
  )
))]
impl core::error::Error for Canceled {}
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
#[derive(Debug)]
pub enum AfterHandleError<E> {
  Canceled,
  Join(E),
}
#[cfg(feature = "time")]
impl<E: core::fmt::Display> core::fmt::Display for AfterHandleError<E> {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    match self {
      Self::Canceled => write!(f, "after function was canceled"),
      Self::Join(e) => write!(f, "{e}"),
    }
  }
}
#[cfg(feature = "time")]
impl<E: core::error::Error> core::error::Error for AfterHandleError<E> {}
#[cfg(all(feature = "time", feature = "std"))]
impl<E: core::error::Error + Send + Sync + 'static> From<AfterHandleError<E>> for std::io::Error {
  fn from(value: AfterHandleError<E>) -> Self {
    match value {
      AfterHandleError::Canceled => std::io::Error::new(std::io::ErrorKind::Other, "task canceled"),
      AfterHandleError::Join(e) => std::io::Error::new(std::io::ErrorKind::Other, e),
    }
  }
}
#[cfg(all(
  feature = "time",
  any(
    feature = "async-std",
    feature = "tokio",
    feature = "smol",
    feature = "wasm"
  )
))]
pub(crate) struct AfterHandleSignals {
  finished: core::sync::atomic::AtomicBool,
  expired: core::sync::atomic::AtomicBool,
}
#[cfg(all(
  feature = "time",
  any(
    feature = "async-std",
    feature = "tokio",
    feature = "smol",
    feature = "wasm"
  )
))]
impl AfterHandleSignals {
  #[inline]
  pub(crate) const fn new() -> Self {
    Self {
      finished: core::sync::atomic::AtomicBool::new(false),
      expired: core::sync::atomic::AtomicBool::new(false),
    }
  }
  #[inline]
  pub(crate) fn set_finished(&self) {
    self
      .finished
      .store(true, core::sync::atomic::Ordering::Release);
  }
  #[inline]
  pub(crate) fn set_expired(&self) {
    self
      .expired
      .store(true, core::sync::atomic::Ordering::Release);
  }
  #[inline]
  pub(crate) fn is_finished(&self) -> bool {
    self.finished.load(core::sync::atomic::Ordering::Acquire)
  }
  #[inline]
  pub(crate) fn is_expired(&self) -> bool {
    self.expired.load(core::sync::atomic::Ordering::Acquire)
  }
}
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
pub trait AfterHandle<F: Send + 'static>:
  Send + Sync + Future<Output = Result<F, Self::JoinError>> + 'static
{
  #[cfg(feature = "std")]
  type JoinError: core::error::Error + Into<std::io::Error> + Send + Sync + 'static;
  #[cfg(not(feature = "std"))]
  type JoinError: core::error::Error + 'static;
  fn cancel(self) -> impl Future<Output = Option<Result<F, Self::JoinError>>> + Send;
  fn reset(&self, duration: core::time::Duration);
  fn abort(self);
  fn detach(self)
  where
    Self: Sized,
  {
    drop(self)
  }
  fn is_expired(&self) -> bool;
  fn is_finished(&self) -> bool;
}
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
pub trait AsyncAfterSpawner: Copy + Send + Sync + 'static {
  type Instant: crate::time::Instant;
  type JoinHandle<F>: AfterHandle<F>
  where
    F: Send + 'static;
  fn spawn_after<F>(duration: Duration, future: F) -> Self::JoinHandle<F::Output>
  where
    F::Output: Send + 'static,
    F: Future + Send + 'static;
  fn spawn_after_detach<F>(duration: Duration, future: F)
  where
    F::Output: Send + 'static,
    F: Future + Send + 'static,
  {
    core::mem::drop(Self::spawn_after(duration, future));
  }
  fn spawn_after_at<F>(instant: Self::Instant, future: F) -> Self::JoinHandle<F::Output>
  where
    F::Output: Send + 'static,
    F: Future + Send + 'static;
  fn spawn_after_at_detach<F>(instant: Self::Instant, future: F)
  where
    F::Output: Send + 'static,
    F: Future + Send + 'static,
  {
    Self::spawn_after_at(instant, future).detach()
  }
}