use std::{
    f32::consts::TAU,
    path::Path,
    rc::Rc,
    sync::{Arc, Mutex},
};

use anyhow::Result;
use glam::{UVec2, Vec2, Vec3, Vec3Swizzles};
use image::RgbaImage;

use crate::{
    asset::{Asset, AssetFetchOutput, AssetFetchRequest, AssetManager, AssetType, FetchedTask},
    camera::Camera,
    color::Color,
    commands::*,
    font::{Font, FontConfig, FontManager},
    handle::Handle,
    input::InputState,
    platform::types::CircleUniform,
    prelude::Transform,
    render::{BackendState, Render, ScaleMode},
    renderer::post::ScreenShader,
    renderer::shader2d::default::DefaultShader,
    renderer::shader3d::draw3d::Draw3D,
    renderer::traits::{PostShader, Shader},
    sprite::Sprite,
    tasks,
    text::{Text, TextCache, TextRenderCache},
    types::{DrawContent, Line, LinePattern},
    utils::bytes::to_wgsl_bytes,
};

/// Max tick
const ENGINE_MAX_TICK: f32 = 100.0;
/// Default font
const DEFAULT_FONT_BYTES: &[u8; 49432] = include_bytes!("../assets/C_C_Red_Alert.ttf");
/// Command buffer size
const COMMANDS_BUFFER: usize = 1024;

// Scene trait
pub trait Scene {
    // Init the scene, use it to load assets and setup entities.
    fn init(&mut self, _g: &mut Engine) {}

    // Update scene per frame, you probably want to call scene_base_update if you override this function.
    fn update(&mut self, _g: &mut Engine) {}

    // Draw scene per frame, use it to draw entities or Hud, you probably want to call scene_base_draw if you override this function.
    fn draw(&mut self, _g: &mut Engine) {}

    // Called when cleanup scene, release assets and resources.
    fn cleanup(&mut self, _g: &mut Engine) {}
}

#[derive(Default, Debug)]
pub struct Perf {
    pub entities: usize,
    pub update: f32,
    pub draw: f32,
    pub total: f32,
}

pub trait Clock {
    fn now(&mut self) -> f32;
}

#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum CameraMode {
    #[default]
    Game,
    Ui,
}

#[cfg(feature = "egui")]
type EGUILayer = Box<dyn FnMut(&egui::Context, &mut Engine)>;

pub struct Engine {
    // Clock
    pub clock: Box<dyn Clock>,

    // The real time in seconds since program start
    pub time_real: f32,

    // The game time in seconds since scene start
    pub time: f32,

    // A global multiplier for how fast game time should advance. Default: 1.0
    pub time_scale: f32,

    // The time difference in seconds from the last frame to the current.
    // Typically 0.01666 (assuming 60hz)
    pub tick: f32,

    // The frame number in this current scene. Increases by 1 for every frame.
    pub frame: f32,

    // Various infos about the last frame
    pub perf: Perf,

    // Input
    pub input: InputState,

    // CameraMode
    pub camera_mode: CameraMode,

    // states
    pub(crate) exit: bool,
    is_running: bool,
    is_window_resized: bool,
    scene: Option<Box<dyn Scene>>,
    scene_next: Option<Box<dyn Scene>>,
    font_manager: FontManager,
    text_cache: TextCache,
    commands_buffer: Vec<Command>,
    resource_commands: Vec<ResourceCommand>,

    // camera
    pub(crate) camera: Camera,
    // Ui camera
    pub(crate) ui_camera: Camera,

    // 3d draws
    draws3d: Vec<Draw3D>,

    // 3D camera
    pub(crate) camera3d: crate::camera3d::Camera3D,
    // render
    pub(crate) render: Render,
    #[cfg(feature = "egui")]
    egui_layers: Vec<EGUILayer>,
    // AssetsManager
    pub assets: AssetManager,
    // Cached handle for the default white texture
    default_texture: Option<Handle>,
    asset_fetch_job: Option<AssetFetchJob>,
}

