use macroquad::prelude as mq;
use nalgebra as na;
use rapier3d::prelude as rp;
use std::any::Any;

use crate::{camera::CameraController, engine::World};

pub struct AttachedCamera {
    pub attached_handle: rp::RigidBodyHandle,
    pub front_local_direction: na::Vector3<f32>,
    pub offset: na::Vector3<f32>,
    translation: na::Vector3<f32>,
    front: na::Vector3<f32>,
    up: na::Vector3<f32>,
}

impl AttachedCamera {
    pub fn new(
        rb_handle: rp::RigidBodyHandle,
        front_direction: na::Vector3<f32>,
        offset: na::Vector3<f32>,
    ) -> AttachedCamera {
        AttachedCamera {
            attached_handle: rb_handle,
            front_local_direction: front_direction,
            offset,
            translation: Default::default(),
            front: Default::default(),
            up: Default::default(),
        }
    }
}

impl CameraController for AttachedCamera {
    fn get_position(&self) -> mq::Vec3 {
        self.translation.into()
    }
    fn get_front(&self) -> mq::Vec3 {
        self.front.into()
    }

    fn apply(&self) {
        mq::set_camera(&mq::Camera3D {
            position: self.translation.into(),
            up: self.up.into(),
            target: (self.translation + self.front).into(),
            fovy: 2.0,
            ..Default::default()
        });
    }
    fn update(&mut self, _delta: f32, world: &World) {
        let rb = world.bodies.get(self.attached_handle).unwrap();
        self.translation = *rb.translation() + rb.rotation().transform_vector(&self.offset);
        self.up = rb.rotation().transform_vector(&na::vector![0.0, 1.0, 0.0]);
        self.front = rb.position().transform_vector(&self.front_local_direction);
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
    fn as_mut_any(&mut self) -> &mut dyn Any {
        self
    }
}
