// SixtyFourView.cpp : implementation of the CSixtyFourView class
//

#include "stdafx.h"
#include "SixtyFour.h"

#include "SixtyFourDoc.h"
#include "SixtyFourView.h"

#include "Search.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSixtyFourView

IMPLEMENT_DYNCREATE(CSixtyFourView, CView)

BEGIN_MESSAGE_MAP(CSixtyFourView, CView)
	//{{AFX_MSG_MAP(CSixtyFourView)
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_WM_DESTROY()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_COMMAND(ID_MOVE_NEXT, OnMoveNext)
	ON_UPDATE_COMMAND_UI(ID_MOVE_NEXT, OnUpdateMoveNext)
	ON_COMMAND(ID_MOVE_BACK, OnMoveBack)
	ON_UPDATE_COMMAND_UI(ID_MOVE_BACK, OnUpdateMoveBack)
	ON_COMMAND(ID_MOVE_INVOKE_CPU, OnMoveInvokeCpu)
	ON_UPDATE_COMMAND_UI(ID_MOVE_INVOKE_CPU, OnUpdateMoveInvokeCpu)
	ON_COMMAND(ID_MOVE_FIRST, OnMoveFirst)
	ON_COMMAND(ID_MOVE_LAST, OnMoveLast)
	ON_COMMAND(ID_MOVE_SUGGEST, OnMoveSuggest)
	ON_UPDATE_COMMAND_UI(ID_MOVE_SUGGEST, OnUpdateMoveSuggest)
	ON_WM_TIMER()
	ON_UPDATE_COMMAND_UI(ID_GAME_CPU_WHITE, OnUpdateGameCpuWhite)
	ON_COMMAND(ID_GAME_CPU_WHITE, OnGameCpuWhite)
	ON_COMMAND(ID_GAME_CPU_NONE, OnGameCpuNone)
	ON_UPDATE_COMMAND_UI(ID_GAME_CPU_NONE, OnUpdateGameCpuNone)
	ON_COMMAND(ID_GAME_CPU_BLACK, OnGameCpuBlack)
	ON_UPDATE_COMMAND_UI(ID_GAME_CPU_BLACK, OnUpdateGameCpuBlack)
	ON_UPDATE_COMMAND_UI(ID_MOVE_FIRST, OnUpdateMoveBack)
	ON_UPDATE_COMMAND_UI(ID_MOVE_LAST, OnUpdateMoveNext)
	ON_WM_KILLFOCUS()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////////////////////
//
// OpenGLpalette
//
unsigned char CSixtyFourView::m_threeto8[8] = {
    0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377
};
unsigned char CSixtyFourView::m_twoto8[4] = {
   0, 0x55, 0xaa, 0xff
};
unsigned char CSixtyFourView::m_oneto8[2] = {
    0, 255
};

int CSixtyFourView::m_defaultOverride[13] = {
    0, 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91
};

PALETTEENTRY CSixtyFourView::m_defaultPalEntry[20] = {
    { 0,   0,   0,    0 }, //0
    { 0x80,0,   0,    0 }, 
    { 0,   0x80,0,    0 }, 
    { 0x80,0x80,0,    0 }, 
    { 0,   0,   0x80, 0 },
    { 0x80,0,   0x80, 0 },
    { 0,   0x80,0x80, 0 },
    { 0xC0,0xC0,0xC0, 0 }, //7

    { 192, 220, 192,  0 }, //8
    { 166, 202, 240,  0 },
    { 255, 251, 240,  0 },
    { 160, 160, 164,  0 }, //11

    { 0x80,0x80,0x80, 0 }, //12
    { 0xFF,0,   0,    0 },
    { 0,   0xFF,0,    0 },
    { 0xFF,0xFF,0,    0 },
    { 0,   0,   0xFF, 0 },
    { 0xFF,0,   0xFF, 0 },
    { 0,   0xFF,0xFF, 0 },
    { 0xFF,0xFF,0xFF, 0 }  //19
  };

GLfloat CSixtyFourView::m_dvShinyMaterial[][4] = {
	{ 0.02f, 0.02f, 0.02f, 1.0f },
	{ 0.01f, 0.01f, 0.01f, 1.0f },
	{ 0.4f, 0.4f, 0.4f, 1.0f },
	{ 0.0f, 0.0f, 0.0f, 0.0f },
	{ 10.0f }
};

