use crossbeam_channel::{self, Sender};
use lazy_static::lazy_static;
use std::thread;
use std::time::Instant;

const CHANNEL_SIZE: usize = 1024;

lazy_static! {
    static ref LOGGER: Sender<Message> = create_logger();
}

fn create_logger() -> Sender<Message> {
    let (tx, rx) = crossbeam_channel::bounded(CHANNEL_SIZE);
    thread::spawn(move || loop {
        match rx.recv() {
            Ok(Message::Plain(text)) => println!("{}", text),
            Ok(Message::WithTimestampMs(text, time)) => println!("[{:6} ms] {}", time, text),
            Ok(Message::WithTimestampUs(text, time)) => println!("[{:9} us] {}", time, text),
            Err(error) => eprintln!("LOGGER ERROR: Failed to receive log:\n\"{}\"", error),
        }
    });
    tx
}

#[allow(dead_code)]
pub enum Message {
    Plain(&'static str),
    WithTimestampMs(&'static str, u64),
    WithTimestampUs(&'static str, u128),
}

#[allow(dead_code)]
pub fn log(message: Message) {
    if let Err(error) = LOGGER.send(message) {
        eprintln!("LOGGER ERROR: Failed to send log:\n\"{}\"", error);
    }
}

#[allow(dead_code)]
pub struct TimeLogger {
    start: Instant,
}

#[allow(dead_code)]
impl TimeLogger {
    pub fn new() -> Self {
        Self {
            start: Instant::now(),
        }
    }

    pub fn log(&self, message: &'static str) {
        let timestamp = self.start.elapsed().as_micros();
        log(Message::WithTimestampUs(message, timestamp));
    }
}
