#include "system.h"

static Texture* clOutput = 0;
static Shader* shader = 0;
static Kernel* clearKernel = 0, *renderKernel = 0, *render2Kernel = 0, *resetKernel = 0, *gridKernel = 0;
static Kernel* sim1Kernel = 0, *sim2Kernel = 0, *simXKernel = 0;
static Buffer* outputBuffer, *balls, *balls2, *grid;
static float gr = 0;

bool Game::Init()
{
	// load shader and texture
	clOutput = new Texture( SCRWIDTH, SCRHEIGHT, Texture::FLOAT );
	shader = new Shader( "shaders/vignette.vert", "shaders/vignette.frag" );
	// load OpenCL code
	clearKernel = new Kernel( "programs/program.cl", "clear" );
	renderKernel = new Kernel( "programs/program.cl", "render" );
	render2Kernel = new Kernel( "programs/program.cl", "render2" );
	resetKernel = new Kernel( "programs/program.cl", "clearGrid" );
	gridKernel = new Kernel( "programs/program.cl", "fillGrid" );
	sim1Kernel = new Kernel( "programs/program.cl", "simulate1" );
	sim2Kernel = new Kernel( "programs/program.cl", "simulate2" );
	simXKernel = new Kernel( "programs/program.cl", "simulateX" );
	// link cl output texture as an OpenCL buffer
	outputBuffer = new Buffer( clOutput->GetID(), Buffer::TARGET );
	balls = new Buffer( BALLCOUNT * 4 * sizeof( float ) );
	balls2 = new Buffer( BALLCOUNT * 4 * sizeof( float ) );
	grid = new Buffer( GRIDX * GRIDY * (BALLSPERCELL + 1) * sizeof( unsigned int ) );
	clearKernel->SetArgument( 0, outputBuffer );
	renderKernel->SetArgument( 0, outputBuffer );
	renderKernel->SetArgument( 1, balls );
	renderKernel->SetArgument( 2, grid );
	render2Kernel->SetArgument( 0, outputBuffer );
	render2Kernel->SetArgument( 1, balls );
	render2Kernel->SetArgument( 2, grid );
	resetKernel->SetArgument( 0, grid );
	gridKernel->SetArgument( 0, balls );
	gridKernel->SetArgument( 1, grid );
	sim1Kernel->SetArgument( 0, balls );
	sim1Kernel->SetArgument( 1, grid );
	sim2Kernel->SetArgument( 0, balls );
	sim2Kernel->SetArgument( 1, balls2 );
	sim2Kernel->SetArgument( 2, grid );
	simXKernel->SetArgument( 0, balls );
	simXKernel->SetArgument( 1, balls2 );
	simXKernel->SetArgument( 2, grid );
	// put initial ball positions in buffer
	float* fb = (float*)balls->GetHostPtr();
	for( int i = 0; i < BALLCOUNT; i++ )
	{
		fb[i * 4] = Rand( 1 );
		fb[i * 4 + 1] = Rand( 1 );
		fb[i * 4 + 2] = fb[i * 4 + 0];
		fb[i * 4 + 3] = fb[i * 4 + 1];
	}
	balls->CopyToDevice();
	// done
	return true;
}

void Game::Tick()
{
	// initialize
	clearKernel->Run( outputBuffer );
	// simulate stage 1
	float angle = 50 * sinf( gr ) * PI / 180;
	gr += 0.005f;
	if (gr >= 2 * PI) gr -= 2 * PI;
	float gx = 0; // 0.0002f * sinf( angle );
	float gy = 0; // 0.0002f * cosf( angle );
	sim1Kernel->SetArgument( 1, gx );
	sim1Kernel->SetArgument( 2, gy );
	sim1Kernel->Run( BALLCOUNT );
	// update grid
	resetKernel->Run( GRIDX * GRIDY );
	gridKernel->Run( BALLCOUNT );
	// simulate stage 2
	balls->CopyTo( balls2 );
#if 0
	sim2Kernel->Run( GRIDX * GRIDY );
#else
	POINT p;
	GetCursorPos( &p );
	ScreenToClient( GetWindowHandle(), &p );
	float mx = (float)p.x / SCRWIDTH;
	float my = (float)p.y / SCRHEIGHT;
	simXKernel->SetArgument( 3, mx );
	simXKernel->SetArgument( 4, 1 - my );
	simXKernel->Run( BALLCOUNT );
#endif
	// update grid
	resetKernel->Run( GRIDX * GRIDY );
	gridKernel->Run( BALLCOUNT );
	// run cl code to fill texture
#if 0
	render2Kernel->Run( outputBuffer, GRIDX * GRIDY );
#else
	renderKernel->Run( outputBuffer );
#endif
	// run shader on cl-generated texture
	shader->Bind();
	shader->SetInputTexture( GL_TEXTURE0, "color", clOutput );
	shader->SetInputMatrix( "view", mat4::Identity() );
	DrawQuad();
}

void Game::Shutdown()
{
	delete clOutput;
	delete shader;
	delete renderKernel;
}

// EOF