// Template, major revision 6, update for INFOMOV
// IGAD/NHTV/UU - Jacco Bikker - 2006-2016

#pragma once

#include "math.h"
#include "stdlib.h"
#include "emmintrin.h"
#include "stdio.h"
#include "windows.h"
#include "surface.h"
#include <vector>
#include "freeimage.h"
#include "threads.h"

extern "C" 
{ 
#include "glew.h" 
}
#include "gl.h"
#include "io.h"
#include <ios>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include "SDL.h"
#include "SDL_syswm.h"
#include "wglext.h"
#include "fcntl.h"

using namespace Tmpl8;				// to use template classes
using namespace std;				// to use stl vectors

inline float Rand( float range ) { return ((float)rand() / RAND_MAX) * range; }
inline int IRand( int range ) { return rand() % range; }
int filesize( FILE* f );

namespace Tmpl8 {

#define PI					3.14159265358979323846264338327950f
#define INVPI				0.31830988618379067153776752674503f

#define MALLOC64(x)			_aligned_malloc(x,64)
#define FREE64(x)			_aligned_free(x)
#define PREFETCH(x)			_mm_prefetch((const char*)(x),_MM_HINT_T0)
#define PREFETCH_ONCE(x)	_mm_prefetch((const char*)(x),_MM_HINT_NTA)
#define PREFETCH_WRITE(x)	_m_prefetchw((const char*)(x))
#define loadss(mem)			_mm_load_ss((const float*const)(mem))
#define broadcastps(ps)		_mm_shuffle_ps((ps),(ps), 0)
#define broadcastss(ss)		broadcastps(loadss((ss)))

struct Timer 
{ 
	typedef long long value_type; 
	static double inv_freq; 
	value_type start; 
	Timer() : start( get() ) { init(); } 
	float elapsed() const { return (float)((get() - start) * inv_freq); } 
	static value_type get() 
	{ 
		LARGE_INTEGER c; 
		QueryPerformanceCounter( &c ); 
		return c.QuadPart; 
	} 
	static double to_time(const value_type vt) { return double(vt) * inv_freq; } 
	void reset() { start = get(); }
	static void init() 
	{ 
		LARGE_INTEGER f; 
		QueryPerformanceFrequency( &f ); 
		inv_freq = 1000./double(f.QuadPart); 
	} 
}; 

typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned char byte;

#define BADFLOAT(x) ((*(uint*)&x & 0x7f000000) == 0x7f000000)

// vectors
class float2 // adapted from https://github.com/dcow/RayTracer
{
public:
	union { struct { float x, y; }; float cell[2]; };
	float2() {}
	float2( float v ) : x( v ), y( v ) {}
	float2( float x, float y ) : x( x ), y( y ) {}
	float2 operator - () const { return float2( -x, -y ); }
	float2 operator + ( const float2& addOperand ) const { return float2( x + addOperand.x, y + addOperand.y ); }
	float2 operator - ( const float2& operand ) const { return float2( x - operand.x, y - operand.y ); }
	float2 operator * ( const float2& operand ) const { return float2( x * operand.x, y * operand.y ); }
	float2 operator * ( float operand ) const { return float2( x * operand, y * operand ); }
	void operator -= ( const float2& a ) { x -= a.x; y -= a.y; }
	void operator += ( const float2& a ) { x += a.x; y += a.y; }
	void operator *= ( const float2& a ) { x *= a.x; y *= a.y; }
	void operator *= ( float a ) { x *= a; y *= a; }
	float& operator [] ( const int idx ) { return cell[idx]; }
	float length() { return sqrtf( x * x + y * y ); }
	float sqrLentgh() { return x * x + y * y; }
	float2 normalized() { float r = 1.0f / length(); return float2( x * r, y * r ); }
	void normalize() { float r = 1.0f / length(); x *= r; y *= r; }
	static float2 normalize( float2 v ) { return v.normalized(); }
	float dot( const float2& operand ) const { return x * operand.x + y * operand.y; }
};

class float3
{
public:
	union { struct { float x, y, z; }; float cell[3]; };
	float3() {}
	float3( float v ) : x( v ), y( v ), z( v ) {}
	float3( float x, float y, float z ) : x( x ), y( y ), z( z ) {}
	float3 operator - () const { return float3( -x, -y, -z ); }
	float3 operator + ( const float3& addOperand ) const { return float3( x + addOperand.x, y + addOperand.y, z + addOperand.z ); }
	float3 operator - ( const float3& operand ) const { return float3( x - operand.x, y - operand.y, z - operand.z ); }
	float3 operator * ( const float3& operand ) const { return float3( x * operand.x, y * operand.y, z * operand.z ); }
	void operator -= ( const float3& a ) { x -= a.x; y -= a.y; z -= a.z; }
	void operator += ( const float3& a ) { x += a.x; y += a.y; z += a.z; }
	void operator *= ( const float3& a ) { x *= a.x; y *= a.y; z *= a.z; }
	void operator *= ( const float a ) { x *= a; y *= a; z *= a; }
	float operator [] ( const uint& idx ) const { return cell[idx]; }
	float& operator [] ( const uint& idx ) { return cell[idx]; }
	float length() const { return sqrtf( x * x + y * y + z * z ); }
	float sqrLentgh() const { return x * x + y * y + z * z; }
	float3 normalized() const { float r = 1.0f / length(); return float3( x * r, y * r, z * r ); }
	void normalize() { float r = 1.0f / length(); x *= r; y *= r; z *= r; }
	static float3 normalize( const float3 v ) { return v.normalized(); }
	float3 cross( const float3& operand ) const
	{
		return float3( y * operand.z - z * operand.y, z * operand.x - x * operand.z, x * operand.y - y * operand.x );
	}
	float dot( const float3& operand ) const { return x * operand.x + y * operand.y + z * operand.z; }
};

float3 normalize( const float3& v );
float3 cross( const float3& a, const float3& b );
float dot( const float3& a, const float3& b );
float3 operator * ( const float& s, const float3& v );
float3 operator * ( const float3& v, const float& s );

class float4
{
public:
	union { struct { float x, y, z, w; }; struct { float3 xyz; float w2; }; float cell[4]; };
	float4() {}
	float4( float v ) : x( v ), y( v ), z( v ), w( v ) {}
	float4( float x, float y, float z, float w ) : x( x ), y( y ), z( z ), w( w ) {}
	float4( float3 a, float b ) : x( a.x ), y( a.y ), z( a.z ), w( b ) {}
	float4 operator - () const { return float4( -x, -y, -z, -w ); }
	float4 operator + ( const float4& addOperand ) const { return float4( x + addOperand.x, y + addOperand.y, z + addOperand.z, w + addOperand.w ); }
	float4 operator - ( const float4& operand ) const { return float4( x - operand.x, y - operand.y, z - operand.z, w - operand.w ); }
	float4 operator * ( const float4& operand ) const { return float4( x * operand.x, y * operand.y, z * operand.z, w * operand.w ); }
	float4 operator * ( float operand ) const { return float4( x * operand, y * operand, z * operand, w * operand ); }
	void operator -= ( const float4& a ) { x -= a.x; y -= a.y; z -= a.z; w -= a.w; }
	void operator += ( const float4& a ) { x += a.x; y += a.y; z += a.z; w += a.w; }
	void operator *= ( const float4& a ) { x *= a.x; y *= a.y; z *= a.z; w *= a.w; }
	void operator *= ( float a ) { x *= a; y *= a; z *= a; w *= a; }
	float& operator [] ( const int idx ) { return cell[idx]; }
	float operator [] ( const uint& idx ) const { return cell[idx]; }
	float length() { return sqrtf( x * x + y * y + z * z + w * w ); }
	float sqrLentgh() { return x * x + y * y + z * z + w * w; }
	float4 normalized() { float r = 1.0f / length(); return float4( x * r, y * r, z * r, w * r ); }
	void normalize() { float r = 1.0f / length(); x *= r; y *= r; z *= r; w *= r; }
	static float4 normalize( float4 v ) { return v.normalized(); }
	float dot( const float4& operand ) const { return x * operand.x + y * operand.y + z * operand.z + w * operand.w; }
};

class uint4
{
public:
	union { struct { uint x, y, z, w; }; uint cell[4]; };
	uint4() {}
	uint4( int v ) : x( v ), y( v ), z( v ), w( v ) {}
	uint4( int x, int y, int z, int w ) : x( x ), y( y ), z( z ), w( w ) {}
	uint4 operator + ( const uint4& addOperand ) const { return uint4( x + addOperand.x, y + addOperand.y, z + addOperand.z, w + addOperand.w ); }
	uint4 operator - ( const uint4& operand ) const { return uint4( x - operand.x, y - operand.y, z - operand.z, w - operand.w ); }
	uint4 operator * ( const uint4& operand ) const { return uint4( x * operand.x, y * operand.y, z * operand.z, w * operand.w ); }
	uint4 operator * ( uint operand ) const { return uint4( x * operand, y * operand, z * operand, w * operand ); }
	void operator -= ( const uint4& a ) { x -= a.x; y -= a.y; z -= a.z; w -= a.w; }
	void operator += ( const uint4& a ) { x += a.x; y += a.y; z += a.z; w += a.w; }
	void operator *= ( const uint4& a ) { x *= a.x; y *= a.y; z *= a.z; w *= a.w; }
	void operator *= ( uint a ) { x *= a; y *= a; z *= a; w *= a; }
	uint& operator [] ( const int idx ) { return cell[idx]; }
};

class int4
{
public:
	union { struct { int x, y, z, w; }; int cell[4]; };
	int4() {}
	int4( int v ) : x( v ), y( v ), z( v ), w( v ) {}
	int4( int x, int y, int z, int w ) : x( x ), y( y ), z( z ), w( w ) {}
	int4 operator - () const { return int4( -x, -y, -z, -w ); }
	int4 operator + ( const int4& addOperand ) const { return int4( x + addOperand.x, y + addOperand.y, z + addOperand.z, w + addOperand.w ); }
	int4 operator - ( const int4& operand ) const { return int4( x - operand.x, y - operand.y, z - operand.z, w - operand.w ); }
	int4 operator * ( const int4& operand ) const { return int4( x * operand.x, y * operand.y, z * operand.z, w * operand.w ); }
	int4 operator * ( int operand ) const { return int4( x * operand, y * operand, z * operand, w * operand ); }
	void operator -= ( const int4& a ) { x -= a.x; y -= a.y; z -= a.z; w -= a.w; }
	void operator += ( const int4& a ) { x += a.x; y += a.y; z += a.z; w += a.w; }
	void operator *= ( const int4& a ) { x *= a.x; y *= a.y; z *= a.z; w *= a.w; }
	void operator *= ( int a ) { x *= a; y *= a; z *= a; w *= a; }
	int& operator [] ( const int idx ) { return cell[idx]; }
};

class matrix
{
public:
	enum 
	{ 
		TX=3, 
		TY=7, 
		TZ=11, 
		D0=0, D1=5, D2=10, D3=15, 
		SX=D0, SY=D1, SZ=D2, 
		W=D3 
	};
	matrix() { Identity(); }
	float& operator [] ( int a_N ) { return cell[a_N]; }
	void Identity()
	{
		cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] =
		cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0;
		cell[D0] = cell[D1] = cell[D2] = cell[W] = 1;
	}
	void Init( float3 a_Pos, float a_RX, float a_RY, float a_RZ )
	{
		matrix t;
		t.RotateX( a_RZ );
		RotateY( a_RY );
		Concatenate( t );
		t.RotateZ( a_RX );
		Concatenate( t );
		Translate( a_Pos );
	}
	void RotateX( float a_RX )
	{
		float sx = (float)sin( a_RX * PI / 180 );
		float cx = (float)cos( a_RX * PI / 180 );
		Identity();
		cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx;
	}
	void RotateY( float a_RY )
	{
		float sy = (float)sin( a_RY * PI / 180 );
		float cy = (float)cos( a_RY * PI / 180 );
		Identity ();
		cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy;
	}
	void RotateZ( float a_RZ )
	{
		float sz = (float)sin( a_RZ * PI / 180 );
		float cz = (float)cos( a_RZ * PI / 180 );
		Identity ();
		cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz;
	}
	void Translate( float3 a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; }
	void SetTranslation( float3 a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; }
	void Concatenate( matrix& m2 )
	{
		matrix res;
		int c;
		for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ )
			res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] +
				  				  cell[r * 4 + 1] * m2.cell[c + 4] +
								  cell[r * 4 + 2] * m2.cell[c + 8] +
								  cell[r * 4 + 3] * m2.cell[c + 12];
		for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c];
	}
	float3 Transform( float3& v )
	{
		float x  = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
		float y  = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
		float z  = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
		return float3( x, y, z );
	}
	void Invert()
	{
		matrix t;
		int h, i;
		float tx = -cell[3], ty = -cell[7], tz = -cell[11];
		for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4];
		for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i];
		cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
		cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
		cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
	}
	float cell[16];
};


}; // namespace Tmpl8

#include "game.h"
