use std::sync::mpsc;
use crate::system::System;
use crate::uistate::UIBackend;
use crate::uifrontend::UIFrontend;
use crate::uifrontend::UIFrontendType;
use crate::topologycache::TopologyCache;

#[cfg(feature = "gui")]
use crate::eguifrontend::EguiFrontend;

/// Events that can happen outside the UI thread that the UI needs to handle.
pub enum Event {
    /// Process/system info update tick.
    Tick,
}

/// Main 'application'.
pub struct CPUMap {
    /// System information and configuration (cpu topology, getting/setting affinities, etc.)
    pub(crate) system:   System,

    /// Application state.
    pub(crate) backend:  UIBackend,

    /// Renders UI.
    pub(crate) frontend: UIFrontend,
}

impl CPUMap {
    /// Construct CPUMap with egui GUI frontend, see CPUMap member documentation.
    pub fn new(
        topo:          hwloc2::Topology,
        system:        sysinfo::System,
        event_rx:      mpsc::Receiver<Event>,
        frontend_type: UIFrontendType) -> Self
    {
        let cache  = TopologyCache::new(topo.object_at_root(), system.cpus())
                     .expect("Failed to init topology cache");
        // TODO maybe replace topo by cache completely here
        let system = System::new( system, cache, topo );
        let ui     = UIBackend::new(&system, event_rx);
        Self {
            system,
            backend: ui,
            frontend: match frontend_type {
                UIFrontendType::Egui => {
                    #[cfg(feature = "gui")] { Some(UIFrontend::egui()) }
                    #[cfg(not(feature = "gui"))] { None }
                }
                UIFrontendType::Ratatui => {
                    #[cfg(feature = "tui")] { Some(UIFrontend::ratatui())} 
                    #[cfg(not(feature = "tui"))] {None }
                }
            }.unwrap()
        }
    }

    /// Run and consume CPUMap, returning any fatal error.
    pub fn run(mut self) -> anyhow::Result<()> {
        #[cfg(any(feature = "gui", feature = "tui"))]
        match self.frontend {
            #[cfg(feature = "gui")]
            UIFrontend::Egui(_) => {
                EguiFrontend::run(Box::new(move|_cc| Box::new(self)))
            },
            #[cfg(feature = "tui")]
            UIFrontend::Ratatui(mut ratatui) => {
                ratatui.run(&mut self.backend, &mut self.system)
            }
        }
        #[cfg(not(any(feature = "gui", feature = "tui")))]
        Ok(())
    }
}