impl Engine {
    pub fn new<C: Clock + 'static>(clock: C, backend_state: BackendState) -> Self {
        Self {
            clock: Box::new(clock),
            time_real: 0.0,
            time: 0.0,
            time_scale: 1.0,
            tick: 0.0,
            frame: 0.0,
            camera: Camera::default(),
            ui_camera: Camera::default(),
            camera3d: crate::camera3d::Camera3D::default(),
            camera_mode: CameraMode::default(),
            perf: Perf::default(),
            is_running: false,
            is_window_resized: false,
            scene: None,
            scene_next: None,
            font_manager: FontManager::default(),
            text_cache: TextCache::default(),
            commands_buffer: Vec::with_capacity(COMMANDS_BUFFER),
            resource_commands: Vec::with_capacity(COMMANDS_BUFFER),
            draws3d: Vec::with_capacity(COMMANDS_BUFFER),
            render: Render::new(backend_state),
            input: InputState::default(),
            assets: AssetManager::new("assets"),
            default_texture: None,
            asset_fetch_job: None,
            exit: false,
            #[cfg(feature = "egui")]
            egui_layers: Vec::new(),
        }
    }

    pub fn exit(&mut self) {
        self.exit = true;
    }

    /// Load default font
    pub fn load_default_font<P: AsRef<Path>>(&mut self, path: P) {
        let handle = self.assets.load_font(path);
        self.set_default_font(handle);
    }

    /// Set snap to px
    pub fn set_snap_to_px(&mut self, snap_to_px: bool) {
        self.camera.set_snap_to_px(snap_to_px);
        self.ui_camera.set_snap_to_px(snap_to_px);
    }

    /// Set default font
    pub fn set_default_font(&mut self, handle: Handle) {
        self.font_manager.set_default_font(handle);
    }

    /// Set default font
    pub fn set_font_config(&mut self, config: FontConfig) {
        self.font_manager.set_font_config(config);
    }

    /// Render text
    pub fn render_text(&mut self, text: &Text) -> Result<(Handle, UVec2)> {
        let (handle, size) = match self.text_cache.get(text) {
            Some(TextRenderCache { texture, size }) => (texture.clone(), *size),
            None => {
                log::trace!("Render new text {text:?}");
                // render text texture
                let (handle, size) = self.create_text_texture(text)?;
                self.text_cache.update(
                    text.clone(),
                    TextRenderCache {
                        texture: handle.clone(),
                        size,
                    },
                );
                (handle, size)
            }
        };
        Ok((handle, size))
    }

    /// Draw text
    ///
    /// # Arguments
    ///
    /// * `text` - Text
    /// * `anchor` - Anchor, center is (0.5, 0.5), min value (0.0, 0.0), max value (1.0, 1.0)
    /// * `transform` - Transform, the size will be multiplied by text size
    ///
    /// # Examples
    ///
    /// ```
    /// # use roast2d_internal::prelude::*;
    /// // draw a text with left-top anchor (0, 0)
    /// # fn draw(g: &mut Engine, id: Ent, viewport: Vec2) {
    ///    let text = Text::new(format!("Hello"), 20.0, BLUE);
    ///    g.draw_text(
    ///        &text,
    ///        None,
    ///        Transform::new(Vec3::new(0.0, 20.0, 1.0))
    ///    );
    /// # }
    /// ```
    pub fn draw_text(&mut self, text: &Text, anchor: Option<Vec2>, mut transform: Transform) {
        if let Ok((handle, size)) = self.render_text(text) {
            let mut sprite = Sprite::new(handle, size);
            if let Some(anchor) = anchor {
                sprite.anchor = anchor;
            }
            // Transform no longer carries size; Sprite contains its pixel size.
            transform.pos.x += self.font_manager.font_config.offset.x;
            transform.pos.y += self.font_manager.font_config.offset.y;

            self.draw(&sprite, transform);
        }
    }

    /// Draw 3d mesh
    pub fn draw3d(&mut self, draw: Draw3D) {
        self.draws3d.push(draw);
    }

    /// Create text texture
    pub fn create_text_texture(&mut self, text: &Text) -> Result<(Handle, UVec2)> {
        let handle = self.assets.insert(Asset {
            asset_type: AssetType::Texture,
            bytes: None,
        });
        let (data, size) = self.render.render_text_texture(&self.font_manager, text)?;
        let cmd = CreateTexture {
            handle: handle.clone(),
            data,
        };
        self.push_res_cmd(ResourceCommand::CreateTexture(cmd));
        Ok((handle, size))
    }

    /// Create raw texture
    pub fn create_raw_texture(&mut self, texture: wgpu::Texture) -> Result<Handle> {
        let handle = self.assets.insert(Asset {
            asset_type: AssetType::Texture,
            bytes: None,
        });
        self.push_res_cmd(ResourceCommand::CreateRawTexture(CreateRawTexture {
            handle: handle.clone(),
            texture,
        }));
        Ok(handle)
    }

    fn push_draw_cmd(&mut self, cmd: Command) {
        self.commands_buffer.push(cmd);
    }

    fn push_res_cmd(&mut self, cmd: ResourceCommand) {
        self.resource_commands.push(cmd);
    }

    pub(crate) fn take_draw_commands(&mut self) -> Vec<Command> {
        std::mem::replace(
            &mut self.commands_buffer,
            Vec::with_capacity(COMMANDS_BUFFER),
        )
    }

    pub(crate) fn take_resource_commands(&mut self) -> Vec<ResourceCommand> {
        std::mem::replace(
            &mut self.resource_commands,
            Vec::with_capacity(COMMANDS_BUFFER),
        )
    }

    pub(crate) fn take_3d_draws(&mut self) -> Vec<Draw3D> {
        std::mem::replace(&mut self.draws3d, Vec::with_capacity(COMMANDS_BUFFER))
    }

    /// Draw within Ui context
    pub fn with_ui<R, F: FnOnce(&mut Engine) -> R>(&mut self, f: F) -> R {
        let mode = self.camera_mode;
        self.camera_mode = CameraMode::Ui;
        let ret = f(self);
        self.camera_mode = mode;
        ret
    }

    /// Draw rectangle
    ///
    /// # Arguments
    ///
    /// * `color` - Color, color of the rectangle
    /// * `anchor` - Anchor, default is (0.5, 0.5), min value (0.0, 0.0), max value (1.0, 1.0)
    /// * `transform` - Transform
    ///
    /// # Examples
    ///
    /// ```
    /// # use roast2d_internal::prelude::*;
    /// // draw a blue rectangle
    /// # fn draw(g: &mut Engine, id: Ent, viewport: Vec2) {
    ///   g.draw_rect(
    ///       BLUE,
    ///       Vec2::splat(40.0),
    ///       None,
    ///       Transform::new(Vec3::new(50.0, 100.0, 1.0)),
    ///   );
    /// # }
    /// ```
    pub fn draw_rect(
        &mut self,
        color: Color,
        size: Vec2,
        anchor: Option<Vec2>,
        transform: Transform,
    ) {
        let texture = self.default_texture_handle();
        let mut image = Sprite::new(texture, size.as_uvec2());
        image.color = color;
        if let Some(anchor) = anchor {
            image.anchor = anchor;
        }
        self.draw(&image, transform);
    }

    /// Draw line
    ///
    /// # Arguments
    ///
    /// * `line` - Line struct containing start, end, thickness, color, and z-index
    ///
    /// # Examples
    ///
    /// ```
    /// # use roast2d_internal::prelude::*;
    /// // draw a red line from (10, 10) to (100, 50) with 2px thickness at z-index 1.0
    /// # fn draw(g: &mut Engine) {
    ///   g.draw_line(Line {
    ///       start: Vec2::new(10.0, 10.0),
    ///       end: Vec2::new(100.0, 50.0),
    ///       thickness: 2.0,
    ///       color: RED,
    ///   }, 1.0);
    /// # }
    /// ```
    pub fn draw_line(&mut self, line: Line, z: f32) {
        self.draw_lines(vec![line], z);
    }

    /// Draw line with extended styling options
    ///
    /// # Arguments
    ///
    /// * `line` - Line struct containing start, end, thickness, color, and z-index
    /// * `pattern` - Line pattern (Solid, Dashed, or Dotted)
    /// * `offset` - Pattern offset for animation effects
    ///
    /// # Examples
    ///
    /// ```
    /// # use roast2d_internal::prelude::*;
    /// // draw a dashed red line from (10, 10) to (100, 50)
    /// # fn draw(g: &mut Engine) {
    ///   g.draw_line_ex(
    ///       Line {
    ///           start: Vec2::new(10.0, 10.0),
    ///           end: Vec2::new(100.0, 50.0),
    ///           thickness: 2.0,
    ///           color: RED,
    ///       },
    ///       1.0,
    ///       LinePattern::Dashed { dash_length: 10.0, gap_length: 5.0 },
    ///       0.0
    ///   );
    /// # }
    /// ```
    pub fn draw_line_ex(&mut self, line: Line, z: f32, pattern: LinePattern, offset: f32) {
        let (dash_length, gap_length, dash_offset) = match pattern {
            LinePattern::Solid => {
                self.draw_line(line, z);
                return;
            }
            LinePattern::Dashed {
                dash_length,
                gap_length,
            } => (dash_length, gap_length, offset),
            LinePattern::Dotted { spacing } => (spacing * 0.5, spacing * 0.5, offset),
        };

        let line_vec = line.end - line.start;
        let line_length = line_vec.length();
        let line_dir = line_vec.normalize_or_zero();
        let pattern_length = dash_length + gap_length;
        let mut current_dist = -dash_offset;
        let mut lines = Vec::new();

        while current_dist < line_length {
            let dash_start_dist = current_dist.max(0.0);
            let dash_end_dist = (current_dist + dash_length).min(line_length);

            if dash_start_dist < dash_end_dist {
                lines.push(Line {
                    start: line.start + line_dir * dash_start_dist,
                    end: line.start + line_dir * dash_end_dist,
                    thickness: line.thickness,
                    color: line.color,
                });
            }
            current_dist += pattern_length;
        }
        self.draw_lines(lines, z);
    }

    /// Draw a batch of lines
    ///
    /// # Arguments
    ///
    /// * `lines` - Slice of Line structs to draw
    ///
    /// # Examples
    ///
    /// ```
    /// # use roast2d_internal::prelude::*;
    /// // draw multiple lines in a single batch
    /// # fn draw(g: &mut Engine) {
    ///   g.draw_lines(vec![
    ///       Line {
    ///           start: Vec2::new(10.0, 10.0),
    ///           end: Vec2::new(100.0, 50.0),
    ///           thickness: 2.0,
    ///           color: RED,
    ///       },
    ///       Line {
    ///           start: Vec2::new(20.0, 20.0),
    ///           end: Vec2::new(120.0, 70.0),
    ///           thickness: 3.0,
    ///           color: BLUE,
    ///       },
    ///   ], 1.0);
    /// # }
    /// ```
    pub fn draw_lines(&mut self, lines: Vec<Line>, z: f32) {
        if lines.is_empty() {
            return;
        }

        let shader = self.render.solid_shader.clone();

        // Determine bounding box for the group of lines
        let mut min_p = lines[0].start;
        let mut max_p = lines[0].end;
        for line in &lines {
            min_p = min_p.min(line.start).min(line.end);
            max_p = max_p.max(line.start).max(line.end);
        }
        let size = max_p - min_p;
        let center = (min_p + max_p) * 0.5;
        let transparent = lines.iter().any(|l| l.color.a < 1.0);

        let cmd = Draw {
            shader: shader.clone(),
            shader_data: None,
            content: DrawContent::Lines(lines),
            src: None,
            transform: Transform::new(Vec3::new(center.x, center.y, z)),
            size,
            flip_x: false,
            flip_y: false,
            anchor: Vec2::splat(0.5),
            transparent,
        };
        self.push_draw(cmd);
    }

    /// Draw a filled regular polygon (3+ sides) with the provided center, radius, rotation, and color.
    pub fn draw_poly(
        &mut self,
        center: Vec3,
        sides: u32,
        radius: f32,
        rotation: f32,
        color: Color,
    ) {
        if sides < 3 || radius <= 0.0 {
            return;
        }

        let (vertices, min, max) =
            Self::polygon_vertices_with_bounds(center.xy(), sides, radius, rotation);
        let size = max - min;
        let polygon_renderer = self.render.solid_shader.clone();

        let cmd = Draw {
            shader: polygon_renderer,
            shader_data: None,
            content: DrawContent::Polygon { vertices, color },
            src: None,
            transform: Transform::new(Vec3::new(
                (min.x + max.x) * 0.5,
                (min.y + max.y) * 0.5,
                center.z,
            )),
            size,
            flip_x: false,
            flip_y: false,
            anchor: Vec2::splat(0.5),
            transparent: color.a < 1.0,
        };
        self.push_draw(cmd);
    }

    /// Draw a textured regular polygon with UVs covering the sprite (respecting optional src rect).
    pub fn draw_poly_textured(
        &mut self,
        center: Vec3,
        sides: u32,
        radius: f32,
        rotation: f32,
        sprite: &Sprite,
    ) {
        if sides < 3 || radius <= 0.0 {
            return;
        }

        let (vertices, min, max) =
            Self::polygon_vertices_with_bounds(center.xy(), sides, radius, rotation);
        let size = max - min;
        let uvs = Self::polygon_uvs_for_sprite(&vertices, min, size, sprite);

        let renderer = self.render.shader.clone();
        let renderer_data = self.render.send_shader_data.take();

        let cmd = Draw {
            shader: renderer,
            shader_data: renderer_data,
            content: DrawContent::PolygonTextured {
                vertices,
                uvs,
                color: sprite.color,
                handle: sprite.texture.clone(),
            },
            src: None,
            transform: Transform::new(Vec3::new(
                (min.x + max.x) * 0.5,
                (min.y + max.y) * 0.5,
                center.z,
            )),
            size,
            flip_x: false,
            flip_y: false,
            anchor: Vec2::splat(0.5),
            transparent: sprite.color.a < 1.0,
        };
        self.push_draw(cmd);
    }

    /// Draw the outline of a regular polygon, using the line renderer internally.
    pub fn draw_poly_lines(
        &mut self,
        center: Vec3,
        sides: u32,
        radius: f32,
        rotation: f32,
        thickness: f32,
        color: Color,
    ) {
        if sides < 3 || radius <= 0.0 {
            return;
        }
        let vertices = Self::regular_polygon_vertices(center.xy(), radius, sides, rotation);
        let mut lines = Vec::with_capacity(vertices.len());
        for i in 0..vertices.len() {
            let start = vertices[i];
            let end = vertices[(i + 1) % vertices.len()];
            lines.push(Line {
                start,
                end,
                thickness,
                color,
            });
        }
        self.draw_lines(lines, center.z);
    }

    fn regular_polygon_vertices(center: Vec2, radius: f32, sides: u32, rotation: f32) -> Vec<Vec2> {
        let step = TAU / sides as f32;
        (0..sides)
            .map(|i| {
                let angle = rotation + step * i as f32;
                center + Vec2::new(angle.cos(), angle.sin()) * radius
            })
            .collect()
    }

    fn polygon_vertices_with_bounds(
        center: Vec2,
        sides: u32,
        radius: f32,
        rotation: f32,
    ) -> (Vec<Vec2>, Vec2, Vec2) {
        let vertices = Self::regular_polygon_vertices(center, radius, sides, rotation);
        debug_assert!(vertices.len() >= 3);
        let mut min = vertices[0];
        let mut max = vertices[0];
        for v in &vertices[1..] {
            min = min.min(*v);
            max = max.max(*v);
        }
        (vertices, min, max)
    }

    fn polygon_uvs_for_sprite(
        vertices: &[Vec2],
        min: Vec2,
        size: Vec2,
        sprite: &Sprite,
    ) -> Vec<Vec2> {
        let tex_size = sprite.size.as_vec2().max(Vec2::splat(1.0));
        let (tex_min, tex_max) = sprite
            .src
            .as_ref()
            .map(|rect| (rect.min, rect.max))
            .unwrap_or((Vec2::ZERO, tex_size));
        let tex_extent = tex_max - tex_min;

        vertices
            .iter()
            .map(|v| {
                let rel_x = if size.x.abs() <= f32::EPSILON {
                    0.5
                } else {
                    (v.x - min.x) / size.x
                };
                let rel_y = if size.y.abs() <= f32::EPSILON {
                    0.5
                } else {
                    (v.y - min.y) / size.y
                };
                let mut rel = Vec2::new(rel_x, rel_y);
                if sprite.flip_x {
                    rel.x = 1.0 - rel.x;
                }
                if sprite.flip_y {
                    rel.y = 1.0 - rel.y;
                }
                let uv_pixels = tex_min + tex_extent * rel;
                uv_pixels / tex_size
            })
            .collect()
    }

    /// Draw circle
    ///
    /// # Arguments
    ///
    /// * `center` - Center point of the circle (x, y, z)
    /// * `radius` - Circle radius
    /// * `color` - Circle color
    ///
    /// # Examples
    ///
    /// ```
    /// # use roast2d_internal::prelude::*;
    /// // draw a blue circle at (50, 50) with radius 25 at z-index 0.5
    /// # fn draw(g: &mut Engine) {
    ///   g.draw_circle(
    ///       Vec3::new(50.0, 50.0, 0.5),
    ///       25.0,
    ///       BLUE
    ///   );
    /// # }
    /// ```
    pub fn draw_circle(&mut self, center: Vec3, radius: f32, color: Color) {
        /// Calculate bounding box for a circle with radius
        fn calculate_circle_bounds(center: Vec2, radius: f32) -> (Vec2, Vec2) {
            let min = center - Vec2::splat(radius);
            let max = center + Vec2::splat(radius);

            (min, max)
        }

        // Calculate bounding box
        let (min, max) = calculate_circle_bounds(center.xy(), radius);

        let size = max - min;

        // Create circle uniform data
        let circle_data = CircleUniform { center, radius };

        // Serialize to bytes for shader buffer
        let shader_data = to_wgsl_bytes(&circle_data);

        // Use CircleRenderer (reusable instance)
        let circle_renderer = self.render.circle_shader.clone();
        let transform = Transform::new(center);

        // Create Draw command for circle
        let cmd = Draw {
            shader: circle_renderer,
            shader_data: Some(shader_data),
            content: DrawContent::Circle { radius, color },
            src: None,
            transform,
            size,
            flip_x: false,
            flip_y: false,
            anchor: Vec2::splat(0.5),
            transparent: color.a < 1.0,
        };
        self.push_draw(cmd);
    }

    /// Draw image
    ///
    /// # Arguments
    ///
    /// * `image` - Sprite to draw
    /// * `transform` - Position
    ///
    /// # Examples
    ///
    /// ```
    /// # use roast2d_internal::prelude::*;
    /// // draw entity's sprite
    /// # fn draw(g: &mut Engine, sprite: Sprite) {
    ///   let transform = Transform::new(Vec2::ZERO.extend(1.0));
    ///   g.draw(
    ///       &sprite,
    ///       transform
    ///   );
    /// # }
    /// ```
    pub fn draw(&mut self, sprite: &Sprite, transform: Transform) {
        let renderer = self.render.shader.clone();
        let renderer_args = self.render.send_shader_data.take();
        let cmd = Draw::new(sprite, transform, renderer, renderer_args);
        self.push_draw(cmd);
    }

    fn push_draw(&mut self, cmd: Draw) {
        let camera = match self.camera_mode {
            CameraMode::Game => self.camera(),
            CameraMode::Ui => self.ui_camera(),
        };

        let view = camera.viewport();

        // ignore if bound is out of camera view
        let size = cmd.size * cmd.transform.scale;
        let b = cmd.transform.bounds_with_anchor_and_size(cmd.anchor, size);
        if b.min.x > view.max.x
            || b.min.y > view.max.y
            || b.max.x < view.min.x
            || b.max.y < view.min.y
        {
            return;
        }

        // push draw command
        match self.camera_mode {
            CameraMode::Game => {
                self.push_draw_cmd(Command::Draw(cmd));
            }
            CameraMode::Ui => {
                self.push_draw_cmd(Command::DrawUi(cmd));
            }
        }
    }

    /// View size
    pub fn view_size(&self) -> Vec2 {
        self.render.logical_size()
    }

    /// Screen size
    pub fn screen_size(&self) -> Vec2 {
        self.render.screen_size()
    }

    pub fn is_window_resized(&self) -> bool {
        self.is_window_resized
    }

    pub fn scale_mode(&self) -> ScaleMode {
        self.render.scale_mode()
    }

    /// Set scale mode
    pub fn set_scale_mode(&mut self, mode: ScaleMode) {
        self.render.set_scale_mode(mode)
    }

    // Return seconds since game start
    pub fn now(&mut self) -> f32 {
        self.clock.now()
    }

    // Camera
    pub fn camera(&self) -> &Camera {
        &self.camera
    }

    // Camera mut
    pub fn camera_mut(&mut self) -> &mut Camera {
        &mut self.camera
    }

    // Ui camera
    pub fn ui_camera(&self) -> &Camera {
        &self.ui_camera
    }

    // Ui camera
    pub fn ui_camera_mut(&mut self) -> &mut Camera {
        &mut self.ui_camera
    }

    // 3D camera (immutable)
    pub fn camera3d(&self) -> &crate::camera3d::Camera3D {
        &self.camera3d
    }

    // 3D camera (mutable)
    pub fn camera3d_mut(&mut self) -> &mut crate::camera3d::Camera3D {
        &mut self.camera3d
    }

    // Input
    pub fn input(&self) -> &InputState {
        &self.input
    }

    // Input mut
    pub fn input_mut(&mut self) -> &mut InputState {
        &mut self.input
    }

    pub(crate) fn init<Setup: FnOnce(&mut Engine)>(&mut self, setup: Setup) {
        self.time_real = self.now();
        self.init_default_font();

        setup(self);
    }

    pub(crate) fn init_default_font(&mut self) {
        // set default font
        let handle = self.assets.insert(Asset {
            asset_type: AssetType::Font,
            bytes: None,
        });
        let font =
            Font::from_bytes(DEFAULT_FONT_BYTES.into()).expect("Failed to load default font");
        self.font_manager.add_font(handle.id(), font);
        self.set_default_font(handle);
    }

    pub(crate) fn on_resize(&mut self, physical_size: UVec2, dpi_scale_factor: f64) {
        self.render.resize(physical_size, dpi_scale_factor);
        self.camera.resize(self.render.logical_size());
        self.ui_camera.resize(self.render.logical_size());
        self.is_window_resized = true;

        // resize post shader
        self.render.post_shader.resize(self);
    }

    /// Called per frame, the main update logic of engine
    pub(crate) fn update(&mut self) {
        let time_frame_start = self.now();

        if self.scene_next.is_some() {
            self.is_running = false;
            if let Some(mut scene) = self.scene.take() {
                scene.cleanup(self);
            }

            self.time = 0.;
            self.frame = 0.;

            if let Some(mut scene) = self.scene_next.take() {
                scene.init(self);
                self.scene = Some(scene);
            }
        }
        self.is_running = true;

        let time_real_now = self.now();
        let mut time_delta = time_real_now - self.time_real;

        // When use debugger with break the time_delta can be very large
        // we cap it to 1s / 30fps if it is large than 1.0
        if time_delta > 1.0 {
            time_delta = 1.0 / 30.0;
        }

        let tick = (time_delta * self.time_scale).min(ENGINE_MAX_TICK);
        if tick == 0.0 {
            // skip frame
            return;
        }
        log::debug!("now {time_real_now} tick {tick}");
        self.time_real = time_real_now;
        self.tick = tick;
        self.time += self.tick;
        self.frame += 1.;

        if let Some(mut scene) = self.scene.take() {
            scene.update(self);
            self.scene = Some(scene);
        }

        self.drive_asset_pipeline();

        self.perf.update = self.now() - time_real_now;

        if let Some(mut scene) = self.scene.take() {
            scene.draw(self);
            self.scene = Some(scene);
        }

        self.perf.draw = (self.now() - time_real_now) - self.perf.update;
        self.input.clear();
        self.is_window_resized = false;
        self.perf.total = self.now() - time_frame_start;
        log::trace!("Perf {:?}", &self.perf);
    }

    fn drive_asset_pipeline(&mut self) {
        if self.asset_fetch_job.is_none() {
            match self.assets.begin_fetch() {
                Ok(Some(request)) => {
                    self.asset_fetch_job = Some(AssetFetchJob::new(request));
                }
                Ok(None) => {}
                Err(err) => log::error!("Failed to start asset fetch: {err:?}"),
            }
        }

        if let Some(job) = &self.asset_fetch_job
            && let Some(result) = job.try_take()
        {
            self.asset_fetch_job = None;
            match result {
                Ok(outputs) => match self.assets.finish_fetch(outputs) {
                    Ok(tasks) => self.apply_asset_tasks(tasks),
                    Err(err) => log::error!("Failed to finalize asset fetch: {err:?}"),
                },
                Err(err) => log::error!("Asset fetch failed: {err:?}"),
            }
        }

        let ready_tasks = self.assets.take_ready_tasks();
        if !ready_tasks.is_empty() {
            self.apply_asset_tasks(ready_tasks);
        }

        let drop_tasks = self.assets.collect_drop_tasks();
        if !drop_tasks.is_empty() {
            self.apply_asset_tasks(drop_tasks);
        }
    }

    fn apply_asset_tasks(&mut self, tasks: Vec<FetchedTask>) {
        for task in tasks {
            match task {
                FetchedTask::CreateTexture { handle, data } => {
                    self.push_res_cmd(ResourceCommand::CreateTexture(CreateTexture {
                        handle,
                        data,
                    }));
                }
                FetchedTask::RemoveTexture { handle } => {
                    self.push_res_cmd(ResourceCommand::RemoveTexture(RemoveTexture(handle)));
                }
                FetchedTask::CreateFont { handle, font } => {
                    self.font_manager.add_font(handle.id(), font);
                }
                FetchedTask::RemoveFont { handle } => {
                    self.font_manager.remove_font(handle);
                }
            }
        }
    }

    fn default_texture_handle(&mut self) -> Handle {
        if let Some(handle) = &self.default_texture {
            return handle.clone();
        }

        let data = RgbaImage::from_vec(1, 1, vec![255, 255, 255, 255])
            .expect("Invalid RGBA image")
            .into();
        let handle = self.assets.insert(Asset {
            asset_type: AssetType::Texture,
            bytes: None,
        });
        self.push_res_cmd(ResourceCommand::CreateTexture(CreateTexture {
            handle: handle.clone(),
            data,
        }));
        self.default_texture = Some(handle.clone());
        handle
    }

    /// Set a scene, the scene swap do not happened instantly, it is happened in engine update
    pub fn set_scene(&mut self, scene: impl Scene + 'static) {
        self.scene_next.replace(Box::new(scene));
    }

    /// Whether the engine is running
    pub fn is_running(&self) -> bool {
        self.is_running
    }

    /// Get wgpu state
    pub fn backend_state(&self) -> &BackendState {
        &self.render.backend_state
    }

    /// With shader
    pub fn with_renderer<Ret, F: FnOnce(&mut Engine) -> Ret>(
        &mut self,
        renderer: Rc<dyn Shader + 'static>,
        f: F,
    ) -> Ret {
        let old_renderer = self.render.set_shader(renderer);
        let ret = f(self);
        self.render.set_shader(old_renderer);
        ret
    }

    /// Set shader
    pub fn set_renderer(&mut self, renderer: Option<Rc<dyn Shader + 'static>>) {
        match renderer {
            Some(renderer) => {
                self.render.set_shader(renderer);
            }
            None => {
                let renderer: Rc<dyn Shader> =
                    Rc::new(DefaultShader::new(&self.render.backend_state));
                self.render.set_shader(renderer);
            }
        }
    }

    /// Send args to renderer
    /// The args only used for next draw call
    pub fn send_renderer_data(&mut self, data: Vec<u8>) {
        self.render.send_shader_data(data);
    }

    /// Schedule egui UI for this frame.
    ///
    /// This is an immediate-style API; call it every frame where you want egui to draw:
    ///
    /// ```ignore
    /// g.egui(|ctx, _g| {
    ///     egui::Window::new("Debug").show(ctx, |ui| {
    ///         ui.label("Hello egui");
    ///     });
    /// });
    /// ```
    #[cfg(feature = "egui")]
    pub fn egui<F>(&mut self, f: F)
    where
        F: FnMut(&egui::Context, &mut Engine) + 'static,
    {
        self.egui_layers.push(Box::new(f));
    }

    /// Run all registered egui layers for the current frame.
    #[cfg(feature = "egui")]
    pub(crate) fn run_egui_layers(&mut self, ctx: &egui::Context) {
        let layers = core::mem::take(&mut self.egui_layers);
        for mut layer in layers {
            (layer)(ctx, self);
        }
    }

    /// Setup post renderer
    pub fn set_post_renderer<R: PostShader + 'static>(&mut self, post_renderer: Option<R>) {
        let renderer: Box<dyn PostShader> = if let Some(post_renderer) = post_renderer {
            Box::new(post_renderer)
        } else {
            Box::new(ScreenShader::setup(&self.render.backend_state))
        };
        renderer.resize(self);
        self.render.set_post_shader(renderer);
    }

    pub fn text_cache(&mut self) -> &mut FontManager {
        &mut self.font_manager
    }
}

struct AssetFetchJob {
    result: Arc<Mutex<Option<Result<Vec<AssetFetchOutput>>>>>,
}

impl AssetFetchJob {
    fn new(request: AssetFetchRequest) -> Self {
        let result = Arc::new(Mutex::new(None));
        let result_clone = Arc::clone(&result);
        tasks::spawn_local(async move {
            let AssetFetchRequest { reader, tasks } = request;
            let mut outputs = Vec::with_capacity(tasks.len());
            for task in tasks {
                match reader.read(&task.path).await {
                    Ok(bytes) => outputs.push(AssetFetchOutput {
                        handle: task.handle,
                        asset_type: task.asset_type,
                        bytes,
                    }),
                    Err(err) => {
                        *result_clone.lock().unwrap() = Some(Err(err));
                        return;
                    }
                }
            }
            *result_clone.lock().unwrap() = Some(Ok(outputs));
        });

        Self { result }
    }

    fn try_take(&self) -> Option<Result<Vec<AssetFetchOutput>>> {
        self.result.lock().unwrap().take()
    }
}
