#include "CTransform.hpp"

namespace CosmicCore
{

	void CTransform::calcTranslation(void) {
		m_translation = glm::translate(glm::mat4(1.0f), m_center);
	}

	void CTransform::calcRotation(void) {
		m_rotation = glm::toMat4(m_orientation);
	}

	void CTransform::calcHomothety(void) {
		m_homothety = glm::scale(glm::mat4(1.0f), m_scale);
	}

	void CTransform::calcTranformation(void) {
		m_transformation = m_translation * m_rotation * m_homothety;
	}

	CTransform::CTransform(CEntity& entity) : CAbstractComponent(entity),
		m_center(0.0f, 0.0f, 0.0f),
		m_scale(1.0f, 1.0f, 1.0f),
		m_orientation(),
		m_observers() {
		calcTranslation();
		calcRotation();
		calcHomothety();
		calcTranformation();
	}

	CTransform::CTransform(CEntity& entity, glm::vec3 center, glm::vec3 scale, glm::vec3 eulerAngle) : CAbstractComponent(entity),
		m_center(center),
		m_scale(scale),
		m_orientation(eulerAngle),
		m_observers() {
		calcTranslation();
		calcRotation();
		calcHomothety();
		calcTranformation();
	}

	glm::mat4 CTransform::getTranslation(void) const {
		return m_translation;
	}

	glm::mat4 CTransform::getRotation(void) const {
		return m_rotation;
	}

	glm::mat4 CTransform::getHomothety(void) const {
		return m_homothety;
	}

	glm::mat4 CTransform::getTransormation(void) const {
		return m_transformation;
	}

	glm::mat4 CTransform::getInheritedTransformation(void) const {
		/*const auto& entity = getEntity();
		if(entity)
		{
			if (entity->getParent().expired()) {
				return m_transformation;
			}
			else {
				auto p = entity->getParent().lock();
				return p->getTransform().getInheritedTransformation() * m_transformation;
			}
		}*/
	}

	glm::vec3 CTransform::getCenter(void) const {
		return m_center;
	}

	glm::vec3* CTransform::getCenterReference(void)
	{
		return &m_center;
	}

	glm::vec3 CTransform::getScale(void) const {
		return m_scale;
	}

	glm::vec3& CTransform::getScalePtr(void)
	{
		return m_scale;
	}

	glm::vec3 CTransform::getEulerAngle(void) const {
		return glm::eulerAngles(m_orientation);
	}

	glm::quat CTransform::getOrientation(void) const
	{
		return m_orientation;
	}

	void CTransform::setCenter(glm::vec3 newCenter) {
		m_center = newCenter;
		calcTranslation();
		calcTranformation();
		for (auto const& obs : m_observers) {
			if(auto ptrObs = obs.lock())
				ptrObs->onCenterChange();
		}
	}

	void CTransform::setUniformScale(float newScale) {
		m_scale = glm::vec3(newScale, newScale, newScale);
		calcHomothety();
		calcTranformation();
		for (auto const& obs : m_observers) {
			if(auto ptrObs = obs.lock())
				ptrObs->onScaleChange();
		}
	}

	void CTransform::setScale(glm::vec3 newScale) {
		m_scale = newScale;
		calcHomothety();
		calcTranformation();
		for (auto const& obs : m_observers) {
			if(auto ptrObs = obs.lock())
				ptrObs->onScaleChange();
		}
	}

	void CTransform::rotate(glm::vec3 axis, float angle) {
		m_orientation = glm::rotate(m_orientation, angle, axis);
		calcRotation();
		calcTranformation();
		for (auto const& obs : m_observers) {
			if(auto ptrObs = obs.lock())
				ptrObs->onRotationChange();
		}
	}

	void CTransform::setEulerAngle(glm::vec3 newEulerAngle) {
		m_orientation = glm::quat(newEulerAngle);
		calcRotation();
		calcTranformation();
		for (auto const& obs : m_observers) {
			if(auto ptrObs = obs.lock())
				ptrObs->onRotationChange();
		}
	}

	void CTransform::setQuaternion(glm::quat newQuaternion) {
		m_orientation = newQuaternion;
		calcRotation();
		calcTranformation();
		for (auto const& obs : m_observers) {
			if(auto ptrObs = obs.lock())
				ptrObs->onRotationChange();
		}
	}

		void CTransform::forward(glm::vec3 value) {
		glm::vec3 relValue = m_orientation * value;
		m_center += relValue;
		calcTranslation();
		calcTranformation();
		for (auto const& obs : m_observers) {
			if(auto ptrObs = obs.lock())
				ptrObs->onCenterChange();
		}
	}

	void CTransform::addObserver(std::weak_ptr<CTransformObserver> newObserver) {
		m_observers.emplace_back(newObserver);
	}

	void CTransform::removeObserver(std::weak_ptr<CTransformObserver> obs)
	{
		//TODO
	}
}