//! The `Component` trait and the UI units built on it.
//!
//! A `Component` is one visual, interactive piece of the UI. Trait methods have default
//! implementations, so each component implements only what it needs.

pub mod app_id;
pub mod fps;
pub mod home;
pub mod status_bar;
pub mod ux;

use async_trait::async_trait;
use crossterm::event::{KeyEvent, MouseEvent};
use ratatui::{Frame, layout::Rect};

use status_bar::StatusCommand;

use crate::app::Mode;
use crate::errors::AppResult;
use crate::tui::Tui;
use crate::{action::Action, config::Config, tui::Event};

/// Where a component is drawn within the application layout.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Placement {
    /// The main content area (everything above the status bar).
    #[default]
    Main,
    /// The single-row status bar along the bottom.
    StatusBar,
}

/// `Component` is a trait that represents a visual and interactive element of the user interface.
///
/// Implementors of this trait can be registered with the main application loop and will be able to
/// receive events, update state, and be rendered on the screen.

#[async_trait]
pub trait Component: Send + Sync {
    /// Register a configuration handler that provides configuration settings if necessary.
    ///
    /// # Arguments
    ///
    /// * `config` - Configuration settings.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - An Ok result or an error.
    fn register_config_handler(&mut self, config: Config) -> AppResult<()> {
        let _ = config; // to appease clippy
        Ok(())
    }
    /// Initialize the component.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - An Ok result or an error.
    fn init(&mut self, tui: &mut Tui) -> AppResult<()> {
        let _ = tui; // to appease clippy
        Ok(())
    }
    /// Handle incoming events and produce actions if necessary.
    ///
    /// # Arguments
    ///
    /// * `event` - An optional event to be processed.
    ///
    /// # Returns
    ///
    /// * `Result<Option<Action>>` - An action to be processed or none.
    fn handle_events(&mut self, event: Option<Event>) -> AppResult<Option<Action>> {
        let action = match event {
            Some(Event::Key(key_event)) => self.handle_key_event(key_event)?,
            Some(Event::Mouse(mouse_event)) => self.handle_mouse_event(mouse_event)?,
            _ => None,
        };
        Ok(action)
    }
    /// Handle key events and produce actions if necessary.
    ///
    /// # Arguments
    ///
    /// * `key` - A key event to be processed.
    ///
    /// # Returns
    ///
    /// * `Result<Option<Action>>` - An action to be processed or none.
    fn handle_key_event(&mut self, key: KeyEvent) -> AppResult<Option<Action>> {
        let _ = key; // to appease clippy
        Ok(None)
    }
    /// Handle mouse events and produce actions if necessary.
    ///
    /// # Arguments
    ///
    /// * `mouse` - A mouse event to be processed.
    ///
    /// # Returns
    ///
    /// * `Result<Option<Action>>` - An action to be processed or none.
    fn handle_mouse_event(&mut self, mouse: MouseEvent) -> AppResult<Option<Action>> {
        let _ = mouse; // to appease clippy
        Ok(None)
    }
    /// Update the state of the component based on a received action. (REQUIRED)
    ///
    /// # Arguments
    ///
    /// * `action` - An action that may modify the state of the component.
    ///
    /// # Returns
    ///
    /// * `Result<Option<Action>>` - An action to be processed or none.
    async fn update(&mut self, action: &Action, tui: &mut Tui) -> AppResult<Option<Action>> {
        let _ = action; // to appease clippy
        let _ = tui;
        Ok(None)
    }
    /// Render the component on the screen. (REQUIRED)
    ///
    /// # Arguments
    ///
    /// * `mode` - Current mode.
    /// * `f` - A frame used for rendering.
    /// * `area` - The area in which the component should be drawn.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - An Ok result or an error.
    fn draw(&mut self, mode: &Mode, frame: &mut Frame, area: Rect) -> AppResult<()>;

    /// Where this component should be drawn. Defaults to the main content area.
    fn placement(&self) -> Placement {
        Placement::Main
    }
}
