use crate::{
	frame::Frame,
	info::Info,
	track::{MainTrack, MainTrackBuilder, MainTrackHandle, SendTrack, Track},
};

use super::{
	ResourceController, ResourceStorage, clocks::Clocks, listeners::Listeners,
	modulators::Modulators,
};

pub(crate) struct Mixer {
	main_track: MainTrack,
	sub_tracks: ResourceStorage<Track>,
	send_tracks: ResourceStorage<SendTrack>,
	temp_buffer: Vec<Frame>,
}

impl Mixer {
	#[must_use]
	pub fn new(
		sub_track_capacity: usize,
		send_track_capacity: usize,
		sample_rate: u32,
		internal_buffer_size: usize,
		main_track_builder: MainTrackBuilder,
	) -> (
		Self,
		ResourceController<Track>,
		ResourceController<SendTrack>,
		MainTrackHandle,
	) {
		let (mut main_track, main_track_handle) = main_track_builder.build(internal_buffer_size);
		main_track.init_effects(sample_rate);
		let (sub_tracks, sub_track_controller) = ResourceStorage::new(sub_track_capacity);
		let (send_tracks, send_track_controller) = ResourceStorage::new(send_track_capacity);
		(
			Self {
				main_track,
				sub_tracks,
				send_tracks,
				temp_buffer: vec![Frame::ZERO; internal_buffer_size],
			},
			sub_track_controller,
			send_track_controller,
			main_track_handle,
		)
	}

	pub fn on_change_sample_rate(&mut self, sample_rate: u32) {
		self.main_track.on_change_sample_rate(sample_rate);
		for (_, track) in &mut self.sub_tracks {
			track.on_change_sample_rate(sample_rate);
		}
		for (_, track) in &mut self.send_tracks {
			track.on_change_sample_rate(sample_rate);
		}
	}

	pub fn on_start_processing(&mut self) {
		self.sub_tracks
			.remove_and_add(|track| track.should_be_removed());
		for (_, track) in &mut self.sub_tracks {
			track.on_start_processing();
		}
		self.send_tracks
			.remove_and_add(|track| track.shared().is_marked_for_removal());
		for (_, track) in &mut self.send_tracks {
			track.on_start_processing();
		}
		self.main_track.on_start_processing();
	}

	pub fn process(
		&mut self,
		out: &mut [Frame],
		dt: f64,
		clocks: &Clocks,
		modulators: &Modulators,
		listeners: &Listeners,
	) {
		for (_, track) in &mut self.sub_tracks {
			track.process(
				&mut self.temp_buffer[..out.len()],
				dt,
				clocks,
				modulators,
				listeners,
				None,
				&mut self.send_tracks,
			);
			for (summed_out, sound_out) in out.iter_mut().zip(self.temp_buffer.iter().copied()) {
				*summed_out += sound_out;
			}
			self.temp_buffer.fill(Frame::ZERO);
		}
		let info = Info::new(
			&clocks.0.resources,
			&modulators.0.resources,
			&listeners.0.resources,
			None,
		);
		for (_, track) in &mut self.send_tracks {
			track.process(&mut self.temp_buffer[..out.len()], dt, &info);
			for (summed_out, sound_out) in out.iter_mut().zip(self.temp_buffer.iter().copied()) {
				*summed_out += sound_out;
			}
			self.temp_buffer.fill(Frame::ZERO);
		}
		self.main_track.process(out, dt, &info);
	}
}
