// RayTracingView.cpp : implementation of the CRayTracingView class
//

#include "stdafx.h"
#include "RayTracing.h"
#include "RayTracingView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

HANDLE hSemaphore;
int ThreadsEnded;
CRayTracingView * pView;

unsigned int Thread(void * Num)
{
	int n = *((int *)Num);
	int a = 0, b = PICSIZE;
	if(n > 0)
		a = pView->ThreadInfo[n-1];
	if(n < THREADS-1)
		b = pView->ThreadInfo[n];

	int i, j, k;
	if((a >= 0) && (b <= PICSIZE))
	{
		for(k = a; k < b; k++)
		{
			i = k % PICX;
			j = k / PICX;
			pView->Array[k] = pView->Scene.CountPixel(i, j);
		}
	}
	WaitForSingleObject(hSemaphore, INFINITE);
	ThreadsEnded++;
	ReleaseSemaphore(hSemaphore, 1, NULL);
	return 0;
}

unsigned int ControlThread(void * Null)
{
	int Numbers[THREADS];
	int i;
	float step = (float)PICSIZE/THREADS;
	for(i = 1; i < THREADS; i++)
		pView->ThreadInfo[i-1] = int(i*step);

	hSemaphore = CreateSemaphore(NULL, 1, 1, "Sem");

	WaitForSingleObject(hSemaphore, INFINITE);
	ThreadsEnded = 0;
	ReleaseSemaphore(hSemaphore, 1, NULL);

	pView->Scene.Camera.SetCamera(pView->Scene.Camera.Pos, pView->Scene.Camera.Pos + pView->Scene.Camera.Forward);

	for(i = 0; i < THREADS; i++)
	{
		Numbers[i] = i;
		if(!AfxBeginThread(Thread, &Numbers[i], 0, 1024, 0, NULL))
		{
			WaitForSingleObject(hSemaphore, INFINITE);
			ThreadsEnded++;
			ReleaseSemaphore(hSemaphore, 1, NULL);
		}
	}
	while(1)
	{
		WaitForSingleObject(hSemaphore, INFINITE);
		if(ThreadsEnded == THREADS)
			break;
		ReleaseSemaphore(hSemaphore, 1, NULL);
	}

	CloseHandle(hSemaphore);

	pView->MakePicArray();
	pView->Invalidate(false);
	return 0;
}


// CRayTracingView

CRayTracingView::CRayTracingView()
{
	pView = this;
}

CRayTracingView::~CRayTracingView()
{

}


BEGIN_MESSAGE_MAP(CRayTracingView, CWnd)
	ON_WM_PAINT()
	ON_WM_CREATE()
	ON_COMMAND(ID_MOVE_LEFT, &CRayTracingView::OnMoveLeft)
	ON_COMMAND(ID_MOVE_RIGHT, &CRayTracingView::OnMoveRight)
	ON_COMMAND(ID_STEP_LEFT, &CRayTracingView::OnStepLeft)
	ON_COMMAND(ID_STEP_RIGHT, &CRayTracingView::OnStepRight)
	ON_COMMAND(ID_MOVE_UP, &CRayTracingView::OnMoveUp)
	ON_COMMAND(ID_MOVE_DOWN, &CRayTracingView::OnMoveDown)
	ON_COMMAND(ID_MOVE_HIGHER, &CRayTracingView::OnMoveHigher)
	ON_COMMAND(ID_MOVE_LOWER, &CRayTracingView::OnMoveLower)
	ON_COMMAND(ID_VIEW_DIFFUSE, &CRayTracingView::OnViewDiffuse)
	ON_COMMAND(ID_VIEW_SPECULAR, &CRayTracingView::OnViewSpecular)
	ON_UPDATE_COMMAND_UI(ID_VIEW_DIFFUSE, &CRayTracingView::OnUpdateViewDiffuse)
	ON_UPDATE_COMMAND_UI(ID_VIEW_SPECULAR, &CRayTracingView::OnUpdateViewSpecular)
	ON_COMMAND(ID_VIEW_AMBIENT, &CRayTracingView::OnViewAmbient)
	ON_UPDATE_COMMAND_UI(ID_VIEW_AMBIENT, &CRayTracingView::OnUpdateViewAmbient)
END_MESSAGE_MAP()



// CRayTracingView message handlers

