pub(crate) mod clocks;
pub(crate) mod listeners;
pub(crate) mod mixer;
pub(crate) mod modulators;

#[cfg(test)]
mod test;

use std::{
	fmt::{Debug, Formatter},
	sync::Mutex,
};

use crate::{
	ResourceLimitReached,
	listener::Listener,
	track::{MainTrackBuilder, MainTrackHandle, SendTrack, Track},
};
use atomic_arena::{Arena, Controller, Key};
use listeners::Listeners;
use rtrb::{Consumer, Producer, RingBuffer};

use crate::{clock::Clock, manager::Capacities, modulator::Modulator};

use self::{clocks::Clocks, mixer::Mixer, modulators::Modulators};

pub(crate) struct ResourceStorage<T> {
	pub(crate) resources: Arena<T>,
	new_resource_consumer: Consumer<(Key, T)>,
	unused_resource_producer: Producer<T>,
}

impl<T> ResourceStorage<T> {
	#[must_use]
	pub fn new(capacity: usize) -> (Self, ResourceController<T>) {
		let (new_resource_producer, new_resource_consumer) = RingBuffer::new(capacity);
		let (unused_resource_producer, unused_resource_consumer) = RingBuffer::new(capacity);
		let resources = Arena::new(capacity);
		let arena_controller = resources.controller();
		(
			Self {
				resources,
				new_resource_consumer,
				unused_resource_producer,
			},
			ResourceController {
				arena_controller,
				new_resource_producer: Mutex::new(new_resource_producer),
				unused_resource_consumer: Mutex::new(unused_resource_consumer),
			},
		)
	}

	pub fn remove_and_add(&mut self, remove_test: impl FnMut(&T) -> bool) {
		for (_, resource) in self.resources.drain_filter(remove_test) {
			self.unused_resource_producer
				.push(resource)
				.unwrap_or_else(|_| panic!("unused resource producer is full"));
		}
		while let Ok((key, resource)) = self.new_resource_consumer.pop() {
			self.resources
				.insert_with_key(key, resource)
				.expect("error inserting resource");
		}
	}

	#[must_use]
	pub fn get_mut(&mut self, key: Key) -> Option<&mut T> {
		self.resources.get_mut(key)
	}

	#[must_use]
	pub fn iter(&self) -> atomic_arena::iter::Iter<'_, T> {
		self.resources.iter()
	}

	#[must_use]
	pub fn iter_mut(&mut self) -> atomic_arena::iter::IterMut<'_, T> {
		self.resources.iter_mut()
	}

	#[must_use]
	pub fn is_empty(&self) -> bool {
		self.resources.is_empty()
	}
}

impl<'a, T> IntoIterator for &'a mut ResourceStorage<T> {
	type Item = (Key, &'a mut T);

	type IntoIter = atomic_arena::iter::IterMut<'a, T>;

	fn into_iter(self) -> Self::IntoIter {
		self.iter_mut()
	}
}

pub(crate) struct SelfReferentialResourceStorage<T> {
	pub(crate) resources: Arena<T>,
	keys: Vec<Key>,
	new_resource_consumer: Consumer<(Key, T)>,
	unused_resource_producer: Producer<T>,
	dummy: T,
}

impl<T> SelfReferentialResourceStorage<T> {
	pub fn new(capacity: usize) -> (Self, ResourceController<T>)
	where
		T: Default,
	{
		let (new_resource_producer, new_resource_consumer) = RingBuffer::new(capacity);
		let (unused_resource_producer, unused_resource_consumer) = RingBuffer::new(capacity);
		let resources = Arena::new(capacity);
		let arena_controller = resources.controller();
		(
			Self {
				resources,
				keys: Vec::with_capacity(capacity),
				new_resource_consumer,
				unused_resource_producer,
				dummy: T::default(),
			},
			ResourceController {
				arena_controller,
				new_resource_producer: Mutex::new(new_resource_producer),
				unused_resource_consumer: Mutex::new(unused_resource_consumer),
			},
		)
	}

	pub fn remove_and_add(&mut self, remove_test: impl FnMut(&T) -> bool) {
		self.remove_unused(remove_test);
		while let Ok((key, resource)) = self.new_resource_consumer.pop() {
			self.resources
				.insert_with_key(key, resource)
				.expect("error inserting resource");
			self.keys.push(key);
		}
	}

	#[cfg(test)]
	#[must_use]
	pub fn get_mut(&mut self, key: Key) -> Option<&mut T> {
		self.resources.get_mut(key)
	}

	#[must_use]
	pub fn iter_mut(&mut self) -> atomic_arena::iter::IterMut<'_, T> {
		self.resources.iter_mut()
	}

