use tracing::{info, trace, debug, warn, error};
use crate::Tracer;

unsafe impl Send for Trace {}
unsafe impl Sync for Trace {}

#[derive(Debug)]
pub struct Trace {
    name: String,
    is_silent: bool,
}

impl Default for Trace {
    fn default() -> Trace {
        return Trace::from("default");
    }
}

impl Tracer for Trace {
    fn name(&self) -> &str {
        return &self.name;
    }

    fn child(&self, child_name: &str) -> Box<dyn Tracer> {
        let parent_name = format!("{}:{}", self.name, child_name);

        return Box::new(
            Trace::from(parent_name)
                .as_silent(self.is_silent),
        );
    }

    fn trace(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        trace!("{}", self.get_log_message(message));

        return self;
    }

    fn debug(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        debug!("{}", self.get_log_message(message));

        return self;
    }

    fn info(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        info!("{}", self.get_log_message(message));

        return self;
    }

    fn warn(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        warn!("{}", self.get_log_message(message));

        return self;
    }

    fn error(&self, message: &str) -> &dyn Tracer {
        if self.is_silent {
            return self;
        }

        error!("{}", self.get_log_message(message));

        return self;
    }
}

impl Trace {
    fn get_log_message(&self, message: &str) -> String {
        if message.trim().len() == 0 {
            return format!("{}", self.name);
        }

        return format!("{} -> {}", self.name, message);
    }

    pub fn as_silent(mut self, is_silent: bool) -> Self {
        self.is_silent = is_silent;

        return self;
    }

    pub fn name(&self) -> &str {
        return Tracer::name(self);
    }

    pub fn child(&self, child_name: &str) -> Box<dyn Tracer> {
        return Tracer::child(self, child_name);
    }

    pub fn trace(&self, message: &str) -> &dyn Tracer {
        return Tracer::trace(self, message);

    }

    pub fn debug(&self, message: &str) -> &dyn Tracer {
        return Tracer::debug(self, message);
    }

    pub fn info(&self, message: &str) -> &dyn Tracer {
        return Tracer::info(self, message);

    }

    pub fn warn(&self, message: &str) -> &dyn Tracer {
        return Tracer::warn(self, message);

    }

    pub fn error(&self, message: &str) -> &dyn Tracer {
        return Tracer::error(self, message);

    }
}

impl<T: ToString> From<T> for Trace {
    fn from(name: T) -> Self {
        let name = name.to_string();

        return Trace { name, is_silent: false };
    }
}
