use std::time::Duration;
use enum_dispatch::enum_dispatch;
use audio_processor_file::AudioFileProcessor;
use audio_processor_traits::AudioContext;
use augmented_adsr_envelope::Envelope;
use augmented_oscillator::Oscillator;
#[enum_dispatch(MetronomeSoundType)]
pub trait MetronomeSound {
    fn prepare(&mut self, context: &mut AudioContext);
    fn set_accent_beat(&mut self, is_accent: bool);
    fn trigger(&mut self);
    fn process(&mut self) -> f32;
}
#[enum_dispatch]
pub enum MetronomeSoundType {
    Sine(MetronomeSoundSine),
    File(MetronomeSoundFile),
}
impl MetronomeSoundType {
    pub fn file(file: AudioFileProcessor) -> Self {
        Self::File(MetronomeSoundFile {
            file_processor: file,
            envelope: crate::build_envelope(),
        })
    }
}
pub struct MetronomeSoundSine {
    oscillator: Oscillator<f32>,
    envelope: Envelope,
}
impl MetronomeSoundSine {
    pub fn new(sample_rate: f32, envelope: Envelope) -> Self {
        Self {
            oscillator: Oscillator::sine(sample_rate),
            envelope,
        }
    }
}
impl MetronomeSound for MetronomeSoundSine {
    fn prepare(&mut self, context: &mut AudioContext) {
        self.envelope
            .set_sample_rate(context.settings.sample_rate());
        self.oscillator
            .set_sample_rate(context.settings.sample_rate())
    }
    fn set_accent_beat(&mut self, is_accent: bool) {
        if is_accent {
            self.oscillator.set_frequency(880.0);
        } else {
            self.oscillator.set_frequency(440.0);
        }
    }
    fn trigger(&mut self) {
        self.envelope.note_on();
    }
    fn process(&mut self) -> f32 {
        let out = self.envelope.volume() * self.oscillator.get();
        self.envelope.tick();
        self.oscillator.tick();
        out
    }
}
pub struct MetronomeSoundFile {
    file_processor: AudioFileProcessor,
    envelope: Envelope,
}
impl MetronomeSound for MetronomeSoundFile {
    fn prepare(&mut self, context: &mut AudioContext) {
        audio_processor_traits::AudioProcessor::prepare(&mut self.file_processor, context);
        self.envelope
            .set_sample_rate(context.settings.sample_rate());
        self.envelope.set_decay(Duration::from_millis(50));
        self.file_processor.handle().stop();
        self.file_processor.handle().set_should_loop(false);
    }
    fn set_accent_beat(&mut self, _is_accent: bool) {}
    fn trigger(&mut self) {
        self.envelope.note_on();
        self.file_processor.handle().stop();
        self.file_processor.handle().play();
    }
    fn process(&mut self) -> f32 {
        let out = self.envelope.volume() * self.file_processor.process_single().sum::<f32>() / 2.0;
        self.envelope.tick();
        out
    }
}