use std::mem;
use dioxus_core::VirtualDom;
use freya_core::{
    dom::SafeDOM,
    prelude::EventMessage,
};
use freya_engine::prelude::*;
use winit::{
    dpi::LogicalSize,
    event_loop::{
        ActiveEventLoop,
        EventLoopProxy,
    },
    window::Window,
};
use crate::{
    app::Application,
    config::WindowConfig,
    devtools::Devtools,
    drivers::GraphicsDriver,
    size::WinitSize,
    LaunchConfig,
};
pub struct NotCreatedState<'a, State: Clone + 'static> {
    pub(crate) sdom: SafeDOM,
    pub(crate) vdom: VirtualDom,
    pub(crate) devtools: Option<Devtools>,
    pub(crate) config: LaunchConfig<'a, State>,
}
pub struct CreatedState {
    pub(crate) app: Application,
    pub(crate) surface: Surface,
    pub(crate) dirty_surface: Surface,
    pub(crate) graphics_driver: GraphicsDriver,
    pub(crate) window: Window,
    pub(crate) window_config: WindowConfig,
    pub(crate) is_window_focused: bool,
}
pub enum WindowState<'a, State: Clone + 'static> {
    NotCreated(NotCreatedState<'a, State>),
    Creating,
    Created(CreatedState),
}
impl<'a, State: Clone + 'a> WindowState<'a, State> {
    pub fn created_state(&mut self) -> &mut CreatedState {
        let Self::Created(created) = self else {
            panic!("Unexpected.")
        };
        created
    }
    pub fn has_been_created(&self) -> bool {
        matches!(self, Self::Created(..))
    }
    pub fn create(
        &mut self,
        event_loop: &ActiveEventLoop,
        event_loop_proxy: &EventLoopProxy<EventMessage>,
    ) {
        let Self::NotCreated(NotCreatedState {
            sdom,
            vdom,
            devtools,
            mut config,
        }) = mem::replace(self, WindowState::Creating)
        else {
            panic!("Unexpected.")
        };
        let mut window_attributes = Window::default_attributes()
            .with_visible(false)
            .with_title(config.window_config.title)
            .with_decorations(config.window_config.decorations)
            .with_transparent(config.window_config.transparent)
            .with_window_icon(config.window_config.icon.take())
            .with_inner_size(LogicalSize::<f64>::from(config.window_config.size));
        set_resource_cache_total_bytes_limit(1000000); set_resource_cache_single_allocation_byte_limit(Some(500000)); if let Some(min_size) = config.window_config.min_size {
            window_attributes =
                window_attributes.with_min_inner_size(LogicalSize::<f64>::from(min_size));
        }
        if let Some(max_size) = config.window_config.max_size {
            window_attributes =
                window_attributes.with_max_inner_size(LogicalSize::<f64>::from(max_size));
        }
        if let Some(with_window_attributes) = &config.window_config.window_attributes_hook {
            window_attributes = (with_window_attributes)(window_attributes);
        }
        let (graphics_driver, window, mut surface) =
            GraphicsDriver::new(event_loop, window_attributes, &config);
        if config.window_config.visible {
            window.set_visible(true);
        }
        window.set_ime_allowed(true);
        let mut dirty_surface = surface
            .new_surface_with_dimensions(window.inner_size().to_skia())
            .unwrap();
        let scale_factor = window.scale_factor();
        surface
            .canvas()
            .scale((scale_factor as f32, scale_factor as f32));
        surface.canvas().clear(config.window_config.background);
        dirty_surface
            .canvas()
            .scale((scale_factor as f32, scale_factor as f32));
        dirty_surface
            .canvas()
            .clear(config.window_config.background);
        let mut app = Application::new(
            sdom,
            vdom,
            event_loop_proxy,
            devtools,
            &window,
            config.embedded_fonts,
            config.plugins,
            config.default_fonts,
        );
        app.init_doms(scale_factor as f32, config.state.clone());
        app.process_layout(window.inner_size(), scale_factor);
        *self = WindowState::Created(CreatedState {
            surface,
            dirty_surface,
            graphics_driver,
            window,
            app,
            window_config: config.window_config,
            is_window_focused: false,
        });
    }
}