use crossbeam_channel::{self,Sender,Receiver};

use super::AudioSource;

const CHANNEL_SIZE: usize = 256;

struct Input {
    source: Box<dyn AudioSource + Send>,
    enabled: bool,
    volume: f32,
}

pub enum Message {
    SetInputEnabled((usize,bool)),
    _SetInputVolume((usize,f32)),
}

pub type MixerHandle = Sender<Message>;

pub struct Mixer {
    inputs: Vec<Input>,
    messages: Receiver<Message>,
}

impl Mixer {
    pub fn new() -> (Mixer, MixerHandle) {
        let (tx, rx) = crossbeam_channel::bounded(CHANNEL_SIZE);
        let mixer = Mixer {
            inputs: Vec::new(),
            messages: rx,
        };
        (mixer, tx)
    }

    pub fn add(&mut self, source: Box<dyn AudioSource + Send>, enabled: bool, volume: f32) -> usize {
        // TODO: Use f32::clamp() once it's stable
        let volume = if volume > 1.0 { 1.0 } else if volume < 0.0 { 0.0 } else { volume };
        let input = Input {
            source,
            enabled,
            volume: volume,
        };
        self.inputs.push(input);
        self.inputs.len() - 1
    }
}


impl AudioSource for Mixer {
    fn get_sample(&mut self) -> f32 {
        self.inputs.iter_mut()
            .filter(|x| x.enabled)
            .map(|x| x.source.get_sample() * x.volume)
            .sum()
    }

    fn process_messages(&mut self) {
        match self.messages.try_recv() {
            Ok(Message::SetInputEnabled((idx,enabled))) => {
                self.inputs.get_mut(idx).unwrap().enabled = enabled;
            },

            Ok(Message::_SetInputVolume((idx,volume))) => {
                self.inputs.get_mut(idx).unwrap().volume = volume;
            }

            _ => {}
        }

        for input in self.inputs.iter_mut() {
            input.source.process_messages();
        }
    }
}