//! Declares the most basic traits concerning [`Signals`](Signal).

use crate::{freq::Freq, sample::Sample};

/// A trait for a stream of data [`Samples`](Sample), generated every frame.
///
/// This data can either be audio data, meaning [`Mono`](crate::sample::Mono) or
/// [`Stereo`](crate::sample::Stereo), or envelope data
/// [`Env`](crate::sample::Env).
///
/// ## Implementing the trait
///
/// In order to implement this trait on your custom type, you only need to
/// define three methods:
///
/// - [`get`](Signal::get): Gets the next sample from the signal.
/// - [`advance`](Signal::advance): Advances the state of the signal by a frame.
/// - [`retrigger`](Signal::retrigger): Resets the signal to its initial state.
///
/// ## Example
///
/// The following example is a simplified implementation of
/// [`NoiseGen`](crate::prelude::NoiseGen).
///
/// ```
/// # use pointillism::prelude::*;
/// /// A signal that produces random envelope data.
/// struct NoiseGen {
///     /// The current random value.
///     current: Env,
/// }
///
/// impl Signal for NoiseGen {
///     // This signal produces envelope data.
///     type Sample = Env;
///
///     // Returns the current value.
///     fn get(&self) -> Env {
///         self.current
///     }
///
///     // Updates the current value.
///     fn advance(&mut self) {
///         self.current = Env::rand();
///     }
///     
///     // Retriggering a random signal amounts to choosing a new random value.
///     fn retrigger(&mut self) {         
///         self.current = Env::rand();
///     }
/// }
/// ```
pub trait Signal {
    /// The type of sample generated by the signal.
    type Sample: Sample;

    /// Gets the next sample from the signal.
    ///
    /// This should return the same result when called repeatedly, as long as
    /// the signal isn't modified, and `advance` or `retrigger` is not called.
    fn get(&self) -> Self::Sample;

    /// Advances the state of the signal by a frame.
    fn advance(&mut self);

    /// Resets the signal to its initial state.
    fn retrigger(&mut self);

    /// Gets the next sample and advances the state of the signal.
    fn next(&mut self) -> Self::Sample {
        let res = self.get();
        self.advance();
        res
    }
}

/// A trait for a signal with a "main" frequency.
///
/// Not to be confused with [`Freq`].
///
/// This is implemented both for signals that have a frequency parameter such as
/// [`LoopGen`](crate::generators::LoopGen), as well as straightforward wrappers
/// for these signals.
pub trait Frequency: Signal {
    /// The "main" frequency of the signal.
    fn freq(&self) -> Freq;

    /// A mutable reference to the "main" frequency of the signal.
    fn freq_mut(&mut self) -> &mut Freq;
}

/// A trait for a signal with a "base" signal.
///
/// This is often a generator in a chain of effects.
///
/// This is implemented both for basic signals that don't depend on others, as
/// well as straightforward wrappers of these.
pub trait Base: Signal {
    /// The "base" signal type.
    type Base: Signal;

    /// A reference to the "base" signal.
    fn base(&self) -> &Self::Base;

    /// A mutable reference to the "base" signal.
    fn base_mut(&mut self) -> &mut Self::Base;
}

/// Represents a signal that ends.
///
/// Is used in [`Polyphony`](crate::prelude::Polyphony) so that a synth can be
/// cleared from memory when it stops.
///
/// If a signal never ends, it should not implement this trait. If you really
/// want to use such a signal within a `Polyphony` object, wrap it in the
/// [`Trailing`](crate::prelude::Trailing) structure.
pub trait Done: Signal {
    /// Returns whether the signal has stopped producing any sound altogether.
    ///
    /// If this returns `true` once, it must return `true` in all successive
    /// times, unless retriggered. Further, if this returns `true`, then getting
    /// a sample from the signal must always return zero.
    fn is_done(&self) -> bool;
}

/// Represents a signal that can be stopped.
///
/// Note that stopping a signal doesn't mean it will immediately stop producing
/// sound. Use [`Panic`] for this purpose.
pub trait Stop: Signal {
    /// Releases a note.
    fn stop(&mut self);
}

/// Represents a signal that can be stopped abruptly.
///
/// All sound or envelope data will stop being produced once the `panic` method
/// is called.
///
/// Depending on how your code is structured, it might be easier to simply stop
/// calling `next` on your signal.
pub trait Panic: Signal {
    /// Stops all subsequent sound.
    fn panic(&mut self);
}
