use std::sync::Arc;

use crate::audio::sources::metronome;
use crate::audio::sources::mixer;
use crate::audio::sources::synthesizer;

use super::helpers::Tracked;

pub struct MenuModel {
    audio_ctx: Arc<crate::AudioContext>,

    pub pressed_fade_in_enabled: Tracked<bool>,
    pub pressed_fade_in_duration: Tracked<f32>,

    pub pressed_fade_out_enabled: Tracked<bool>,
    pub pressed_fade_out_duration: Tracked<f32>,

    pub released_fade_out_enabled: Tracked<bool>,
    pub released_fade_out_duration: Tracked<f32>,

    pub metronome_enabled: Tracked<bool>,
    pub metronome_meter: Tracked<metronome::Meter>,
    pub metronome_bpm: Tracked<u32>,
}

impl MenuModel {
    pub fn new(audio_ctx: Arc<crate::AudioContext>) -> Self {
        Self {
            audio_ctx,

            pressed_fade_in_enabled: Tracked::new(false),
            pressed_fade_in_duration: Tracked::new(0.0),

            pressed_fade_out_enabled: Tracked::new(true),
            pressed_fade_out_duration: Tracked::new(3.0),

            released_fade_out_enabled: Tracked::new(false),
            released_fade_out_duration: Tracked::new(0.1),

            metronome_enabled: Tracked::new(false),
            metronome_bpm: Tracked::new(metronome::DEFAULT_BPM),
            metronome_meter: Tracked::new(metronome::DEFAULT_METER),
        }
    }

    pub fn commit(&mut self) {
        if self.pressed_fade_in_enabled.is_updated()
            || self.pressed_fade_in_duration.is_updated()
            || self.pressed_fade_out_enabled.is_updated()
            || self.pressed_fade_out_duration.is_updated()
        {
            let fade_map = match (
                self.pressed_fade_in_enabled.get(),
                self.pressed_fade_out_enabled.get(),
            ) {
                (false, false) => synthesizer::amplitude::none(),
                (false, true) => {
                    synthesizer::amplitude::fade_out(*self.pressed_fade_out_duration.get())
                }
                (true, false) => {
                    synthesizer::amplitude::fade_in(*self.pressed_fade_in_duration.get())
                }
                (true, true) => synthesizer::amplitude::fade(
                    *self.pressed_fade_in_duration.get(),
                    *self.pressed_fade_out_duration.get(),
                ),
            };
            let message = synthesizer::Message::SetFadePressed(fade_map);
            if let Err(error) = self.audio_ctx.synthesizer.send(message) {
                eprintln!("Failed to send SetFadePressed message:\n{}", error);
            }
        }

        if self.released_fade_out_enabled.is_updated()
            || self.released_fade_out_duration.is_updated()
        {
            let fade_map = if *self.released_fade_out_enabled.get() {
                synthesizer::amplitude::fade(0.0, *self.released_fade_out_duration.get())
            } else {
                synthesizer::amplitude::none()
            };
            let message = synthesizer::Message::SetFadeReleased(fade_map);
            if let Err(error) = self.audio_ctx.synthesizer.send(message) {
                eprintln!("Failed to send SetFadeReleased message:\n{}", error);
            }
        }

        if let Some(&enabled) = self.metronome_enabled.get_if_updated() {
            let message = mixer::Message::SetInputEnabled((self.audio_ctx.metronome_id, enabled));
            if let Err(error) = self.audio_ctx.mixer.send(message) {
                eprintln!("Failed to send SetInputEnabled message:\n{}", error);
            }
        }

        if let Some(&bpm) = self.metronome_bpm.get_if_updated() {
            let message = metronome::Message::SetBPM(bpm);
            if let Err(error) = self.audio_ctx.metronome.send(message) {
                eprintln!("Failed to send SetBPM message:\n{}", error);
            }
        }

        if let Some(&meter) = self.metronome_meter.get_if_updated() {
            let message = metronome::Message::SetMeter(meter);
            if let Err(error) = self.audio_ctx.metronome.send(message) {
                eprintln!("Failed to send SetMeter message:\n{}", error);
            }
        }
    }
}
