// glview.cpp
//

#include "stdafx.h"
#include "glview.h"
#include <math.h>


GLView3D::GLView3D(void)
{
	InitImage();
}

GLView3D::~GLView3D(void)
{
}

void GLView3D::CreateImage(UINT32 width, UINT32 height)
{
	if ((width > 0) && (height > 0))
	{
		m_dib.Create(width,height,24);

		if (!IsInitialized())
		{
			InitializeOpenGL();
			if (CreateView())
			{
				m_IsInitialized = 1;
			}
		}
	}
}

void GLView3D::SizeImage(UINT32 width, UINT32 height)
{
	if ((width > 0) && (height > 0))
	{
		if (!IsInitialized())
		{
			CreateImage(width,height);
		}
		else
		{
			if (!m_dib.IsCreated())
			{
				m_dib.Create(width,height,24);

				if (SetImagePixelFormat(*m_dib.GetDC()))
				{
					CreateImageGLContext(*m_dib.GetDC());
				}
			}
			else if ((width != m_dib.Width()) || (height != m_dib.Height()))
			{
				m_dib.Create(width,height,24);
				CreateView();
			}
		}

		glViewport(0,0,width,height);
		double aspect;
		aspect = (height == 0) ? (double)width : (double)width/(double)height;
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(45,aspect,0.01,25.0);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}
}

void GLView3D::InitializeOpenGL(void)
{
	m_hGLContext = NULL;
	m_GLPixelIndex = 0;
	InitGeometry();
}

void GLView3D::InitGeometry(void)
{
	m_model_matrix.SetRotation(45.0,0.0,0.0);
	m_model_matrix.SetScale(1.0,1.0,1.0);
	m_model_matrix.SetPosition(0.0,0.0,-5.0);
}

  // Lights, camera, action...
int GLView3D::CreateView(void)
{
	int result = 0;

	  // do the standard OpenGL initialization
	if(SetImagePixelFormat(*m_dib.GetDC()) && CreateImageGLContext(*m_dib.GetDC()))
		result = 1;
	else
		return result;

	glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);


	// Lights, material properties
	GLfloat	ambientProperties[]  = {0.3f, 0.3f, 0.3f, 0.7f};
	GLfloat	diffuseProperties[]  = {0.9f, 0.9f, 0.9f, 1.0f};
	GLfloat	specularProperties[] = {0.7f, 0.7f, 0.7f, 1.0f};
	GLfloat mat_amb_diff[] = { 0.7f, 0.7f, 0.7f, 1.0f};
	GLfloat shine[] = { 128.0f};
	GLfloat emissionProperties[] = {0.2f, 0.2f, 0.2f, 1.0f};
		
	glLightfv( GL_LIGHT0, GL_AMBIENT, ambientProperties);
	glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseProperties);
	glLightfv( GL_LIGHT0, GL_SPECULAR, specularProperties);

	glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.0);

	//glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_amb_diff);
	//glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
	//glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emissionProperties);

	// lighting
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);

	return result;
}

BOOL GLView3D::SetImagePixelFormat(HDC hDC)
{
	PIXELFORMATDESCRIPTOR pixelDesc;
	
	pixelDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	pixelDesc.nVersion = 1;
	
	pixelDesc.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL |
		PFD_SUPPORT_GDI;
	
	pixelDesc.iPixelType = PFD_TYPE_RGBA;
	pixelDesc.cColorBits = 24;
	pixelDesc.cRedBits = 0;
	pixelDesc.cRedShift = 0;
	pixelDesc.cGreenBits = 0;
	pixelDesc.cGreenShift = 0;
	pixelDesc.cBlueBits = 0;
	pixelDesc.cBlueShift = 0;
	pixelDesc.cAlphaBits = 0;
	pixelDesc.cAlphaShift = 0;
	pixelDesc.cAccumBits = 0;
	pixelDesc.cAccumRedBits = 0;
	pixelDesc.cAccumGreenBits = 0;
	pixelDesc.cAccumBlueBits = 0;
	pixelDesc.cAccumAlphaBits = 0;
	pixelDesc.cDepthBits = 24;
	pixelDesc.cStencilBits = 0;
	pixelDesc.cAuxBuffers = 0;
	pixelDesc.iLayerType = PFD_MAIN_PLANE;
	pixelDesc.bReserved = 0;
	pixelDesc.dwLayerMask = 0;
	pixelDesc.dwVisibleMask = 0;
	pixelDesc.dwDamageMask = 0;
	
	int pix_index = ChoosePixelFormat(hDC,&pixelDesc);
	
	if(!SetPixelFormat(hDC,pix_index,&pixelDesc))
	{
		DWORD code = GetLastError();
		return FALSE;
	}
	
	return TRUE;
}

