Documentation
use std::sync::{Mutex, Once};

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum LogLevel {
    V,
    D,
    I,
    W,
    E,
    S,
}

impl LogLevel {
    pub fn index(&self) -> u32 {
        match self {
            LogLevel::V => 0,
            LogLevel::D => 1,
            LogLevel::I => 2,
            LogLevel::W => 3,
            LogLevel::E => 4,
            LogLevel::S => 5,
        }
    }

    pub fn greater_than(&self, o: &Self) -> bool {
        return self.index() > o.index();
    }

    pub fn greater_or_equal(&self, o: &Self) -> bool {
        return self.index() >= o.index();
    }

    pub fn less_than(&self, o: &Self) -> bool {
        return self.index() < o.index();
    }

    pub fn less_or_equal(&self, o: &Self) -> bool {
        return self.index() <= o.index();
    }
}

static INIT: Once = Once::new();
static mut LOG_LEVEL: Option<Mutex<LogLevel>> = None;

fn init() {
    INIT.call_once(|| unsafe { LOG_LEVEL = Some(Mutex::new(LogLevel::I)) });
}

pub fn set_log_level(level: LogLevel) {
    init();
    unsafe {
        if let Some(ref mutex) = LOG_LEVEL {
            let mut ll = mutex.lock().unwrap();
            *ll = level;
        }
    }
}

pub fn get_log_level() -> LogLevel {
    init();

    unsafe {
        if let Some(ref mutex) = LOG_LEVEL {
            let ll = mutex.lock().unwrap();
            match *ll {
                LogLevel::V => LogLevel::V,
                LogLevel::D => LogLevel::D,
                LogLevel::I => LogLevel::I,
                LogLevel::W => LogLevel::W,
                LogLevel::E => LogLevel::E,
                LogLevel::S => LogLevel::S,
            }
        } else {
            LogLevel::I
        }
    }
}

fn p(current_level: LogLevel, log_level: &LogLevel, msg: &str) {
    if log_level.less_than(&current_level) {
        return;
    }
    println!("{msg}");
}

pub fn v(msg: &str) {
    let current_level = get_log_level();
    p(current_level, &LogLevel::V, msg)
}

pub fn d(msg: &str) {
    let current_level = get_log_level();
    p(current_level, &LogLevel::D, msg)
}

pub fn i(msg: &str) {
    let current_level = get_log_level();
    p(current_level, &LogLevel::I, msg)
}

pub fn w(msg: &str) {
    let current_level = get_log_level();
    p(current_level, &LogLevel::W, msg)
}

pub fn e(msg: &str) {
    let current_level = get_log_level();
    p(current_level, &LogLevel::E, msg)
}

#[cfg(test)]
mod tests {
    use crate::set_log_level;

    use super::*;

    #[test]
    fn log() {
        set_log_level(LogLevel::V);
        println!("log_level:{:?}", get_log_level());
        v("verbose");
        d("debug");
        i("info");
        w("warn");
        e("error");

        set_log_level(LogLevel::D);
        println!("log_level:{:?}", get_log_level());
        v("verbose");
        d("debug");
        i("info");
        w("warn");
        e("error");

        set_log_level(LogLevel::I);
        println!("log_level:{:?}", get_log_level());
        v("verbose");
        d("debug");
        i("info");
        w("warn");
        e("error");

        set_log_level(LogLevel::W);
        println!("log_level:{:?}", get_log_level());
        v("verbose");
        d("debug");
        i("info");
        w("warn");
        e("error");

        set_log_level(LogLevel::E);
        println!("log_level:{:?}", get_log_level());
        v("verbose");
        d("debug");
        i("info");
        w("warn");
        e("error");

        set_log_level(LogLevel::S);
        println!("log_level:{:?}", get_log_level());
        v("verbose");
        d("debug");
        i("info");
        w("warn");
        e("error");
    }
}