use super::context::CameraMode;
use nightshade::prelude::*;

#[derive(Clone, Copy, PartialEq)]
pub enum CameraView {
    Front,
    Right,
    Top,
}

pub fn set_camera_view(world: &mut World, view: CameraView) {
    let Some(camera_entity) = world.resources.active_camera else {
        return;
    };

    let Some(pan_orbit) = world.core.get_pan_orbit_camera_mut(camera_entity) else {
        return;
    };

    let (yaw, pitch) = match view {
        CameraView::Front => (0.0, 0.0),
        CameraView::Right => (std::f32::consts::FRAC_PI_2, 0.0),
        CameraView::Top => (0.0, std::f32::consts::FRAC_PI_2 - 0.001),
    };

    pan_orbit.target_yaw = yaw;
    pan_orbit.target_pitch = pitch;
}

pub fn toggle_camera_orthographic(world: &mut World) {
    use nightshade::ecs::camera::components::{OrthographicCamera, PerspectiveCamera};

    let Some(camera_entity) = world.resources.active_camera else {
        return;
    };

    let camera_distance = world
        .core
        .get_pan_orbit_camera(camera_entity)
        .map(|orbit| orbit.radius)
        .unwrap_or(10.0);

    let Some(camera) = world.core.get_camera_mut(camera_entity) else {
        return;
    };

    camera.projection = match &camera.projection {
        Projection::Perspective(persp) => {
            let half_height = camera_distance * (persp.y_fov_rad * 0.5).tan();
            let aspect = persp.aspect_ratio.unwrap_or(16.0 / 9.0);
            let half_width = half_height * aspect;
            Projection::Orthographic(OrthographicCamera {
                x_mag: half_width,
                y_mag: half_height,
                z_near: persp.z_near,
                z_far: persp.z_far.unwrap_or(1000.0),
            })
        }
        Projection::Orthographic(ortho) => Projection::Perspective(PerspectiveCamera {
            y_fov_rad: 45.0_f32.to_radians(),
            aspect_ratio: None,
            z_near: ortho.z_near,
            z_far: Some(ortho.z_far),
        }),
    };
}

pub fn toggle_camera_mode(world: &mut World, camera_mode: &mut CameraMode) {
    let Some(camera_entity) = world.resources.active_camera else {
        return;
    };

    match *camera_mode {
        CameraMode::Orbit => {
            if let Some(camera) = world.core.get_camera_mut(camera_entity) {
                camera.smoothing = Some(nightshade::ecs::camera::components::Smoothing::default());
            }
            *camera_mode = CameraMode::Fly;
        }
        CameraMode::Fly => {
            if let Some(camera) = world.core.get_camera_mut(camera_entity) {
                camera.smoothing = None;
            }

            let (position, forward) =
                if let Some(transform) = world.core.get_local_transform(camera_entity) {
                    (transform.translation, transform.forward_vector())
                } else {
                    (Vec3::zeros(), nalgebra_glm::vec3(0.0, 0.0, -1.0))
                };

            let focus = position + forward * 5.0;

            let direction = position - focus;
            let radius = nalgebra_glm::length(&direction);
            let horizontal = nalgebra_glm::vec3(direction.x, 0.0, direction.z);
            let yaw = horizontal.x.atan2(horizontal.z);
            let pitch = (direction.y / radius).asin();

            if let Some(pan_orbit) = world.core.get_pan_orbit_camera_mut(camera_entity) {
                pan_orbit.focus = focus;
                pan_orbit.target_focus = focus;
                pan_orbit.radius = radius;
                pan_orbit.target_radius = radius;
                pan_orbit.yaw = yaw;
                pan_orbit.target_yaw = yaw;
                pan_orbit.pitch = pitch;
                pan_orbit.target_pitch = pitch;
            }

            *camera_mode = CameraMode::Orbit;
        }
    }
}

pub fn is_mouse_in_selected_viewport(world: &World) -> bool {
    if let Some(viewport_rect) = &world.resources.window.active_viewport_rect {
        let mouse_pos = world.resources.input.mouse.position;
        return viewport_rect.contains(mouse_pos);
    }
    true
}