GLfloat CSixtyFourView::m_dvDefaultMaterial[][4] = {
	{ 0.02f, 0.02f, 0.02f, 1.0f },
	{ 0.08f, 0.08f, 0.08f, 1.0f },
	{ 0.0f, 0.0f, 0.0f, 1.0f },
	{ 0.0f, 0.0f, 0.0f, 0.0f },
	{ 0.0f }
};

GLfloat CSixtyFourView::m_dvRedEmissiveMaterial[][4] = {
	{ 0.0f, 0.0f, 0.0f, 1.0f },
	{ 1.0f, 0.0f, 0.0f, 1.0f },
	{ 1.0f, 0.0f, 0.0f, 1.0f },
	{ 1.0f, 0.0f, 0.0f, 1.0f },
	{ 64.0f }
};

GLfloat CSixtyFourView::m_dvBlueEmissiveMaterial[][4] = {
	{ 0.0f, 0.0f, 0.0f, 1.0f },
	{ 0.0f, 0.0f, 1.0f, 1.0f },
	{ 0.0f, 0.0f, 1.0f, 1.0f },
	{ 0.0f, 0.0f, 1.0f, 1.0f },
	{ 64.0f }
};

#define MAT_AMBIENT		0
#define MAT_DIFFUSE		1
#define MAT_SPECULAR	2
#define MAT_EMISSION	3
#define MAT_SHININESS	4

#define SET_MATERIAL(m)	\
		glMaterialfv(GL_FRONT, GL_AMBIENT, m[MAT_AMBIENT]); \
		glMaterialfv(GL_FRONT, GL_DIFFUSE, m[MAT_DIFFUSE]); \
		glMaterialfv(GL_FRONT, GL_SPECULAR, m[MAT_SPECULAR]); \
		glMaterialfv(GL_FRONT, GL_EMISSION, m[MAT_EMISSION]); \
		glMaterialfv(GL_FRONT, GL_SHININESS, m[MAT_SHININESS]); \

#define SET_LIGHT_PROPERTY(l, a, b, c, d)	\
	l[0] = a; l[1] = b; l[2] = c; l[3] = d;	

#define SET_LIGHT_AMBIENT(gl, l, a, b, c, d)	\
	SET_LIGHT_PROPERTY(l, a, b, c, d);			\
	glLightfv(gl, GL_AMBIENT, l);

#define SET_LIGHT_DIFFUSE(gl, l, a, b, c, d)	\
	SET_LIGHT_PROPERTY(l, a, b, c, d);			\
	glLightfv(gl, GL_DIFFUSE, l);

#define SET_LIGHT_SPECULAR(gl, l, a, b, c, d)	\
	SET_LIGHT_PROPERTY(l, a, b, c, d);			\
	glLightfv(gl, GL_SPECULAR, l);

#define SET_LIGHT_POSITION(gl, l, a, b, c, d)	\
	SET_LIGHT_PROPERTY(l, a, b, c, d);			\
	glLightfv(gl, GL_POSITION, l);


/////////////////////////////////////////////////////////////////////////////
// CSixtyFourView construction/destruction

CSixtyFourView::CSixtyFourView()
{
	m_nTicksElapsedSinceMove = 0;
	m_nHintRotation = 0;
	m_dColorCycle = 0;
	m_nTimerID = 0;
	m_dColorIncr = 0.01;
	g_bThreadRunning = FALSE;
	m_pPal = NULL ;
	m_glqSphere = gluNewQuadric();
}

CSixtyFourView::~CSixtyFourView()
{
	gluDeleteQuadric(m_glqSphere);
	delete(m_pPal);
}

BOOL CSixtyFourView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

	return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CSixtyFourView drawing

void CSixtyFourView::OnDraw(CDC* pDC)
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// Select the palette.

    CPalette* ppalOld = pDC->SelectPalette(m_pPal, 0);
    pDC->RealizePalette();

	// Draw.
	
	RenderScene(GL_RENDER);

	glFinish();

	// Swap buffers.
	
	SwapBuffers(pDC->m_hDC) ;

	// Select old palette. 
	
	if (ppalOld) 
		pDC->SelectPalette(ppalOld, 0); 
}

/////////////////////////////////////////////////////////////////////////////
// CSixtyFourView diagnostics

#ifdef _DEBUG
void CSixtyFourView::AssertValid() const
{
	CView::AssertValid();
}

