use ccutils::sync::{ArcRwLock, IntoArcRwLock};

use crate::prelude::*;

struct ReporterInner {
    messages: Vec<Message>,
    has_errors: bool,
}

/// This structure manages the reporting of errors and logs.
#[derive(Clone)]
pub(crate) struct Reporter {
    inner: ArcRwLock<ReporterInner>,
}

impl Reporter {
    pub(crate) fn new() -> Reporter {
        Reporter {
            inner: ReporterInner {
                messages: Default::default(),
                has_errors: false,
            }
            .into_arc_rw_lock(),
        }
    }
    pub(crate) fn handle_result<T, E>(
        &self,
        res: Result<T, E>,
        severity: Severity,
        msg: &'static str,
        here: Here,
    ) -> Option<T>
    where
        E: std::fmt::Display,
    {
        match res {
            Ok(t) => Some(t),
            Err(e) => {
                self.report_message(severity, format!("{}: '{}'", msg, e), here);
                None
            }
        }
    }
    /// Report a failure with the given message, filename and line number.
    /// There here argument can be created with the `here!()` macro.
    ///
    /// ```rust
    /// reporter.report_error("An error has occurred.", here!());
    /// ```
    pub fn report_error(&self, msg: String, here: Here) {
        self.report_message(Severity::Error, msg, here);
    }
    /// Report an error with the given message, filename and line number.
    ///
    /// ```rust
    /// reporter.report_error("An error has occurred.", here!());
    /// ```
    pub fn report_message(&self, severity: Severity, msg: String, here: Here) {
        let mut inner = self.inner.write().unwrap();
        inner.messages.push(Message {
            severity,
            content: msg,
            filename: here.filename,
            line: here.line,
        });
        match severity {
            Severity::Error => inner.has_errors = true,
            _ => {}
        }
    }
    /// Return the reported messages.
    pub fn clone_messages(&self) -> Vec<Message> {
        self.inner.read().unwrap().messages.clone()
    }
    /// For each message.
    pub fn for_each_message(&self, f: impl FnMut(&Message)) {
        self.inner.read().unwrap().messages.iter().for_each(f);
    }
    /// The number of messages
    pub fn messages_count(&self) -> usize {
        self.inner.read().unwrap().messages.len()
    }
    /// Return if there are any errors
    pub fn has_errors(&self) -> bool {
        self.inner.read().unwrap().has_errors
    }
}
