#include "template.h"

#define DOTS 65536

using namespace Tmpl8;

float rx = 0, ry = 0, rz = 0;

//-----------------------------------------------------------
// Game::Sort
// Sort the dots according to depth
//-----------------------------------------------------------
void Swap( float3& a, float3& b ) { float3 t = a; a = b; b = t; }
int Pivot( float3 a[], int first, int last )
{
   int p = first;
   float3 e = a[first];
   for( int i = first + 1; i <= last; i++ ) if (a[i].z <= e.z) Swap( a[i], a[++p] );
   Swap( a[p], a[first] );
   return p;
}
void QuickSort( float3 a[], int first, int last)
{
   int pivotElement;
   if (first >= last) return;
   pivotElement = Pivot( a, first, last );
   QuickSort( a, first, pivotElement - 1 );
   QuickSort( a, pivotElement + 1, last );
}
void Game::Sort()
{
	QuickSort( m_Rotated, 0, DOTS - 1 );
}

//-----------------------------------------------------------
// Game::Init
// Initialize the application
//-----------------------------------------------------------
Sprite* scaled[64];
void Game::Init()
{	
	m_Dot = new Sprite( new Surface( "testdata/dot.tga" ), 1 );	
	m_Dot->SetFlags( Sprite::FLARE );
	m_Points = new float3[DOTS];
	m_Rotated = new float3[DOTS];
	for ( int i = 0; i < DOTS; i++ )
	{
		float dist, x, y, z;
		do
		{
			x = Rand( 2 ) - 1, y = Rand( 2 ) - 1, z = Rand( 2 ) - 1;
			dist = sqrtf( x * x + y * y + z * z );
		}
		while (dist > 1);
		m_Points[i] = float3( x, y, z );
	}
 for( int i = 0; i < 64; i++ )
   {
      int size = i + 1;
      scaled[i] = new Sprite( new Surface( size, size ), 1 );
      scaled[i]->GetSurface()->Clear( 0 );
      m_Dot->DrawScaled( 0, 0, size, size, scaled[i]->GetSurface() );
   }

}

//-----------------------------------------------------------
// Game::Transform
// Rotate the dots around the origin using matrix m
//-----------------------------------------------------------
void Game::Transform()
{
	matrix m;
	float3 pos( 0, 0, 0 );
	m.Identity();
	m.Init( pos, rx, ry, rz );
	rx += 0.02f, ry += 0.035f, rz += 0.01f;
	if (rx > 360) rx -= 360;
	if (ry > 360) ry -= 360;
	if (rz > 360) rz -= 360;
	for ( int i = 0; i < DOTS; i++ ) m_Rotated[i] = m.Transform( m_Points[i] );
}

//-----------------------------------------------------------
// Game::Render
// Draw the dots to the window surface
//-----------------------------------------------------------
void Game::Render()
{
	// m_Surface->Clear( 0 );
	memset( m_Surface->GetBuffer(), 0, SCRWIDTH * SCRHEIGHT * 4 );
	for ( int i = 0; i < DOTS; i++ )
	{
		int sx = (int)(m_Rotated[i].x * 280.0f) + 400;
		int sy = (int)(m_Rotated[i].y * 280.0f) + 300;
		int size = (int)((m_Rotated[i].z + 2) * 10.0f);
		// m_Dot->DrawScaled( (float)(sx - size / 2), (float)(sy - size / 2), (float)size, (float)size, m_Surface );
		if (m_Rotated[i].z > -0.2f) 
		{
			float3 v = m_Rotated[i];
			float d = v.x * v.x + v.y * v.y + v.z * v.z;
			if (d > (0.9f * 0.9f)) scaled[size]->Draw( (sx - size / 2), (sy - size / 2), m_Surface );
		}
	}
}

//-----------------------------------------------------------
// Game::Tick
// Main function of application
//-----------------------------------------------------------
Timer t;
void Game::Tick( float a_DT )
{
	float elapsed1, elapsed2, elapsed3;
	
	t.reset();	Transform();	elapsed1 = t.elapsed();
	t.reset();	Sort();			elapsed2 = t.elapsed();
	t.reset();	Render();		elapsed3 = t.elapsed();

	char t[200];
	sprintf( t, "transform: %7.4f", elapsed1 );
	m_Surface->Print( t, 2, 2, 0x11ffffff );
	sprintf( t, "sort:      %7.4f", elapsed2 );
	m_Surface->Print( t, 2, 12, 0x11ffffff );
	sprintf( t, "render:    %7.4f", elapsed3 );
	m_Surface->Print( t, 2, 22, 0x11ffffff );
}