BOOL GLView3D::CreateImageGLContext(HDC hDC)
{
	m_hGLContext = wglCreateContext(hDC);
	
	if(m_hGLContext==NULL)
		return FALSE;
	
	if(wglMakeCurrent(hDC,m_hGLContext)==FALSE)
		return FALSE;
	
	return TRUE;
}

void GLView3D::SetBackgroundColor(double r, double g, double b)
{
	m_red_bg = r;
	m_green_bg = g;
	m_blue_bg = b;
}

void GLView3D::RenderImage(void)
{
	if (m_IsInitialized && m_dib.IsCreated())
	{
		glClearColor((float)m_red_bg,(float)m_blue_bg,
			(float)m_green_bg,1.0f);

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
		  // put a working copy on the matrix stack
		glPushMatrix();
		
			// Position / translation / scale
		glTranslated(m_model_matrix.X(),m_model_matrix.Y(),m_model_matrix.Z());
		glRotated(m_model_matrix.XRot(), 1.0, 0.0, 0.0);
		glRotated(m_model_matrix.YRot(), 0.0, 1.0, 0.0);
		glRotated(m_model_matrix.ZRot(), 0.0, 0.0, 1.0);
		glScaled(m_model_matrix.XScale(),m_model_matrix.YScale(),m_model_matrix.ZScale());

		  // build the scene...if necessary
		RenderPrimaryImage();

		glFlush();
	
		  // pop the working copy of the matrix stack
		glPopMatrix();
	}
}

void GLView3D::RenderPrimaryImage(void)
{
	//glColor3ub(128,128,128);
	//GLUquadricObj * q = gluNewQuadric();
	//gluQuadricDrawStyle(q, GLU_LINE);
	//gluQuadricNormals(q, GLU_SMOOTH);
	//gluSphere(q,1.0,16,16);
	//gluDeleteQuadric(q);

	GLdouble v1[3], v2[3];

	glBegin(GL_LINES);
	v1[0] = v1[1] = v1[2] = 0.0;
	v2[0] = 1.5; v2[1] = v2[2] = 0.0;
	glColor3ub(224,0,0);
	glVertex3dv(v1);
	glVertex3dv(v2);
	v2[0] = 0.0; v2[1] = 1.5; v2[2] = 0.0;
	glColor3ub(0,224,0);
	glVertex3dv(v1);
	glVertex3dv(v2);
	v2[0] = 0.0; v2[1] = 0.0; v2[2] = 1.5;
	glColor3ub(0,0,224);
	glVertex3dv(v1);
	glVertex3dv(v2);
	glEnd();
}

void GLView3D::DrawImage(CWnd * wnd)
{
	CClientDC dc(wnd);
	m_dib.Draw(&dc,0,0);
}

void GLView3D::PushMatrixValues(void)
{
	m_saved_matrix = m_model_matrix;
}

void GLView3D::PopMatrixValues(void)
{
	m_model_matrix = m_saved_matrix;
}

void NormalVector(GLdouble p1[3], GLdouble p2[3],
				  GLdouble p3[3], GLdouble n[3])
{
	GLdouble v1[3], v2[3], d;
	// calculate two vectors, using the middle point
	// as the common origin
	v1[0] = p3[0] - p1[0];
	v1[1] = p3[1] - p1[1];
	v1[2] = p3[2] - p1[2];
	v2[0] = p3[0] - p2[0];
	v2[1] = p3[1] - p2[1];
	v2[2] = p3[2] - p2[2];

	// calculate the cross product of the two vectors
	n[0] = v1[1] * v2[2] - v2[1] * v1[2];
	n[1] = v1[2] * v2[0] - v2[2] * v1[0];
	n[2] = v1[0] * v2[1] - v2[0] * v1[1];

	// normalize the vector
	d = ( n[0] * n[0] + n[1] * n[1] + n[2] * n[2] );
	// try to catch very small vectors
	if (d < (GLdouble)0.00000001)
	{
		d = (GLdouble)100000000.0;
	}
	else
	{
		d = (GLdouble)1.0 / sqrt(d);
	}

	n[0] *= d;
	n[1] *= d;
	n[2] *= d;
}
