//	*******************************************************************************
//	Name	:	glut_cv.hxx
//			:	callback GLUT
//	Author	:	I. Nakagawa
//	Create	:	2019/10/24
//	Modify	:	2019/10/24		
//	*******************************************************************************

#pragma		once

#include	"glut_cb.hxx"
#include	"vxx_fnc3.hxx"
#include	"extent.hxx"
#include	"i_define.hxx"

#define		cv_init			cb_init
//#define 	cv_keyboard		cb_keyboard
#define		cv_idle			cb_idle
//#define 	cv_resize		cb_resize

////
//	*******************************************************************************
//	eye ...
//	Create	:	2019/10/24
//	*******************************************************************************
class	C_glut	{
public:
	C_glut	()	{
			EP = Vd3(7,-10, 6) ;
			TP = Vd3(2,  5, 3) ;
			Up = Vd3(0,  0, 1) ;
			Distnc = LastDs = 50 ;
			Anchor = Vd2(-1) ;
			Button = -1 ;
			D_min  =  10 ;
			D_max  = 200 ;
			Rotate = false ;
			}
public:
	Vd3		EP ;			//	Eye
	Vd3		TP ;			//	Target
	Vd3		Up ;			//	up
	double	Distnc ;		//	distance
	Vd4		BG ;			//	BG color
	bool	Rotate ;		//	rotate
public:
	int		Button ;		//	button		Left : Eye ,	Middle : Distance
	Vd2		Anchor ;		//	Eye Drag Start
	Vd3		LastEP ;		//	Last eye
	double	LastDs ;		//	Last distnce
public:
	Ed3		Ext ;			//	extent
	double	D_min ;			//	distance min
	double	D_max ;			//	distancd max
	} ;

inline	C_glut*	get_c_glut	(void)
{
	static	C_glut	G_c_glut ;
	return	&G_c_glut ;
	}

////
//	*******************************************************************************
//	ClearColor
//	Create	:	2019/10/25
//	*******************************************************************************
inline	void	ClearColor	(const Vd4& bg)
{
	::glClearColor(float(bg.x),float(bg.y),float(bg.z),float(bg.w)) ;
	#ifdef	_DEBUG
	//	std::tout	<<	::V4_To_tstring(bg)	<<	std::endl ;
	#endif
	}

////
//	*******************************************************************************
//	mouse
//	Create	:	2019/10/24
//	*******************************************************************************
inline	void	cv_mouse	(int button,int state,int x,int y)
{
	C_glut*	gm = ::get_c_glut() ;
//#ifdef	_DEBUG
//	std::tout	<<	_T("Button=")	<<	button	<<	_T("\t")	<<	x	<<	_T(" , ")	<<	y	<<	_T("\t")	<<	state	<<	std::endl ;
//#endif
//	LastEP = Vd3(0) ;
	switch (button)	{
		case	GLUT_LEFT_BUTTON	:
			if (state == GLUT_DOWN)	{
				gm->Anchor = Vd2(x,y) ;
				gm->LastEP = gm->EP ;
				}
			if (state == GLUT_DOWN)	{	::glutIdleFunc(cb_idle) ;	}
			else					{	::glutIdleFunc(NULL) ;		}
			break;
		case	GLUT_MIDDLE_BUTTON	:
			if (state == GLUT_DOWN)	{
				gm->Anchor = Vd2(x,y) ;
			//	gm->LastEP = gm->EP ;
				gm->LastDs = gm->Distnc ;
				}
			if (state == GLUT_DOWN)	{	::glutIdleFunc(cb_idle) ;	}
			else					{	::glutIdleFunc(NULL) ;		}
			break;
		default:
			break;
		}
	if (state == GLUT_DOWN)	{	gm->Button = button ;	}
	else					{	gm->Button = -1 ;		}
	}

