#ifndef _HANDLE_H_
#define _HANDLE_H_

#include <vector>
#include "headers\math_headers.h"
#include "headers\opengl_headers.h"

class Handle
{
public:
	Handle() {init();}
	Handle(const std::vector<unsigned int>& indices, const VectorX& vertices, bool mode, const glm::vec3& color, const int id) : 
		m_indices(indices), 
		m_original_x(vertices), 
		m_mode(mode),
		m_color(color),
		m_handle_id(id)
	{
		init();
	}
    Handle(const Handle& other) : 
        m_indices(other.m_indices), 
        m_original_x(other.m_original_x),
		m_original_centor_of_mass(other.m_original_centor_of_mass),
		m_x(other.m_x),
        m_mode(other.m_mode),
		m_dim(other.m_dim),
		m_translation(other.m_translation),
		m_rotation_angle_2d(other.m_rotation_angle_2d),
		m_temp_rotation_angle_2d(other.m_temp_rotation_angle_2d),
		m_rotation_primal_quat_3d(other.m_rotation_primal_quat_3d),
		m_temp_rotation_primal_quat_3d(other.m_temp_rotation_primal_quat_3d),
		m_color(other.m_color),
		m_handle_id(other.m_handle_id)
    {
		update();
    }
	~Handle() {cleanup();}

	void Reset();
	bool Select(std::vector<glm::vec3> ray);
	ScalarType BoundingBoxSize();
	glm::vec3 GetCoM();
	glm::vec3 GetLocalCoM();
	void MoveTemporary(const glm::vec3& trans);
	void MoveFinalize();
	void RotateToValue(ScalarType theta);
	void RotateTemporary(const glm::vec3& axis, const float& theta);
	void RotateFinalize();
	void ChangeTranslationOnXAxis(ScalarType t);
	void RotateExtra(ScalarType theta);

	void Draw(const VBO& vbos, bool selected = false);

	ScalarType HandleHandleDifference(const Handle& other);

	// data accesser
	inline std::vector<unsigned int>& Indices() {return m_indices;}
	inline glm::vec3& Color() {return m_color;}
	inline EigenMatrix& GetTransformationMatrix() {return m_T; }
	inline ScalarType& RotationAngle2d() {return m_rotation_angle_2d;}
	inline EigenQuaternion& Rotation3d() {return m_rotation_primal_quat_3d;}
	inline VectorX& Translation() {return m_translation;}
	inline VectorX& CoM() {return m_original_centor_of_mass;}
	inline EigenMatrix GetRotationMatrix() {return m_T.block(0, 0, m_dim-1, m_dim-1); }
	inline unsigned int& ID() {return m_handle_id;}
	inline const VectorX& Vertices() {return m_x;}
	inline VectorX operator[] (int i) {return m_x.block(i*m_dim, 0, m_dim, 1);}

protected:

	// mode
	bool m_mode;
	unsigned int m_dim;
	// key components
	std::vector<unsigned int> m_indices;
	VectorX m_original_x;
	VectorX m_original_centor_of_mass;
	VectorX m_x;
	// translation
	VectorX m_temp_translation;
	VectorX m_translation;
	// Scaling
	ScalarType m_scaling;
	// 2d rotation
	ScalarType m_temp_rotation_angle_2d;
	ScalarType m_rotation_angle_2d;
	// 3d rotation
	EigenQuaternion m_temp_rotation_primal_quat_3d;
	EigenQuaternion m_rotation_primal_quat_3d;
	// transformation matrix 3x3 in 2d case and 4x4 in 3d case
	EigenMatrix m_T;

	// for visualization and selection and identity
	unsigned int m_handle_id;
	glm::vec3 m_color;
	glm::vec3 m_bounding_box_min;
	glm::vec3 m_bounding_box_max;

protected:
	void init();
	void cleanup();
	bool insidebb(const glm::vec3& p);

	void updateTransformationMatrix();
	void update();
};

#endif