void CSixtyFourView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CSixtyFourDoc* CSixtyFourView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSixtyFourDoc)));
	return (CSixtyFourDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSixtyFourView message handlers

int CSixtyFourView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	CClientDC dc(this) ;
	//
	// Fill in the pixel format descriptor.
	//
	PIXELFORMATDESCRIPTOR pfd ;
	memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)) ;
	pfd.nSize      = sizeof(PIXELFORMATDESCRIPTOR); 
	pfd.nVersion   = 1 ; 
	pfd.dwFlags    = PFD_DOUBLEBUFFER |
					PFD_SUPPORT_OPENGL |
					PFD_DRAW_TO_WINDOW ;
	pfd.iPixelType = PFD_TYPE_RGBA ;
	pfd.cColorBits = 24 ;
	pfd.cDepthBits = 32 ;
	pfd.iLayerType = PFD_MAIN_PLANE ;

	int nPixelFormat = ChoosePixelFormat(dc.m_hDC, &pfd);
	if (nPixelFormat == 0)
	{
		TRACE("ChoosePixelFormat Failed %d\r\n",GetLastError()) ;
		return -1 ;
	}

	BOOL bResult = SetPixelFormat (dc.m_hDC, nPixelFormat, &pfd);
	if (!bResult)
	{
		TRACE("SetPixelFormat Failed %d\r\n",GetLastError()) ;
		return -1 ;
	}

	//
	// Create a rendering context.
	//
	m_hrc = wglCreateContext(dc.m_hDC);
	if (!m_hrc)
	{
		TRACE("wglCreateContext Failed %x\r\n", GetLastError()) ;
		return -1;
	}

	return 0;
}

//
// CreateRGBPalette
//
BOOL CSixtyFourView::CreateRGBPalette(HDC hDC)
{
	//
	// Check to see if we need a palette
	//

    PIXELFORMATDESCRIPTOR pfd;
    int n = GetPixelFormat(hDC);
    DescribePixelFormat(hDC, n, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
	if (!(pfd.dwFlags & PFD_NEED_PALETTE)) return FALSE ;

	TRACE0("Creating palette\r\n") ;

    // allocate a log pal and fill it with the color table info

    LOGPALETTE* pPal = (LOGPALETTE*) malloc(sizeof(LOGPALETTE) 
                     + 256 * sizeof(PALETTEENTRY));
    if (!pPal) 
    {
        TRACE("Out of memory for logpal");
        return FALSE;
    }
    pPal->palVersion = 0x300; // Windows 3.0
    pPal->palNumEntries = 256; // table size

	//
	// Create RGB Palette
	//

	ASSERT( pfd.cColorBits == 8) ;
    n = 1 << pfd.cColorBits;
    for (int i=0; i<n; i++)
    {
        pPal->palPalEntry[i].peRed =
            ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift);
        pPal->palPalEntry[i].peGreen =
            ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift);
        pPal->palPalEntry[i].peBlue =
            ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift);
        pPal->palPalEntry[i].peFlags = 0;
    }

	//
	// Fix up color table with system colors.
	//
    if ((pfd.cColorBits == 8)                           &&
        (pfd.cRedBits   == 3) && (pfd.cRedShift   == 0) &&
        (pfd.cGreenBits == 3) && (pfd.cGreenShift == 3) &&
        (pfd.cBlueBits  == 2) && (pfd.cBlueShift  == 6)
       )
    {
	    for (int j = 1 ; j <= 12 ; j++)
	        pPal->palPalEntry[m_defaultOverride[j]] = m_defaultPalEntry[j];
	}

    // Delete any existing GDI palette
	if (m_pPal) delete m_pPal ;
	m_pPal = new CPalette ;

    BOOL bResult = m_pPal->CreatePalette(pPal);
    free (pPal);

    return bResult;
}

//
// ComponentFromIndex
//
unsigned char CSixtyFourView::ComponentFromIndex(int i, UINT nbits, UINT shift)
{
    unsigned char val;

    val = (unsigned char) (i >> shift);
    switch (nbits) {

    case 1:
        val &= 0x1;
        return m_oneto8[val];

    case 2:
        val &= 0x3;
        return m_twoto8[val];

    case 3:
        val &= 0x7;
        return m_threeto8[val];

    default:
        return 0;
    }
}