////
//	*******************************************************************************
//	motion
//	Create	:	2019/10/24
//	*******************************************************************************
inline	void	cv_motion	(int x,int y)
{
	C_glut*	gm = ::get_c_glut() ;
//	std::tout	<<	_T("MouseMove")			<<	_T("\t")	<<	x	<<	_T(" , ")	<<	y	<<	std::endl ;
//	if (LastEP == Vd3(0)) { return; }
	if (gm->Button == GLUT_LEFT_BUTTON) {
		Vd3		eye = gm->LastEP ;
		{
			double	width_	= ::glutGet(GLUT_WINDOW_WIDTH) ;
			double	height	= ::glutGet(GLUT_WINDOW_HEIGHT);
			double	angleXY	=  rad(180 * (gm->Anchor.x - x) / width_) ;
			double	angle_Z	= -rad(180 * (gm->Anchor.y - y) / height) ;
			eye = ::Eye_GetSpinXY(eye,angleXY) ;
			eye = ::Eye_GetSpin_Z(eye,angle_Z) ;
		//	std::tout	<<	deg(angleXY)	<<	_T("\t")	<<	deg(angle_Z)		<<	std::endl ;
			}
		gm->EP = eye ;
		}
	else if (gm->Button == GLUT_MIDDLE_BUTTON) {
		double	distnc = gm->LastDs ;
		{
			double	height = ::glutGet(GLUT_WINDOW_HEIGHT) ;
			double	ratio  = (gm->Anchor.y-y) / height ;
			if (ratio > 0)				{	distnc *= 1 + ratio ;	}
			else						{	distnc *= 1 + ratio ;	}
			if (distnc < gm->D_min)		{	distnc = gm->D_min ;	}
			if (distnc > gm->D_max)		{	distnc = gm->D_max ;	}
		//	std::tout	<<	ratio	<<	_T("\t")	<<	distnc	<<	std::endl ;
			}
		gm->Distnc = distnc ;
		}
	}

////
//	*******************************************************************************
//	resize
//	Create	:	2019/10/29
//	*******************************************************************************
inline	void	cv_resize	(int w,int h)
{
	::glViewport(0,0,w,h) ;
	::glMatrixMode(GL_PROJECTION) ;
	::glLoadIdentity() ;
	C_glut*	gm = ::get_c_glut() ;
	::Perspective(gm->D_max*1.5) ;
	}

////
//	*******************************************************************************
//	LookAt
//	Create	:	2019/10/24
//	*******************************************************************************
inline	void	LookAt	(Vd3& ep,Vd3& tp,Vd3& up)
{
	::glMatrixMode(GL_MODELVIEW) ;
	::glLoadIdentity() ;
	::gluLookAt(ep.x,ep.y,ep.z,
				tp.x,tp.y,tp.z,
				up.x,up.y,up.z) ;
	}

inline	void	LookAt	(void)
{
	C_glut*	gm = ::get_c_glut() ;
	{
		Vd3		ep = gm->EP ;
		ep.Normalize() ;
	//	ep *= 100 ;
		ep *= gm->Distnc ;
		gm->EP = ep ;
		}
	Vd3	eye = gm->EP + gm->TP ;
	::LookAt(eye,gm->TP,gm->Up) ;
	}

////
//	*******************************************************************************
//	display
//	Create	:	2019/10/24
//	*******************************************************************************
inline	void	cv_draw	(const Vd3& col=Vd3(0))
{
	GLdouble vertex[][3] = {
		{ 0,  0, 0 },
		{ 4,  0, 0 },
		{ 4, 10, 0 },
		{ 0, 10, 0 },
		{ 0,  0, 6 },
		{ 4,  0, 6 },
		{ 4, 10, 6 },
		{ 0, 10, 6 }
		} ;
	int edge[][2] = {
		{ 0, 1 },
		{ 1, 2 },
		{ 2, 3 },
		{ 3, 0 },
		{ 4, 5 },
		{ 5, 6 },
		{ 6, 7 },
		{ 7, 4 },
		{ 0, 4 },
		{ 1, 5 },
		{ 2, 6 },
		{ 3, 7 }
		} ;
	::glColor3d(col.x,col.y,col.z) ;
	::glBegin(GL_LINES) ;
	for (int i = 0; i < int(countof(edge)) ; ++i) {
		::glVertex3dv(vertex[edge[i][0]]) ;
		::glVertex3dv(vertex[edge[i][1]]) ;
		}
	::glEnd() ;
	}

