#include "stdafx.h"																								// precompiled header
#include "polybumpworkerthread.h"
//#include "resource.h"
#include <mmsystem.h>																							// timeGetTime()
#include "tga.h"																									// PIX_LoadTGA32()
#include "PbTri.h"																								// CPbTri
#include "SimpleTriangleRasterizer.h"															// CSimpleTriangleRasterizer
#include "PolyBump.h"																							// CPbMesh

static const char *g_szWindowClass = "POLYBUMPTHREADERWND";



LRESULT static CALLBACK ThreadderWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

void CPolyBumpWorkerThread::FreeData()
{
	m_HighMeshData.FreeData();
}


bool CPolyBumpWorkerThread::PushHighPolyMesh( const CSimpleIndexedMesh &rMesh )
{
	assert(m_bSetupPhase);

	DebugLog("build high poly 1");

	// build high poly data structure + accelerator structure

	try
	{
    m_HighMeshData.GetNormalsAndClearBaseVectors(rMesh);

		DebugLog("build high poly 2");

		char str[256];

		sprintf(str,"HighRes triangle count: %d (%d bytes)",m_HighMeshData.m_nNumTris,m_HighMeshData.m_nNumTris*sizeof(CPbHighTri));

		DebugLog("build high poly 3 (%s)",str);
  }
	catch (CMemoryException* )
	{
	  FreeData();

		MessageBox(0,"can't allocate memory","High-Poly mesh ",MB_OK);
		return false;
	}

	return true;
}


UINT CPolyBumpWorkerThread::ThreadMain()
{
	m_bComputationFinished = false;

	SetStartTime(timeGetTime());


	// calles ThreadPollMessages() from time to time
	CreateBumpMap(this);
/*
	{
		char str[256];
		DWORD time=timeGetTime()-GetStartTime();

		sprintf(str,"Processing time: %dms\n",time);
		OutputDebugString(str);

		m_Properties.m_bSummariesString+=str;
	}

	{
		char str[256];

		sprintf(str,"FP status :0x%.4x\n", _control87( 0, 0 ));
		OutputDebugString(str);

		m_Properties.m_bSummariesString+=str;
	}
*/
	m_bComputationFinished = true;
	DestroyThreadderHWND();

/*
	if(!m_bUserHasStopped)
	{
		if(m_Properties.m_bPrintSummary)
		{
			MessageBox(0,m_Properties.m_bSummariesString.c_str(),"PolyBumpPlugin Summary",MB_OK);
		}
	}
*/

	return 0;
}








// constructor
CPolyBumpWorkerThread::CPolyBumpWorkerThread()
{
	m_bComputationFinished = false;
	m_hThreadderWndParent=0;
	m_hThreadderWnd=0;

	m_bSetupPhase=false;
	
	m_dwStartTime=0;m_dwComputationTime=0;
	m_bUserHasStopped=false;
	m_nImageSizeX=0;
	m_nImageSizeY=0;

	SetThreadPriority(m_hThread,THREAD_PRIORITY_BELOW_NORMAL);

  // Register the window class
  WNDCLASS wndClass = { 0, ThreadderWndProc, 0, 0,GetModuleHandle(0),NULL,LoadCursor( NULL, IDC_ARROW ), ::GetSysColorBrush(COLOR_BTNFACE), NULL, g_szWindowClass };

	RegisterClass(&wndClass);
}


CPolyBumpWorkerThread::~CPolyBumpWorkerThread()
{ 
	UnregisterClass(g_szWindowClass,GetModuleHandle(0));
}
 