void CSixtyFourView::OnSize(UINT nType, int cx, int cy) 
{
	CView::OnSize(nType, cx, cy);
	
	if ( (cx <= 0) || (cy <= 0) ) return ;
	
    CClientDC dc(this) ;

	//
	// Make the rendering context m_hrc current
	//

    BOOL bResult = wglMakeCurrent(dc.m_hDC, m_hrc);
	if (!bResult)
	{
		TRACE("wglMakeCurrent Failed %x\r\n", GetLastError() ) ;
		return ;
	}

    //
    // Set up the mapping of 3-space to screen space
    //

    m_gldAspect = (GLdouble) cx / (GLdouble) cy;
	ResetProjection();
    glViewport(0, 0, cx, cy);
}

void CSixtyFourView::OnDestroy() 
{
	CView::OnDestroy();

	if (m_nTimerID)
	{
		KillTimer(m_nTimerID);
		m_nTimerID = 0;
	}

	while (g_bThreadRunning)
	{
		g_bAbortSearch = TRUE;
		Sleep(500);
	}

	wglMakeCurrent(NULL,NULL) ; 
	if (m_hrc) 
	{
		wglDeleteContext(m_hrc) ;
		m_hrc = NULL ;
	}		
}

void CSixtyFourView::ResetProjection()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, m_gldAspect, 10.0, 1400.0);
}

#define CURRENT_BALL		(pDoc->m_uGameCube[(i << 4) + (j << 2) + k] & MOVE)

void CSixtyFourView::RenderScene(GLint nMode)
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	int i, j, k, x, y, z;

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	glTranslated(0.0, 0.0, -DISTANCE_FROM_CENTER);
	glRotated(pDoc->m_gldRotateAroundX, 0.0, 1.0, 0.0);
	glRotated(pDoc->m_gldRotateAroundY, 1.0, 0.0, 0.0);

	SET_MATERIAL(m_dvShinyMaterial);

	if (nMode == GL_RENDER)
	{
		// Enable depth testing

		glEnable(GL_DEPTH_TEST);

		// Enable lighting calculations

		glEnable(GL_LIGHTING) ;
		glEnable(GL_LIGHT0) ;
		glDisable(GL_LIGHT1);
		glDisable(GL_LIGHT2);

		// Clear the color and depth buffers

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Highlight the suggested move

		if (m_bSuggested 
			&& !pDoc->m_bGameOver)
		{
			glPushMatrix();
				glTranslated(m_moveSuggested.nMoveK * 50 - 75, m_moveSuggested.nMoveJ * 50 - 75, m_moveSuggested.nMoveI * 50 - 75);
				glPushMatrix();
				glRotated(m_nHintRotation, 0.0, 0.0, 1.0);
				glTranslated(0, 20, 0);

				if (pDoc->m_nCurrentPlayer == PLAYER_RED)
				{
					glColor3d(1.0, 0.0, 0.0);
					GLfloat dvLightPosition[4];
					SET_LIGHT_POSITION(GL_LIGHT1, dvLightPosition, 0.0f, 0.0f, 0.0f, 1.0f);
					SET_MATERIAL(m_dvRedEmissiveMaterial);
					glEnable(GL_LIGHT1);
				}
				else
				{
					glColor3d(0.0, 0.0, 1.0);
					GLfloat dvLightPosition[4];
					SET_LIGHT_POSITION(GL_LIGHT2, dvLightPosition, 0.0f, 0.0f, 0.0f, 1.0f);
					SET_MATERIAL(m_dvBlueEmissiveMaterial);
					glEnable(GL_LIGHT2);
				}

				glCallList(LIST_HINT);
				glPopMatrix();

				SET_MATERIAL(m_dvShinyMaterial);
				glCallList(LIST_DOT);
			glPopMatrix();
		}

		// Draw the balls

		for (z = -75, i = 0; z <= 75; z += 50, i++)
			for (y = -75, j = 0; y <= 75; y += 50, j++)
				for (x = -75, k = 0; x <= 75; x += 50, k++)
				{
					glPushMatrix();
						glTranslated(x, y, z);
						switch(CURRENT_BALL)
						{
						case 0:
							if (m_bSuggested 
								&& i == m_moveSuggested.nMoveI
								&& j == m_moveSuggested.nMoveJ
								&& k == m_moveSuggested.nMoveK)
								break;

							glColor3d(0.6, 0.6, 0.6);
							glCallList(LIST_DOT);

							break;
						case 1:
							if (IsHighlighted(i, j, k))
								glColor3d(1.0, m_dColorCycle, m_dColorCycle);
							else
								glColor3d(1.0, 0.0, 0.0);

							if (m_nTicksElapsedSinceMove > 0
								&& i == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveI
								&& j == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveJ
								&& k == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveK)
							{
								glPushMatrix();
									double factor = (10 - (double)m_nTicksElapsedSinceMove) / 10.0 + .7;
									glScaled(factor, factor, factor);
									glCallList(LIST_BALL);
								glPopMatrix();
								break;
							}

							glCallList(LIST_BALL);
							break;
						case 2:
							if (IsHighlighted(i, j, k))
								glColor3d(m_dColorCycle, m_dColorCycle, 1.0);
							else
								glColor3d(0.0, 0.0, 1.0);

							if (m_nTicksElapsedSinceMove > 0
								&& i == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveI
								&& j == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveJ
								&& k == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveK)
							{
								glPushMatrix();
									double factor = (10 - (double)m_nTicksElapsedSinceMove) / 10.0 + .7;
									glScaled(factor, factor, factor);
									glCallList(LIST_BALL);
								glPopMatrix();
								break;
							}

							glCallList(LIST_BALL);
							break;
						}
					glPopMatrix();
				}
	}
	else /* if (nMode == GL_SELECT) */
	{
		// Disable depth test, lighting

		glDisable(GL_DEPTH_TEST);
		glDisable(GL_LIGHTING);

		// Render

		for (z = -75, i = 0; z <= 75; z += 50, i++)
			for (y = -75, j = 0; y <= 75; y += 50, j++)
				for (x = -75, k = 0; x <= 75; x += 50, k++)
				{
					glPushMatrix();
						glTranslated(x, y, z);

						glPushName(i);
						glPushName(j);
						glPushName(k);
						
						switch(CURRENT_BALL)
						{
						case 0:
							glCallList(LIST_DOT);
							break;
						}
						
						glPopName();
						glPopName();
						glPopName();

					glPopMatrix();
				}
	}

    // Flush the drawing pipeline.

    glFlush();

	glPopMatrix();
}