inline	void	cv_display(void)
{
	::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) ;
	::LookAt() ;
	::cv_draw() ;
	::glutSwapBuffers();
	}

////
//	*******************************************************************************
//	set extent
//	Create	:	2019/10/28
//	*******************************************************************************
inline	bool	set_Extent	(const Ed3& ext)
{
	C_glut*	gm = ::get_c_glut() ;
	if (gm->Ext == ext)		{	return	true ;	}
	gm->Ext   = ext ;
	gm->TP    = ext.mmm() ;
	gm->D_min = ext.Volume().Length() ;
	gm->D_max = gm->D_min * 10. ;
	gm->Distnc= gm->D_min * 1.5 ;
	if (::glutGetWindow() != 0) {
		int		w = ::glutGet(GLUT_WINDOW_WIDTH) ;
		int		h = ::glutGet(GLUT_WINDOW_HEIGHT);
		cv_resize(w,h) ;
		}
	return	true ;
	}

////
//	*******************************************************************************
//	rotate
//	Create	:	2019/10/29
//	*******************************************************************************
inline	bool	ut_Rotate	(const double deg_rz=1)
{
	C_glut*	gm = ::get_c_glut() ;
	if (gm->Rotate) {
		Vd3		eye = gm->EP ;
		{
			double	angleXY	= rad(deg_rz) ;
			eye = ::Eye_GetSpinXY(eye,angleXY) ;
			}
		gm->EP = eye ;
		}
	return	true ;
	}

////
//	*******************************************************************************
//	timer
//	Create	:	2019/10/29
//	*******************************************************************************
inline	void	cv_timer	(int value)
{
	::ut_Rotate(1) ;
	int	timing = value ;
	if (::get_c_glut()->Rotate) {	timing =  100 ;		}
	else						{	timing = 1000 ;		}
	::glutTimerFunc(timing,cv_timer,timing) ;
	::glutPostRedisplay() ;
	}

////
//	*******************************************************************************
//	keyboard
//	Create	:	2019/10/25
//	Modify	:	2019/11/14
//	Modify	:	2019/12/05	'w'
//	*******************************************************************************
inline	void	cv_keyboard	(unsigned char key,int x,int y)
{
	C_glut*	gm = ::get_c_glut() ;
	switch (key) {
		case	'b' :
		//	::glutIdleFunc(NULL) ;
			gm->BG = Vd4(1)-gm->BG ;
			::ClearColor(gm->BG) ;
		//	::glutIdleFunc(cb_idle) ;
			::glutPostRedisplay() ;
			break ;
		case	'r' :
			gm->Rotate = !gm->Rotate ;
			if (gm->Rotate)	{	::glutTimerFunc(10,cv_timer,10) ;	}
			break ;
		case	't' :
			if (::glIsEnabled(GL_TEXTURE_2D))	{	::glDisable(GL_TEXTURE_2D) ;	}
			else								{	::glEnable (GL_TEXTURE_2D) ;	}
			::glutPostRedisplay() ;
			break ;
		case	'w' :
			if (::glIsEnabled(GL_LINE_SMOOTH))	{	::glDisable(GL_LINE_SMOOTH) ;	}
			else								{	::glEnable (GL_LINE_SMOOTH) ;	}
			::glutPostRedisplay() ;
			break ;
		case	'l' :
			if (::glIsEnabled(GL_LIGHTING))	{	::glDisable(GL_LIGHTING) ;							}
			else							{	::glEnable (GL_LIGHTING) ;	::glEnable(GL_LIGHT0) ;	}
			::glutPostRedisplay() ;
			break ;
	//	case	'q' :
	//	case	'Q' :
	//	case	'\x1b' :
	//		::exit(0) ;
	//		break ;
		default	:
			::cb_keyboard(key,x,y) ;
			break ;
		}
	}