//!
//! Handle for recoverable process
use crate::proc_data::ProcData;
use crate::proc_handle::ProcHandle;
use crate::state::State;
use std::any::Any;
use std::fmt::{self, Debug, Formatter};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::thread;

/// Recoverable handle which encapsulates a standard Proc Handle and contain all panics inside.
///
/// Execution of `after_panic` will be immediate on polling the [RecoverableHandle]'s future.
pub struct RecoverableHandle<R> {
    inner: ProcHandle<thread::Result<R>>,

    /// Panic callback
    ///
    /// This callback will be called if the interior future panics. It is passed the panic
    // reason i.e. the `Err` of [`std::thread::Result`]
    panicked: Option<Box<dyn FnOnce(Box<dyn Any + Send>) + Send + Sync>>,
}

impl<R> RecoverableHandle<R> {
    pub(crate) fn new(inner: ProcHandle<thread::Result<R>>) -> Self {
        RecoverableHandle {
            inner,
            panicked: None,
        }
    }

    /// Cancels the proc.
    ///
    /// If the proc has already completed, calling this method will have no effect.
    ///
    /// When a proc is cancelled, its future cannot be polled again and will be dropped instead.
    pub fn cancel(&self) {
        self.inner.cancel()
    }

    /// Returns a state of the ProcHandle.
    pub fn state(&self) -> State {
        self.inner.state()
    }

    /// Adds a callback that will be executed should the inner future `panic!`s
    ///
    /// ```rust
    /// # use std::any::Any;
    /// # use tracing::Span;
    /// # use lightproc::prelude::*;
    /// #
    /// # // ... future that does work
    /// # let future = async {
    /// #     println!("Doing some work");
    /// # };
    /// #
    /// # // ... basic schedule function with no waker logic
    /// # fn schedule_function(proc: LightProc) {;}
    /// #
    /// // ... creating a recoverable process
    /// let (proc, recoverable) = LightProc::recoverable(
    ///     future,
    ///     schedule_function,
    ///     Span::current(),
    ///     None
    /// );
    ///
    /// recoverable
    ///     .on_panic(|_e: Box<dyn Any + Send>| {
    ///         println!("Inner future panicked");
    ///     });
    /// ```
    pub fn on_panic<F>(mut self, callback: F) -> Self
    where
        F: FnOnce(Box<dyn Any + Send>) + Send + Sync + 'static,
    {
        self.panicked = Some(Box::new(callback));
        self
    }
}

impl<R> Future for RecoverableHandle<R> {
    type Output = Option<R>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        match Pin::new(&mut self.inner).poll(cx) {
            Poll::Pending => Poll::Pending,
            Poll::Ready(None) => Poll::Ready(None),
            Poll::Ready(Some(Ok(val))) => Poll::Ready(Some(val)),
            Poll::Ready(Some(Err(e))) => {
                if let Some(callback) = self.panicked.take() {
                    callback(e);
                }

                Poll::Ready(None)
            }
        }
    }
}

impl<R> Debug for RecoverableHandle<R> {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        let ptr = self.inner.raw_proc.as_ptr();
        let pdata = ptr as *const ProcData;

        fmt.debug_struct("ProcHandle")
            .field("pdata", unsafe { &(*pdata) })
            .finish_non_exhaustive()
    }
}