BOOL CSixtyFourView::IsHighlighted(int i, int j, int k)
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (pDoc->m_bGameOver)
	{
		for (int l = 0; l < 4; l++)
		{
			if (g_moveWinner[l].nMoveI == i
				&& g_moveWinner[l].nMoveJ == j
				&& g_moveWinner[l].nMoveK == k)
				return TRUE;
		}
	}
	else if (pDoc->m_nCurrentMove >= 0)
	{
		if (i == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveI
			&& j == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveJ
			&& k == pDoc->m_moveHistory[pDoc->m_nCurrentMove].nMoveK)
			return TRUE;
	}

	return FALSE;
}

void CSixtyFourView::DefineStatics()
{
	glNewList(LIST_DOT, GL_COMPILE);
		glPushMatrix();
			gluSphere(m_glqSphere, 4, 10, 10);
		glPopMatrix();
	glEndList();

	glNewList(LIST_BALL, GL_COMPILE);
		glPushMatrix();
			gluSphere(m_glqSphere, 20, 20, 20);
		glPopMatrix();
	glEndList();

	glNewList(LIST_HINT, GL_COMPILE);
		glPushMatrix();
			gluSphere(m_glqSphere, 6, 10, 10);
		glPopMatrix();
	glEndList();
}

BOOL CSixtyFourView::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;
//	return CView::OnEraseBkgnd(pDC);
}

