use crate::profile_result::ProfileResult;

use once_cell::sync::Lazy;
use parking_lot::Mutex;

use crate::backends::base::TracingBackend;

#[cfg(feature = "json")]
use crate::backends::json_profiler::JsonProfiler;

#[cfg(feature = "tracy")]
use crate::backends::tracy_profiler::TracyProfiler;

static PROFILER: Lazy<Mutex<Profiler>> = Lazy::new(|| Mutex::new(Profiler::new()));

pub struct Profiler {
    #[cfg(feature = "json")]
    backend: JsonProfiler,

    #[cfg(feature = "tracy")]
    backend: TracyProfiler,
}

impl Profiler {
    fn new() -> Self {
        Self {
            #[cfg(feature = "json")]
            backend: JsonProfiler::new(),

            #[cfg(feature = "tracy")]
            backend: TracyProfiler::new(),
        }
    }

    pub fn get() -> &'static Mutex<Profiler> {
        &PROFILER
    }

    pub fn write_profile(&mut self, result: &ProfileResult) {
        #[cfg(feature = "json")]
        self.backend.record_event(result);

        #[cfg(feature = "tracy")]
        self.backend.record_event(result);
    }

    pub fn frame_count(&self) -> usize {
        #[cfg(any(feature = "json", feature = "tracy"))]
        return self.backend.frame_count;
        #[cfg(not(any(feature = "json", feature = "tracy")))]
        return 0;
    }
}

impl TracingBackend for Profiler {
    fn begin_session(&mut self, name: &str, filepath: &str) {
        #[cfg(feature = "json")]
        self.backend.begin_session(name, filepath);

        #[cfg(feature = "tracy")]
        self.backend.begin_session(name, filepath);
    }

    fn begin_session_limited(
        &mut self,
        name: &str,
        filepath: &str,
        max_frames: Option<usize>,
        max_duration_ms: Option<u64>,
    ) {
        #[cfg(feature = "json")]
        self.backend
            .begin_session_limited(name, filepath, max_frames, max_duration_ms);

        #[cfg(feature = "tracy")]
        self.backend
            .begin_session_limited(name, filepath, max_frames, max_duration_ms);
    }

    fn end_session(&mut self) -> Result<(), String> {
        #[cfg(feature = "json")]
        self.backend.end_session()?;
        Ok(())
    }

    fn new_frame(&mut self) {
        #[cfg(feature = "json")]
        self.backend.new_frame();
        #[cfg(feature = "tracy")]
        self.backend.new_frame();
    }

    fn record_event(&mut self, event: &ProfileResult) {
        #[cfg(feature = "json")]
        self.backend.record_event(event);

        #[cfg(feature = "tracy")]
        self.backend.record_event(event);
    }

    fn record_span(&mut self, name: &str) {
        #[cfg(feature = "json")]
        self.backend.record_span(name);

        #[cfg(feature = "tracy")]
        self.backend.record_span(name);
    }
}
