pub mod socket;

use std::collections::HashMap;
use log::{LevelFilter, Log, Metadata, Record};
use std::sync::{Arc, RwLock};

pub struct Logger {
    max_level: LevelFilter,
    dispatches: HashMap<u64, Box<dyn Dispatch>>
}

impl Logger {
    pub fn new(max_level: LevelFilter, dispatches: Vec<Box<dyn Dispatch>>) -> Self {
        Logger {
            max_level,
            dispatches: dispatches
                .into_iter()
                .map(|d| (Into::<u64>::into(d.uid()), d))
                .collect(),
        }
    }
}

#[derive(Clone)]
pub struct LogHandle(pub(crate) Arc<RwLock<Logger>>);

impl LogHandle {
    pub fn add_dispatch(&self, d: impl Dispatch + 'static) {
        self.0.write().expect("logger poisoned").dispatches.insert(d.uid(), Box::new(d));
    }

    pub fn remove_dispatch(&self, dispatch: impl Dispatch) {
        let uid = dispatch.uid();
        self.0.write().expect("logger poisoned").dispatches.retain(|d_uid, _| {
            *d_uid != uid
        })
    }
}

impl Log for LogHandle {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= self.0.read().expect("logger poisoned").max_level
    }

    fn log(&self, record: &Record) {
        for (_, dispatch) in &mut self.0.write().expect("logger poisoned").dispatches {
            dispatch.log(record);
        }
    }

    fn flush(&self) {
        for (_, dispatch) in &mut self.0.write().expect("logger poisoned").dispatches {
            dispatch.flush();
        }
    }
}

pub trait Dispatch: Send + Sync {
    fn max_level(&self) -> LevelFilter;
    fn uid(&self) -> u64;
    fn log(&mut self, item: &Record);
    fn flush(&mut self);
}
