use nalgebra_glm::{Quat, Vec3};

#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ThirdPersonCamera {
    pub follow_target: Option<freecs::Entity>,
    pub yaw: f32,
    pub pitch: f32,
    pub distance: f32,
    pub target_yaw: f32,
    pub target_pitch: f32,
    pub target_distance: f32,
    pub height_offset: f32,
    pub pitch_upper_limit: f32,
    pub pitch_lower_limit: f32,
    pub distance_min: f32,
    pub distance_max: f32,
    pub orbit_sensitivity: f32,
    pub zoom_sensitivity: f32,
    pub orbit_smoothness: f32,
    pub zoom_smoothness: f32,
    pub follow_smoothness: f32,
    pub collision_enabled: bool,
    pub collision_radius: f32,
    pub current_focus: Vec3,
}

impl Default for ThirdPersonCamera {
    fn default() -> Self {
        Self {
            follow_target: None,
            yaw: 0.0,
            pitch: 0.3,
            distance: 8.0,
            target_yaw: 0.0,
            target_pitch: 0.3,
            target_distance: 8.0,
            height_offset: 1.5,
            pitch_upper_limit: std::f32::consts::FRAC_PI_2 - 0.05,
            pitch_lower_limit: -0.3,
            distance_min: 1.0,
            distance_max: 30.0,
            orbit_sensitivity: 1.0,
            zoom_sensitivity: 1.0,
            orbit_smoothness: 0.08,
            zoom_smoothness: 0.1,
            follow_smoothness: 0.05,
            collision_enabled: true,
            collision_radius: 0.3,
            current_focus: Vec3::zeros(),
        }
    }
}

impl ThirdPersonCamera {
    pub fn new(distance: f32) -> Self {
        Self {
            distance,
            target_distance: distance,
            ..Default::default()
        }
    }

    pub fn with_target(mut self, target: freecs::Entity) -> Self {
        self.follow_target = Some(target);
        self
    }

    pub fn with_yaw_pitch(mut self, yaw: f32, pitch: f32) -> Self {
        self.yaw = yaw;
        self.pitch = pitch;
        self.target_yaw = yaw;
        self.target_pitch = pitch;
        self
    }

    pub fn with_height_offset(mut self, offset: f32) -> Self {
        self.height_offset = offset;
        self
    }

    pub fn with_distance_limits(mut self, min: f32, max: f32) -> Self {
        self.distance_min = min;
        self.distance_max = max;
        self
    }

    pub fn with_pitch_limits(mut self, min: f32, max: f32) -> Self {
        self.pitch_lower_limit = min;
        self.pitch_upper_limit = max;
        self
    }

    pub fn with_smoothness(mut self, orbit: f32, zoom: f32, follow: f32) -> Self {
        self.orbit_smoothness = orbit;
        self.zoom_smoothness = zoom;
        self.follow_smoothness = follow;
        self
    }

    pub fn compute_camera_transform(&self, focus: Vec3) -> (Vec3, Quat) {
        compute_third_person_transform(focus, self.yaw, self.pitch, self.distance)
    }
}

pub fn compute_third_person_transform(
    focus: Vec3,
    yaw: f32,
    pitch: f32,
    distance: f32,
) -> (Vec3, Quat) {
    let yaw_quat = nalgebra_glm::quat_angle_axis(yaw, &Vec3::y());
    let pitch_quat = nalgebra_glm::quat_angle_axis(-pitch, &Vec3::x());
    let rotation = yaw_quat * pitch_quat;
    let offset = nalgebra_glm::quat_rotate_vec3(&rotation, &Vec3::new(0.0, 0.0, distance));
    let position = focus + offset;
    (position, rotation)
}