	pub fn for_each(&mut self, mut f: impl FnMut(&mut T, &mut Arena<T>)) {
		for key in &self.keys {
			std::mem::swap(&mut self.resources[*key], &mut self.dummy);
			f(&mut self.dummy, &mut self.resources);
			std::mem::swap(&mut self.resources[*key], &mut self.dummy);
		}
	}

	fn remove_unused(&mut self, mut remove_test: impl FnMut(&T) -> bool) {
		let mut i = 0;
		while i < self.keys.len() && !self.unused_resource_producer.is_full() {
			let key = self.keys[i];
			let resource = &mut self.resources[key];
			if remove_test(resource) {
				let resource = self.resources.remove(key).unwrap();
				self.unused_resource_producer
					.push(resource)
					.unwrap_or_else(|_| panic!("unused resource producer is full"));
				self.keys.remove(i);
			} else {
				i += 1;
			}
		}
	}
}

impl<'a, T> IntoIterator for &'a mut SelfReferentialResourceStorage<T> {
	type Item = (Key, &'a mut T);

	type IntoIter = atomic_arena::iter::IterMut<'a, T>;

	fn into_iter(self) -> Self::IntoIter {
		self.iter_mut()
	}
}

pub(crate) struct ResourceController<T> {
	pub arena_controller: Controller,
	pub new_resource_producer: Mutex<Producer<(Key, T)>>,
	pub unused_resource_consumer: Mutex<Consumer<T>>,
}

impl<T> ResourceController<T> {
	pub fn insert(&mut self, resource: T) -> Result<Key, ResourceLimitReached> {
		let key = self.try_reserve()?;
		self.insert_with_key(key, resource);
		Ok(key)
	}

	pub fn try_reserve(&self) -> Result<Key, ResourceLimitReached> {
		self.arena_controller
			.try_reserve()
			.map_err(|_| ResourceLimitReached)
	}

	pub fn insert_with_key(&mut self, key: Key, resource: T) {
		self.remove_unused();
		self.new_resource_producer
			.get_mut()
			.expect("new resource producer mutex poisoned")
			.push((key, resource))
			.unwrap_or_else(|_| panic!("new resource producer full"));
	}

	fn remove_unused(&mut self) {
		let unused_resource_consumer = &mut self
			.unused_resource_consumer
			.get_mut()
			.expect("unused resource consumer mutex poisoned");
		while unused_resource_consumer.pop().is_ok() {}
	}

	#[must_use]
	pub fn capacity(&self) -> usize {
		self.arena_controller.capacity()
	}

	#[must_use]
	pub fn len(&self) -> usize {
		self.arena_controller.len()
	}
}

impl<T> Debug for ResourceController<T> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("ResourceController")
			.field("arena_controller", &self.arena_controller)
			.field("new_resource_producer", &ProducerDebug)
			.field("unused_resource_consumer", &ConsumerDebug)
			.finish()
	}
}

struct ProducerDebug;

impl Debug for ProducerDebug {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("Producer").finish()
	}
}

struct ConsumerDebug;

impl Debug for ConsumerDebug {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("Consumer").finish()
	}
}

pub(crate) struct Resources {
	pub mixer: Mixer,
	pub clocks: Clocks,
	pub modulators: Modulators,
	pub listeners: Listeners,
}

pub(crate) struct ResourceControllers {
	pub sub_track_controller: ResourceController<Track>,
	pub send_track_controller: ResourceController<SendTrack>,
	pub clock_controller: ResourceController<Clock>,
	pub modulator_controller: ResourceController<Box<dyn Modulator>>,
	pub listener_controller: ResourceController<Listener>,
	pub main_track_handle: MainTrackHandle,
}

pub(crate) fn create_resources(
	capacities: Capacities,
	main_track_builder: MainTrackBuilder,
	sample_rate: u32,
	internal_buffer_size: usize,
) -> (Resources, ResourceControllers) {
	let (mixer, sub_track_controller, send_track_controller, main_track_handle) = Mixer::new(
		capacities.sub_track_capacity,
		capacities.send_track_capacity,
		sample_rate,
		internal_buffer_size,
		main_track_builder,
	);
	let (clocks, clock_controller) = Clocks::new(capacities.clock_capacity);
	let (modulators, modulator_controller) = Modulators::new(capacities.modulator_capacity);
	let (listeners, listener_controller) = Listeners::new(capacities.listener_capacity);
	(
		Resources {
			mixer,
			clocks,
			modulators,
			listeners,
		},
		ResourceControllers {
			sub_track_controller,
			send_track_controller,
			clock_controller,
			modulator_controller,
			listener_controller,
			main_track_handle,
		},
	)
}