// /return 
bool CPolyBumpWorkerThread::ThreadPollMessages( float infPercent, const char *inszPassName )
{
	char str[256];
	DWORD dwTimeSoFar=timeGetTime()-m_dwStartTime;		// in ms
	DWORD dwSecToGo=0;
	
	if(infPercent>=1.0f)
	{
		dwSecToGo=(DWORD)(((float)dwTimeSoFar/infPercent)*(100.0f-infPercent)/1000.0f);

		DWORD dwMinToGo=dwSecToGo/60;					dwSecToGo-=dwMinToGo*60;
		DWORD dwHToGo=dwMinToGo/60;						dwMinToGo-=dwHToGo*60;

		sprintf(str,"%.0f%% %s (%d:%d:%.2d to go)",infPercent,inszPassName,dwHToGo,dwMinToGo,dwSecToGo);
	}
	else sprintf(str,"%s %.0f %%",inszPassName,infPercent);


	SetWindowText(m_hThreadderWnd,str);


	MSG msg;

	while(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	if(ShouldDie())
		return false;

	m_dwComputationTime = timeGetTime()-m_dwStartTime;		// in ms

	return true;
}

uint32 CPolyBumpWorkerThread::GetComputationTime() const
{
	return m_dwComputationTime;
}






//
void CPolyBumpWorkerThread::CreateThreadderHWND()
{
	// Create the window
	m_hThreadderWnd = CreateWindowEx( WS_EX_TOOLWINDOW,g_szWindowClass,"Polybump Progress",WS_BORDER|WS_CAPTION|WS_VISIBLE, 
		0,0,
		250+2*GetSystemMetrics(SM_CYFIXEDFRAME),
		14+2*GetSystemMetrics(SM_CYFIXEDFRAME)+GetSystemMetrics(SM_CYSMCAPTION),
		m_hThreadderWndParent,NULL,GetModuleHandle(0),NULL);
}

void CPolyBumpWorkerThread::DestroyThreadderHWND()
{
	::ShowWindow(m_hThreadderWnd,SW_HIDE);
	DestroyWindow(m_hThreadderWnd);
	m_hThreadderWnd=0;

	PostMessage(m_hThreadderWndParent,WM_POLYBUMPTHREADFINISHED,0,0);				// parent application should update the window

	m_hThreadderWndParent=0;
}












//draw triangle in software
//////////////////////////////////////////////////////////////////////
void CPolyBumpWorkerThread::DrawTriPointer( uint16 *pTriIndices, CPbLowTri &rTri, int color, const bool inbConservative )
{ 
	float fU[3],fV[3];

	for(int i=0;i<3;i++)
	{
		fU[i]=(float)(rTri.m_fS[i]*(float)m_nImageSizeX);
		fV[i]=(float)(rTri.m_fT[i]*(float)m_nImageSizeY);
	}

	CSimpleTriangleRasterizer Raster(m_nImageSizeX,m_nImageSizeY);

	assert(color<0xffff);
	Raster.DTFlatFill<uint16>(pTriIndices,m_nImageSizeX,fU,fV,(uint16)color,inbConservative);
}


// draw low res tris pointers
void CPolyBumpWorkerThread::DrawTrisPointers( uint16 *pTriIndices, CPbLowMesh *pLowMesh, const int iMaterialId )
{
	bool bDebug = m_Properties.m_bOutputDebugInfo;
	uint32 k;

	// conservative
  for(k=0;k<pLowMesh->m_nNumTris;k++)  
    if(iMaterialId==-1 || pLowMesh->m_pMesh[k].m_iTriMaterialID==iMaterialId)
			DrawTriPointer(pTriIndices,pLowMesh->m_pMesh[k],k,true);

//	if(bDebug)
//		PIX_SaveTGA32("DrawTrisPointersCon.tga",(unsigned char *)m_pnTriPointer,m_nImageSizeX,m_nImageSizeY,false,false);

	// non conservative
	for(k=0;k<pLowMesh->m_nNumTris;k++)  
		if(iMaterialId==-1 || pLowMesh->m_pMesh[k].m_iTriMaterialID==iMaterialId)
			DrawTriPointer(pTriIndices,pLowMesh->m_pMesh[k],k,false);

//	if(bDebug)
//		PIX_SaveTGA32("DrawTrisPointersBoth.tga",(unsigned char *)m_pnTriPointer,m_nImageSizeX,m_nImageSizeY,false,false);
}


// set attribute
void CPolyBumpWorkerThread::SetStartTime( DWORD indwStartTime )
{
	m_dwStartTime=indwStartTime;
	m_dwComputationTime=0;
}


// get attribute
DWORD CPolyBumpWorkerThread::GetStartTime()
{
	return m_dwStartTime;
}



void CPolyBumpWorkerThread::DebugLog( const char *sFormat, ... )
{
	va_list vl;
	static char sTraceString[1024];

	va_start(vl, sFormat);
	vsprintf(sTraceString, sFormat, vl);
	va_end(vl);

	OutputDebugString(sTraceString);
	OutputDebugString("\n");

	if(!m_Properties.m_bOutputDebugInfo)
		return;

	FILE *out=fopen("PolyBumpDebug.txt","ab");

	if(!out)
		return;

	OutputDebugString(sTraceString);	
	OutputDebugString("\n");	

	fprintf(out,"%s\r\n",sTraceString);

	m_Properties.m_bSummariesString+=sTraceString;
	m_Properties.m_bSummariesString+="\n";

	fclose(out);
}




bool CPolyBumpWorkerThread::StartComputationSetup( HWND hThreadderParent )
{
	if(GetThreadStatus())
		return false;

	m_bSetupPhase=true;
	m_hThreadderWndParent=hThreadderParent;

	m_HighMeshData.FreeData();

	CreateThreadderHWND();

	return true;
}

void CPolyBumpWorkerThread::CancelComputationSetup()
{
	m_LowMesh.FreeData();
	m_HighMeshData.FreeData();
	m_CageMesh.FreeData();
	m_bSetupPhase=false;
	DestroyThreadderHWND();
}


void CPolyBumpWorkerThread::StartComputation()
{
	StartThread();
}


CSRFData *CPolyBumpWorkerThread::GetSRFData() 
{
	if(!m_bComputationFinished)
		return 0;

	return &m_SRFData; 
}