int CRayTracingView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	srand(GetTickCount());

	Scene.Create(PICX, PICY, FLATS, SPHERES, LIGHTS);

	Scene.Flats[0].Pos = _LZ(-10);
	Scene.Flats[0].Material.CreateColored(_V(0.8f, 0.2f, 0.2f));

	Scene.Flats[1].Pos = _LZ(10);
	Scene.Flats[1].Material.CreateColored(_V(0.2f, 0.2f, 0.8f));

	int i;
	for(i = 0; i < SPHERES; i++)
	{
		pView->Scene.Spheres[i].Material.CreateColored(_V(randf(0, 1), randf(0, 1), randf(0, 1)), _L(randf(0.1f, 0.5f)));
		pView->Scene.Spheres[i].Pos = _V(randf(-5, 5), randf(-5, 5), randf(-3, 3));
		pView->Scene.Spheres[i].Radius = randf(0.2f, 1.0f);
	}
	
	for(i = 0; i < LIGHTS; i++)
	{
		Scene.Lights[i].Color = _V(randf(0.7f, 1), randf(0.7f, 1), randf(0.7f, 1));
		Scene.Lights[i].ConstAttenuation = 0.5f;
	}

	Scene.Lights[0].Pos = _V(0, 0, 5);
	//Scene.Lights[1].Pos = _V(-6, -6, 5);

	Scene.Camera.SetCamera(_V(6, 6, 3), _0);

	InvalidatePicture();

	return 0;
}

BOOL CRayTracingView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (!CWnd::PreCreateWindow(cs))
		return FALSE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);

	cs.cx = PICX;
	cs.cy = PICY;

	return TRUE;
}

void CRayTracingView::MakePicArray()
{
	int i;
	for(i = 0; i < PICSIZE; i++)
	{
		if(Array[i].X < 0)
			Array[i].X = 0;
		if(Array[i].Y < 0)
			Array[i].Y = 0;
		if(Array[i].Z < 0)
			Array[i].Z = 0;
		if(Array[i].X > 1.0f)
			Array[i].X = 1.0f;
		if(Array[i].Y > 1.0f)
			Array[i].Y = 1.0f;
		if(Array[i].Z > 1.0f)
			Array[i].Z = 1.0f;

		PicArray[i] = RGB(int(Array[i].Z*255), int(Array[i].Y*255), int(Array[i].X*255));
	}
	bReady = true;
}

void CRayTracingView::InvalidatePicture()
{
	bReady = false;
	AfxBeginThread(ControlThread, NULL, 0, 1024, 0, NULL);
}

void CRayTracingView::OnPaint()
{
	CPaintDC dc(this);
	if(!bReady)
	{
		dc.SetBkMode(TRANSPARENT);
		dc.TextOut(PICX/2, PICY/2, CString(L"Loading..."));
	}
	else
	{
		CBitmap Picture;
		CDC mdc;
		mdc.CreateCompatibleDC(&dc);
		Picture.CreateCompatibleBitmap(&dc, PICX, PICY);
		mdc.SelectObject(&Picture);
		Picture.SetBitmapBits(PICSIZE*sizeof(int), PicArray);
		
		dc.BitBlt(0, 0, PICX, PICY, &mdc, 0, 0, SRCCOPY);
	}
}

void CRayTracingView::OnMoveLeft()
{
	if(!bReady)
		return;
	Scene.Camera.Forward.RotateAround(Scene.Camera.Up, 0.2f);
	InvalidatePicture();
}

void CRayTracingView::OnMoveRight()
{
	if(!bReady)
		return;
	Scene.Camera.Forward.RotateAround(Scene.Camera.Up, -0.2f);
	InvalidatePicture();
}

void CRayTracingView::OnStepLeft()
{
	if(!bReady)
		return;
	Scene.Camera.Pos -= Scene.Camera.Right * 0.25f;
	InvalidatePicture();
}

void CRayTracingView::OnStepRight()
{
	if(!bReady)
		return;
	Scene.Camera.Pos += Scene.Camera.Right * 0.25f;
	InvalidatePicture();
}

void CRayTracingView::OnMoveUp()
{
	if(!bReady)
		return;
	Scene.Camera.Pos += Scene.Camera.Forward * 0.25f;
	InvalidatePicture();
}

void CRayTracingView::OnMoveDown()
{
	if(!bReady)
		return;
	Scene.Camera.Pos -= Scene.Camera.Forward * 0.25f;
	InvalidatePicture();
}

void CRayTracingView::OnMoveHigher()
{
	if(!bReady)
		return;
	Scene.Camera.Forward.RotateAround(Scene.Camera.Right, 0.2f);
	InvalidatePicture();
}

void CRayTracingView::OnMoveLower()
{
	if(!bReady)
		return;
	Scene.Camera.Forward.RotateAround(Scene.Camera.Right, -0.2f);
	InvalidatePicture();
}

void CRayTracingView::OnViewAmbient()
{
	Scene.bAmbient = !Scene.bAmbient;
	InvalidatePicture();
}

void CRayTracingView::OnViewDiffuse()
{
	Scene.bDiffuse = !Scene.bDiffuse;
	InvalidatePicture();
}

void CRayTracingView::OnViewSpecular()
{
	Scene.bSpecular = !Scene.bSpecular;
	InvalidatePicture();
}

void CRayTracingView::OnUpdateViewAmbient(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(Scene.bAmbient);
}


void CRayTracingView::OnUpdateViewDiffuse(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(Scene.bDiffuse);
}

void CRayTracingView::OnUpdateViewSpecular(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(Scene.bSpecular);
}