void CSixtyFourView::OnInitialUpdate() 
{
	CView::OnInitialUpdate();

	m_bRotCam = FALSE;
	m_bSuggested = FALSE;

	srand((unsigned)time(NULL));

	// Set up fog

	GLfloat fvFogColor[] = {0.3f, 0.3f, 0.3f};
	GLfloat fFogStart = 250.0f;
	GLfloat fFogEnd = 500.0f;
	GLfloat fFogDensity = 0.55f;

	glFogfv(GL_FOG_COLOR, fvFogColor);
	glFogfv(GL_FOG_START, &fFogStart);
	glFogfv(GL_FOG_END, &fFogEnd);
	glFogfv(GL_FOG_DENSITY, &fFogDensity);
	glFogi(GL_FOG_MODE, GL_LINEAR);

	glEnable(GL_FOG);

	glClearColor(0.3f, 0.3f, 0.3f, 1.0);
	
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE) ;
	glEnable(GL_COLOR_MATERIAL) ;

	// Set up lighting

	GLfloat lightSetting[4];

	SET_LIGHT_AMBIENT(GL_LIGHT0, lightSetting, 0.05f, 0.05f, 0.05f, 1.0f);
	SET_LIGHT_DIFFUSE(GL_LIGHT0, lightSetting, 1.0f, 1.0f, 1.0f, 1.0f);
	SET_LIGHT_SPECULAR(GL_LIGHT0, lightSetting, 1.0f, 1.0f, 1.0f, 1.0f);
	SET_LIGHT_POSITION(GL_LIGHT0, lightSetting, 0.0f, 0.0f, 1.0f, 0.0f);

	SET_LIGHT_AMBIENT(GL_LIGHT1, lightSetting, 0.0f, 0.0f, 0.0f, 1.0f);
	SET_LIGHT_DIFFUSE(GL_LIGHT1, lightSetting, 1.0f, 0.0f, 0.0f, 1.0f);
	SET_LIGHT_SPECULAR(GL_LIGHT1, lightSetting, 1.0f, 0.0f, 0.0f, 1.0f);

	SET_LIGHT_AMBIENT(GL_LIGHT2, lightSetting, 0.0f, 0.0f, 0.0f, 1.0f);
	SET_LIGHT_DIFFUSE(GL_LIGHT2, lightSetting, 0.0f, 0.0f, 1.0f, 1.0f);
	SET_LIGHT_SPECULAR(GL_LIGHT2, lightSetting, 1.0f, 0.0f, 0.0f, 1.0f);

	m_nTimerID = SetTimer(TIMER_IDLE, 10, NULL);

	DefineStatics();
}

void CSixtyFourView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	UINT uMove;

	if (!pDoc->m_bGameOver 
		&& pDoc->m_nGameState == GAME_PLAYING
		&& pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_MOUSE
		&& (uMove = Pick(point.x, point.y)) != PICK_NO_MATCH)
	{
		Move move = {
			GET_MOVE_I(uMove),
			GET_MOVE_J(uMove),
			GET_MOVE_K(uMove)
		};

		MakeMove(move);

		return;
	}

	m_ptClick = point;
	ClientToScreen(&m_ptClick);
	ShowCursor(FALSE);
	m_bRotCam = TRUE;
	m_ptOldMousePt = point;
	SetCapture();

	CView::OnLButtonDown(nFlags, point);
}

void CSixtyFourView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if (m_bRotCam)
	{
		ReleaseCapture();
		SetCursorPos(m_ptClick.x, m_ptClick.y);
		ShowCursor();
		m_bRotCam = FALSE;
	}

	CView::OnLButtonUp(nFlags, point);
}

void CSixtyFourView::OnMouseMove(UINT nFlags, CPoint point) 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (m_bRotCam)
	{
		pDoc->m_gldRotateAroundX += (point.x - m_ptOldMousePt.x);
		pDoc->m_gldRotateAroundY += (point.y - m_ptOldMousePt.y);

		m_ptOldMousePt = point;
	}
	
	// TODO: Add your message handler code here and/or call default
	
	CView::OnMouseMove(nFlags, point);
}

BOOL CSixtyFourView::Pick(GLdouble x, GLdouble y)
{
	GLuint nvBuffer[104];
	GLint viewport[4];

	glGetIntegerv(GL_VIEWPORT, viewport);

	glSelectBuffer(104, nvBuffer);
	glRenderMode(GL_SELECT);

	glInitNames();						// initialize the name stack

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	// set up the picking matrix (1x1), and perspective
	gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y), 
				  5.0, 5.0, viewport);
	gluPerspective(45.0, m_gldAspect, 10.0, 1400.0);

	RenderScene(GL_SELECT);					// render in selection mode
	glPopMatrix();

	BOOL ret = ProcessHits( glRenderMode(GL_RENDER), nvBuffer);
	ResetProjection();

	return ret;
}

// determines which move was selected
UINT CSixtyFourView::ProcessHits(GLint nHits, GLuint nvBuffer[])
{
	int i, k, names;

	for (i = 0, k = 0; i < nHits; i++, k++)
	{
		names = nvBuffer[k];

		k += 2;
		return PACK_MOVE(nvBuffer[k + 1], nvBuffer[k + 2], nvBuffer[k + 3]);
	}

	return PICK_NO_MATCH;
}

