//! Staged construction of a [`Debounce`].

use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use crate::{Debounce, Inner, Mode, State, Wait};

/// Configures a [`Debounce`] before it is built.
///
/// Returned by [`Debounce::latest`] and [`Debounce::accumulate`]. Set options
/// with the methods below, then call [`build`](Builder::build) to get the
/// wrapper. Every option has a default, so a bare `build` is valid.
///
/// [`Debounce::latest`]: crate::Debounce::latest
/// [`Debounce::accumulate`]: crate::Debounce::accumulate
pub struct Builder<A, T, E> {
    mode: Mode<A, T, E>,
    wait: Wait,
    leading: bool,
}

impl<A, T, E> fmt::Debug for Builder<A, T, E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Builder")
            .field("wait", &self.wait)
            .field("leading", &self.leading)
            .field("accumulate", &self.mode.is_accumulate())
            .finish_non_exhaustive()
    }
}

impl<A, T, E> Builder<A, T, E>
where
    A: Send + 'static,
    T: Clone + Send + Sync + 'static,
    E: Clone + Send + Sync + 'static,
{
    /// Wrap a calling convention with default options. Internal entry point used
    /// by the [`Debounce`] constructors.
    pub(crate) fn from_mode(mode: Mode<A, T, E>) -> Self {
        Builder {
            mode,
            wait: Wait::Fixed(Duration::ZERO),
            leading: false,
        }
    }

    /// Set the window length in milliseconds.
    pub fn wait_ms(mut self, ms: u64) -> Self {
        self.wait = Wait::Fixed(Duration::from_millis(ms));
        self
    }

    /// Set the window length as a [`Duration`].
    pub fn wait(mut self, wait: Duration) -> Self {
        self.wait = Wait::Fixed(wait);
        self
    }

    /// Read the window length from a closure on every call.
    ///
    /// The closure runs once per call to the debounced function, never cached.
    /// Use it for a wait that changes at runtime.
    pub fn wait_fn<F>(mut self, f: F) -> Self
    where
        F: Fn() -> Duration + Send + Sync + 'static,
    {
        self.wait = Wait::Dynamic(Arc::new(f));
        self
    }

    /// Run the function at the leading edge of each fresh window.
    ///
    /// The first call of a cold window runs the function at once and returns its
    /// result. Later calls in the same window join the trailing run. A burst of
    /// N calls runs the function up to twice: once leading, once trailing.
    pub fn leading(mut self, leading: bool) -> Self {
        self.leading = leading;
        self
    }

    /// Build the configured [`Debounce`].
    pub fn build(self) -> Debounce<A, T, E> {
        Debounce {
            inner: Arc::new(Inner {
                mode: self.mode,
                wait: self.wait,
                leading: self.leading,
                state: Mutex::new(State::new()),
            }),
        }
    }
}
