use nalgebra_glm as glm;

use crate::model::Model;

use std::rc::Rc;
use std::cell::RefCell;

pub type NodeRef = Rc<RefCell<Node>>;

pub struct Node {
    pub name: String,
    pub gameobject: GameObject,
    pub children: Vec<NodeRef>,
}

pub trait Childbearing<T> { // TODO remove this
    fn add_child(&mut self, name: &str, child: T);
}

impl Node {
    pub fn find_node(&mut self, name: &str) -> Option<NodeRef> {
	match self.children.iter().find(|node| node.borrow().name == name) {
	    Some(node) => Some(Rc::clone(node)),
	    None => None
	}
    }
}

macro_rules! impl_childbearing {
    ($t:ty, $e:path) => {
	impl Childbearing<$t> for Node {
	    fn add_child(&mut self, name: &str, child: $t) {
		self.children.push(Rc::new(RefCell::new(Node::new(name.to_string(), $e(child)))));
	    }
	}
    }
}



impl Node {
    pub fn new(name: String, gameobject: GameObject) -> Self {
	Node {
	    name,
	    gameobject,
	    children: Vec::new(),
	}
    }
}

pub enum GameObject {
    Null,
    Camera(Camera),
    Mesh(Mesh),
}

impl GameObject {
    pub fn get_transform(&self) -> Result<Transform, String> {
	use GameObject::*;
	match self {
	    Camera(camera) => Ok(camera.get_transform()),
	    Mesh(mesh) => Ok(mesh.get_transform()),
	    _ => Err("object does not implement \"get_transform\"".to_string())
	}
    }
    pub fn set_transform(&mut self, transform: Transform) -> Result<(), String> {
	use GameObject::*;
	match self {
	    Camera(camera) => { camera.set_transform(transform); Ok(()) }
	    Mesh(mesh) => { mesh.set_transform(transform); Ok(()) }
	    _ => Err("object does not implement \"set_transform\"".to_string())
	}
    }
}

#[derive(Clone, Copy)]
pub struct Transform {
    pub position: glm::Vec3,
    pub rotation: glm::Vec3,
    pub scale: glm::Vec3,
}

impl Default for Transform {
    fn default() -> Self {
	Transform {
	    position: glm::zero(),
	    rotation: glm::zero(),
	    scale: glm::vec3(1.0, 1.0, 1.0),
	}
    }
}

pub trait Spatial {
    fn get_transform(&self) -> Transform;
    fn set_transform(&mut self, transform: Transform);
}

pub struct Camera {
    transform: Transform,
}

impl Camera {
    pub fn new() -> Self {
	Camera {
	    transform: Default::default(),
	}
    }
}

impl Spatial for Camera {
    fn get_transform(&self) -> Transform {
	self.transform
    }
    fn set_transform(&mut self, transform: Transform) {
	self.transform = transform
    }
}

impl_childbearing!(Camera, GameObject::Camera);

pub struct Mesh {
    pub model: Model,
    transform: Transform,
}

impl Mesh {
    pub fn new(model: Model) -> Self {
	Mesh {
	    model,
	    transform: Default::default(),
	}
    }
}

impl Spatial for Mesh {
    fn get_transform(&self) -> Transform {
	self.transform
    }
    fn set_transform(&mut self, transform: Transform) {
	self.transform = transform;
    }
}

impl_childbearing!(Mesh, GameObject::Mesh);