void CSixtyFourView::MakeMove(Move move)
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (!pDoc->m_bGameOver && pDoc->MakeMove(move))
	{
		m_bSuggested = FALSE;
		m_nTicksElapsedSinceMove = 10;

		if (pDoc->m_bGameOver)
		{
			if (pDoc->m_nWinner == PLAYER_BOTH)
				AfxMessageBox(ID_MESSAGE_DRAW, MB_ICONINFORMATION);
			else if (pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_MOUSE)
			{
				if (pDoc->m_plPlayers[pDoc->GetOpponent()].nType == PLAYER_TYPE_MOUSE)
					AfxMessageBox(pDoc->m_nCurrentPlayer == PLAYER_RED ? ID_MESSAGE_P1_WIN : ID_MESSAGE_P2_WIN, MB_ICONINFORMATION);
				else
					AfxMessageBox(ID_MESSAGE_HUMAN_WIN, MB_ICONINFORMATION);
			}
			else
				AfxMessageBox(ID_MESSAGE_CPU_WIN, MB_ICONINFORMATION);

			pDoc->SwitchPlayers();
			return;
		}

		pDoc->SwitchPlayers();

		if (pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_COMPUTER)
			InvokeCPUMove();
	}
}

void CSixtyFourView::InvokeCPUMove(BOOL m_bSuggest)
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	CSixtyFourApp* pApp = (CSixtyFourApp*)AfxGetApp();
	ASSERT_VALID(pApp);

	if (!m_bSuggest && pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType != PLAYER_TYPE_COMPUTER)
		return;

	m_tpParam.hMainWindow = m_hWnd;
	m_tpParam.nSkill = pApp->m_nSettingCPUSkill;
	m_tpParam.nTurn = pDoc->m_nCurrentPlayer;
	m_tpParam.uCube = pDoc->m_uGameCube;

	g_bAbortSearch = FALSE;
	g_bThreadRunning = TRUE;
	AfxBeginThread(searchGo, (LPVOID)&m_tpParam);
}

LRESULT CSixtyFourView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	switch(message)
	{
	case WM_SEARCH_DONE:
		CSixtyFourDoc* pDoc = GetDocument();
		ASSERT_VALID(pDoc);

		g_bThreadRunning = FALSE;
		Move move = *((Move*)wParam);
		free((Move*)wParam);

		if (pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_MOUSE)
		{
			m_moveSuggested = move;
			m_bSuggested = TRUE;
		}
		else
			MakeMove(move);

		return 0;
	}
	
	return CView::WindowProc(message, wParam, lParam);
}

void CSixtyFourView::OnMoveNext() 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (g_bThreadRunning)
		return;

	m_bSuggested = FALSE;
	pDoc->HistoryGoForward();
}

void CSixtyFourView::OnUpdateMoveNext(CCmdUI* pCmdUI) 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pCmdUI->Enable(pDoc->HistoryCanGoForward() && !g_bThreadRunning);
}

void CSixtyFourView::OnMoveBack() 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (g_bThreadRunning)
		return;

	m_bSuggested = FALSE;
	pDoc->HistoryGoBack();
}

void CSixtyFourView::OnUpdateMoveBack(CCmdUI* pCmdUI) 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pCmdUI->Enable(pDoc->HistoryCanGoBack() && !g_bThreadRunning);
}

void CSixtyFourView::OnMoveInvokeCpu() 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	
	if (g_bThreadRunning 
		|| pDoc->m_bGameOver
		|| pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType != PLAYER_TYPE_COMPUTER)
		return;

	InvokeCPUMove();
}

void CSixtyFourView::OnUpdateMoveInvokeCpu(CCmdUI* pCmdUI) 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pCmdUI->Enable(!g_bThreadRunning 
		&& !pDoc->m_bGameOver
		&& pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_COMPUTER);
}

void CSixtyFourView::OnMoveFirst() 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (g_bThreadRunning)
		return;

	m_bSuggested = FALSE;
	pDoc->HistoryGoStart();
}

void CSixtyFourView::OnMoveLast() 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (g_bThreadRunning)
		return;

	m_bSuggested = FALSE;
	pDoc->HistoryGoEnd();
}


void CSixtyFourView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	while (g_bThreadRunning)
	{
		g_bAbortSearch = TRUE;
		Sleep(500);
	}

	if (pDoc->m_bGameOver)
	{
		switch(pDoc->m_nWinner)
		{
		case PLAYER_BOTH:
			AfxMessageBox(ID_MESSAGE_DRAW, MB_ICONINFORMATION);
			break;
		case PLAYER_RED:
			AfxMessageBox(ID_MESSAGE_P1_WIN, MB_ICONINFORMATION);
			break;
		case PLAYER_BLUE:
			AfxMessageBox(ID_MESSAGE_P2_WIN, MB_ICONINFORMATION);
			break;
		}
		return;
	}

	if (pDoc->m_nLastMove < 0
		&& !g_bThreadRunning 
		&& !pDoc->m_bGameOver
		&& pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_COMPUTER)
		InvokeCPUMove();
}

