// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};

use tokio::sync::Notify;

pub struct ClientCountNotify {
	allowed_notifications: f32,
	players: Arc<AtomicUsize>,
	spectators: Arc<AtomicUsize>,
	router_notify: Arc<Notify>,
}

const RATE_LIMIT: f32 = 0.5;
const MAX_BURST: f32 = 8.0;

impl ClientCountNotify {
	pub(super) fn new(router_notify: Arc<Notify>) -> ClientCountNotify {
		ClientCountNotify {
			allowed_notifications: MAX_BURST,
			players: Arc::new(AtomicUsize::new(0)),
			spectators: Arc::new(AtomicUsize::new(0)),
			router_notify,
		}
	}

	pub(super) fn player_count(&self) -> Arc<AtomicUsize> {
		Arc::clone(&self.players)
	}

	pub(super) fn spectator_count(&self) -> Arc<AtomicUsize> {
		Arc::clone(&self.spectators)
	}

	pub fn update(&mut self, players: usize, spectators: usize, dt: f32) {
		self.allowed_notifications = (self.allowed_notifications + RATE_LIMIT * dt).min(MAX_BURST);

		if self.allowed_notifications >= 1.0 {
			let (prev_players, prev_spectators) = (self.players.swap(players, Ordering::AcqRel), self.spectators.swap(spectators, Ordering::AcqRel));
			if players != prev_players || spectators != prev_spectators {
				self.router_notify.notify_waiters();
				self.allowed_notifications -= 1.0;
			}
		}
	}
}
