use std::fmt::Debug;

use anyhow::Result;
use glam::UVec2;

use crate::{engine::Engine, platform::platform_run};

type SetupFn = Box<dyn FnOnce(&mut Engine)>;

pub struct App {
    pub title: String,
    pub window: UVec2,
    pub vsync: bool,
    pub resizable: bool,
    pub fullscreen: bool,
    pub cursor_visible: bool,
    pub setup: Option<SetupFn>,
}

impl Debug for App {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "App {{ title: {}, window: {:?}, vsync: {}, resizable: {}, fullscreen: {} }}",
            self.title, self.window, self.vsync, self.resizable, self.fullscreen,
        )
    }
}
impl Default for App {
    fn default() -> Self {
        Self {
            title: "Hello Roast2D".to_string(),
            window: UVec2::new(800, 600),
            vsync: true,
            resizable: false,
            fullscreen: false,
            cursor_visible: true,
            setup: None,
        }
    }
}

impl App {
    pub fn title(mut self, title: String) -> Self {
        self.title = title;
        self
    }

    pub fn window(mut self, window: UVec2) -> Self {
        self.window = window;
        self
    }

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

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

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

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

    /// Run the game
    pub fn run<Setup: FnOnce(&mut Engine) + 'static>(mut self, setup: Setup) -> Result<()> {
        self.setup = Some(Box::new(setup));
        platform_run(self)
    }
}
