use glam::Vec3;

use crate::components::FollowComponent;
use crate::utility::input::InputState;
use crate::world::{Transform, World};

pub fn camera_view_matrix(world: &World) -> Option<glam::Mat4>
{
    let (camera_entity, camera_component) = world.active_camera()?;
    let camera_transform = world.transforms.get(camera_entity)?;

    if let Some(follow) = world.follows.get(camera_entity)
    {
        if let Some(target_transform) = world.transforms.get(follow.target)
        {
            return Some(glam::Mat4::look_at_rh(
                camera_transform.position,
                target_transform.position,
                glam::Vec3::Y,
            ));
        }
    }

    let forward = camera_component.get_forward();
    let target = camera_transform.position + forward;
    Some(glam::Mat4::look_at_rh(
        camera_transform.position,
        target,
        glam::Vec3::Y,
    ))
}

pub fn camera_input_system(world: &mut World, input_state: &InputState)
{
    let cameras: Vec<_> = world.cameras.all();

    for camera_entity in cameras
    {
        if let Some(camera) = world.cameras.get_mut(camera_entity)
        {
            if !camera.is_active
            {
                continue;
            }

            if input_state.mouse_delta.0.abs() > 0.0 || input_state.mouse_delta.1.abs() > 0.0
            {
                let is_following = world.follows.get(camera_entity).is_some();

                camera.yaw += input_state.mouse_delta.0 * 0.0008;

                if is_following
                {
                    camera.pitch += input_state.mouse_delta.1 * 0.0008;
                }
                else
                {
                    camera.pitch -= input_state.mouse_delta.1 * 0.0008;
                }

                camera.pitch = camera
                    .pitch
                    .clamp(-89.0_f32.to_radians(), 89.0_f32.to_radians());
            }
        }
    }
}

pub fn camera_follow_system(world: &mut World)
{
    let camera_entities: Vec<_> = world.follows.all();

    for camera_entity in camera_entities
    {
        if let Some(camera) = world.cameras.get(camera_entity)
        {
            if let Some(follow) = world.follows.get(camera_entity)
            {
                let target_entity = follow.target;
                let offset = follow.offset.position;

                if let Some(target_transform) = world.transforms.get(target_entity)
                {
                    let target_position = target_transform.position;
                    let distance = offset.length();

                    let orbit_yaw = camera.yaw + std::f32::consts::PI;

                    let offset_x = distance * orbit_yaw.cos() * camera.pitch.cos();
                    let offset_y = distance * camera.pitch.sin();
                    let offset_z = distance * orbit_yaw.sin() * camera.pitch.cos();

                    let new_offset = Vec3::new(offset_x, offset_y, offset_z);

                    world
                        .transforms
                        .with_mut(camera_entity, |camera_transform| {
                            camera_transform.position = target_position + new_offset;
                        });

                    world.follows.components.get_mut(&camera_entity).map(|f| {
                        f.offset.position = new_offset;
                    });
                }
            }
        }
    }
}

pub fn camera_noclip_system(world: &mut World, input_state: &InputState, delta: f32)
{
    let cameras: Vec<_> = world.cameras.all();

    for camera_entity in cameras
    {
        if let Some(camera) = world.cameras.get(camera_entity)
        {
            if !camera.is_active
            {
                continue;
            }

            let forward = camera.get_forward();
            let right = camera.get_right();

            let mut input_vec = Vec3::ZERO;

            if input_state.w
            {
                input_vec.z += 1.0;
            }
            if input_state.s
            {
                input_vec.z -= 1.0;
            }
            if input_state.d
            {
                input_vec.x += 1.0;
            }
            if input_state.a
            {
                input_vec.x -= 1.0;
            }
            if input_state.space
            {
                input_vec.y += 10.0;
            }

            if input_vec.length_squared() > 0.0
            {
                input_vec = input_vec.normalize();
            }

            let mut speed = 10.0 * delta;
            if input_state.shift
            {
                speed *= 10.0;
            }

            if let Some(camera_transform) = world.transforms.get_mut(camera_entity)
            {
                camera_transform.position += forward * input_vec.z * speed;
                camera_transform.position += right * input_vec.x * speed;
                camera_transform.position += Vec3::Y * input_vec.y * speed;
            }
        }
    }
}

pub fn start_camera_following(world: &mut World, camera_entity: crate::entity::EntityHandle)
{
    if let Some(camera_transform) = world.transforms.get(camera_entity)
    {
        let player_entities = world.player_tags.all();
        if let Some(&player_entity) = player_entities.first()
        {
            if let Some(target_transform) = world.transforms.get(player_entity)
            {
                let offset = camera_transform.position - target_transform.position;
                let distance = offset.length();

                if distance > 0.0
                {
                    if let Some(camera) = world.cameras.get_mut(camera_entity)
                    {
                        camera.pitch = (offset.y / distance).asin();
                        camera.yaw = offset.z.atan2(offset.x) + std::f32::consts::PI;
                    }
                }

                world.follows.insert(
                    camera_entity,
                    FollowComponent {
                        target: player_entity,
                        offset: Transform::from_position(offset),
                        inherit_rotation: false,
                        inherit_scale: false,
                    },
                );
            }
        }
    }
}

pub fn stop_camera_following(world: &mut World, camera_entity: crate::entity::EntityHandle)
{
    if let Some(follow) = world.follows.get(camera_entity)
    {
        let target_entity = follow.target;

        if let Some(camera_transform) = world.transforms.get(camera_entity)
        {
            if let Some(target_transform) = world.transforms.get(target_entity)
            {
                let look_direction =
                    (target_transform.position - camera_transform.position).normalize();

                if let Some(camera) = world.cameras.get_mut(camera_entity)
                {
                    camera.yaw = look_direction.z.atan2(look_direction.x);
                    camera.pitch = look_direction.y.asin();
                }
            }
        }

        world.follows.remove(camera_entity);
    }
}
