use std::{
    sync::{
        Arc,
        atomic::{AtomicUsize, Ordering},
        mpsc::{Sender, channel},
    },
    thread::{self, JoinHandle},
};

// Sent via channels to a separate thread for metric gathering
pub(crate) enum Measurement {
    Sent(usize),
    Received(usize),
}

#[derive(Clone)]
pub(crate) struct Metrics {
    total_sent: Arc<AtomicUsize>,
    total_read: Arc<AtomicUsize>,
    _h: Arc<JoinHandle<()>>,
}

impl Metrics {
    pub(crate) fn new() -> (Self, Sender<Measurement>) {
        let total_sent: Arc<AtomicUsize> = AtomicUsize::new(0).into();
        let total_read: Arc<AtomicUsize> = AtomicUsize::new(0).into();
        let sent = total_sent.clone();
        let read = total_read.clone();
        let (tx, rx) = channel();
        let h = thread::spawn(move || {
            for m in rx {
                match m {
                    Measurement::Sent(s) => {
                        sent.fetch_add(s, Ordering::Relaxed);
                    }
                    Measurement::Received(s) => {
                        read.fetch_add(s, Ordering::Relaxed);
                    }
                }
            }
        });
        (
            Self {
                total_sent,
                total_read,
                _h: h.into(),
            },
            tx,
        )
    }

    pub fn sent(&self) -> usize {
        self.total_sent.load(Ordering::Relaxed)
    }

    pub fn read(&self) -> usize {
        self.total_read.load(Ordering::Relaxed)
    }
}
