#include "system.h"

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

#define BALLCOUNT		4096
#define GRIDX			(SCRWIDTH / 10)
#define GRIDY			(SCRHEIGHT / 10)
#define BALLSPERCELL	8

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" );
	updateKernel = new Kernel( "programs/program.cl", "update" );
	resetKernel = new Kernel( "programs/program.cl", "clearGrid" );
	gridKernel = new Kernel( "programs/program.cl", "fillGrid" );
	// link cl output texture as an OpenCL buffer
	outputBuffer = new Buffer( clOutput->GetID(), Buffer::TARGET );
	balls = new Buffer( BALLCOUNT * 6 * 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 );
	resetKernel->SetArgument( 0, grid );
	gridKernel->SetArgument( 0, balls );
	gridKernel->SetArgument( 1, grid );
	updateKernel->SetArgument( 0, balls );
	// put initial ball positions in buffer
	float* fb = (float*)balls->GetHostPtr();
	for( int i = 0; i < BALLCOUNT; i++ )
	{
		fb[i * 6] = Rand( 1 );
		fb[i * 6 + 1] = Rand( 1 );
		fb[i * 6 + 2] = Rand( 0.01f ) - 0.005f;
		fb[i * 6 + 3] = Rand( 0.01f ) - 0.005f;
		fb[i * 6 + 4] = fb[i * 6 + 0];
		fb[i * 6 + 5] = fb[i * 6 + 1];
	}
	balls->CopyToDevice();
	// done
	return true;
}

void Game::Tick()
{
	// initialize
	clearKernel->Run( outputBuffer );
	// update ball positions
	updateKernel->Run( BALLCOUNT );
	// update grid
	resetKernel->Run( GRIDX * GRIDY );
	gridKernel->Run( BALLCOUNT );
	// run cl code to fill texture
	renderKernel->Run( outputBuffer );
	// 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;
	delete updateKernel;
}

// EOF