void CSixtyFourView::OnMoveSuggest() 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (!g_bThreadRunning 
		&& !pDoc->m_bGameOver
		&& pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_MOUSE)
		InvokeCPUMove(TRUE);
}

void CSixtyFourView::OnUpdateMoveSuggest(CCmdUI* pCmdUI) 
{
	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pCmdUI->Enable(!g_bThreadRunning 
		&& !pDoc->m_bGameOver
		&& pDoc->m_plPlayers[pDoc->m_nCurrentPlayer].nType == PLAYER_TYPE_MOUSE);
}

void CSixtyFourView::OnTimer(UINT nIDEvent) 
{
	switch(nIDEvent)
	{
	case TIMER_IDLE:
		
		if ((m_nHintRotation += 5) >= 360)
			m_nHintRotation = 0;

		m_dColorCycle += m_dColorIncr;
		if (m_dColorCycle >= 1.0 || m_dColorCycle <= 0.0)
			m_dColorIncr = -m_dColorIncr;

		if (m_nTicksElapsedSinceMove > 0)
			m_nTicksElapsedSinceMove--;

		Invalidate(FALSE);
		return;
	}
	
	CView::OnTimer(nIDEvent);
}

void CSixtyFourView::OnUpdateGameCpuWhite(CCmdUI* pCmdUI) 
{
	CSixtyFourApp* pApp = (CSixtyFourApp*)AfxGetApp();
	ASSERT_VALID(pApp);

	pCmdUI->SetRadio(pApp->m_nSettingCPUColor == DIALOG_COLOR_RED);
}

void CSixtyFourView::OnGameCpuWhite() 
{
	CSixtyFourApp* pApp = (CSixtyFourApp*)AfxGetApp();
	ASSERT_VALID(pApp);

	pApp->m_nSettingCPUColor = DIALOG_COLOR_RED;
	pApp->SaveSettings();

	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pDoc->OnNewDocument();
	OnUpdate(NULL, 0, 0);
}

void CSixtyFourView::OnGameCpuNone() 
{
	CSixtyFourApp* pApp = (CSixtyFourApp*)AfxGetApp();
	ASSERT_VALID(pApp);

	pApp->m_nSettingCPUColor = DIALOG_COLOR_NEITHER;
	pApp->SaveSettings();

	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pDoc->OnNewDocument();
	OnUpdate(NULL, 0, 0);
}

void CSixtyFourView::OnUpdateGameCpuNone(CCmdUI* pCmdUI) 
{
	CSixtyFourApp* pApp = (CSixtyFourApp*)AfxGetApp();
	ASSERT_VALID(pApp);

	pCmdUI->SetRadio(pApp->m_nSettingCPUColor == DIALOG_COLOR_NEITHER);
}

void CSixtyFourView::OnGameCpuBlack() 
{
	CSixtyFourApp* pApp = (CSixtyFourApp*)AfxGetApp();
	ASSERT_VALID(pApp);

	pApp->m_nSettingCPUColor = DIALOG_COLOR_BLUE;
	pApp->SaveSettings();

	CSixtyFourDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pDoc->OnNewDocument();
	OnUpdate(NULL, 0, 0);
}

void CSixtyFourView::OnUpdateGameCpuBlack(CCmdUI* pCmdUI) 
{
	CSixtyFourApp* pApp = (CSixtyFourApp*)AfxGetApp();
	ASSERT_VALID(pApp);

	pCmdUI->SetRadio(pApp->m_nSettingCPUColor == DIALOG_COLOR_BLUE);
}

void CSixtyFourView::OnKillFocus(CWnd* pNewWnd) 
{
	CView::OnKillFocus(pNewWnd);

	if (m_bRotCam)
	{
		SetCursorPos(m_ptClick.x, m_ptClick.y);
		ShowCursor();
		m_bRotCam = FALSE;
		ReleaseCapture();
	}
}

void CSixtyFourView::ShowCursor(BOOL bShow)
{
	if (bShow)
		SetCursor(LoadCursor(NULL, IDC_ARROW));
	else
		SetCursor(NULL);
}
