#ifndef _SIMULATION_H_
#define _SIMULATION_H_

#include <vector>

#include "headers\global_headers.h"
#include "headers\opengl_headers.h"
#include "headers\math_headers.h"
#include "mesh.h"
#include "handle.h"
#include "anttweakbar_wrapper.h"
#include "core\nis_solver.h"

// forward declaration
class Mesh;
class AntTweakBarWrapper;

typedef enum
{
	SKINNING_LBS,
	SKINNING_DQS,
	SKINNING_EXROT,
	SKINNING_RANDOM_ELMT_EXROT,
	SKINNING_COROT,
	SKINNING_TOTAL_NUM

} SkinningMethod;

class Sim
{
	friend class AntTweakBarWrapper;

public:
	Sim() {}
	~Sim() {}
	
	void Reset(Mesh* mesh, bool mode);
	void Cleanup();
	void UpdateLBS();
	void Update();
	void UpdateMatlabSDPSolveYalip(bool has_max_it = true);
	void UpdateMatlabSDPSolveEpigraph();
	void Draw(const VBO& vbos); // draw handles
	void GetOverlayChar(char* overlay_char);

	// save/load state
	void SaveSimulation();
	void LoadSimulation();

	// handles
	void NewHandle(const std::vector<unsigned int>& indices, const glm::vec3 color);
	void DeleteHandle();
	bool SelectHandle(std::vector<glm::vec3> ray);
	void MoveHandleTemporary(const glm::vec3& trans);
	void MoveHandleFinalize();
	void RotateHandleToValue();
	void TranslateHandleSetStepSize();
	void AnimateHandle();
	void RotateHandleSetStepSize();
	void RotateHandleTemporary(const glm::vec3& axis, const float& theta);
	void RotateHandleFinalize();
	//void CombineVertexClassificationRegions(std::vector<RegionClassification>& rc);
	glm::vec3 SelectedHandleLocalCoM();
	glm::vec3 SelectedHandleCoM();
	void SaveHandles();
	void LoadHandles();
	void UpdateHandleInfo();

	// skin weights
	void ComputeSkinWeights();
	void VertexWeightToElementWeight();

	void ShowSkinWeights(std::vector<glm::vec3>& color);
	void SaveSkinWeights();
	void LoadSkinWeights();
	void SaveTargetPoints();
	void SaveExrotationField();
	void SavePerElementWeight();

	// solver
	void InitSolver();
	void OutputDetFFromSolver(std::vector<float>& detf_list);
	void OutputW11FromSolver(std::vector<float>& w11_list);
	void OutputDetWFromSolver(std::vector<float>& detw_list);
	ScalarType OutputMinDetWFromSolver();

	// member accessor
	inline VectorX& V() {return m_x;}
	inline ScalarType LastStepProgress() {return m_last_iteration_progress;}
	inline unsigned int Iteration() {return m_solver.m_iteration_num;}
	inline void NeedUpdate() {m_need_update = true;}
	inline void ChangeMethod() {m_solver.ResetParameterToOptimizationMethod();}

protected:
	void convertVerticesToBBWFormat(EigenMatrix& V);
	void convertElementListToBBWFormat(Eigen::MatrixXi& T);
	bool convertHandlesToBBWFormat(Eigen::VectorXi& b, EigenMatrix& bc);

public:
	NISSolver m_solver;

protected:
	Mesh* m_mesh;
	
	// basic data
	bool m_mode;
	unsigned int m_vertices_number;
	unsigned int m_system_dimension;
	EigenMatrix m_original_x; // nx2 or nx3: original vertices positions for simulation can be either 2d or 3d
	VectorX m_x; // 2nx1 or 3nx1: vertices positions for simulation can be either 2d or 3d, for simulation
	VectorX m_dual_lambda; // number of elements x 1

	// stats
	ScalarType m_last_iteration_progress;

	// state control
	bool m_need_update;

	// skinning weights
	EigenMatrix m_weights; // skinning weights for all vertices
	EigenMatrix m_element_weights; // skinning weights for all elements

	// handle control
	std::vector<int> m_handle_id; // the id of the correspond handle of a vertex, -1 if no handle
	int m_selected_handle_id;
	std::vector<Handle> m_handles;
	int m_keyframe_handle_id;
	ScalarType m_keyframe_handle_unit_translation;
	ScalarType m_keyframe_handle_unit_rotation;

	// depricated, keep it for reference with the uncleaned version
	SkinningMethod m_method;
	bool m_auto_phase_1_method;
	unsigned int m_iteration_num_simulation;
};

#endif