#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "SDL2/SDL.h"

#define		REPERE		    (LENGTH * (WIDTH/2 - 1)) + ((LENGTH/2 - 1))
#define		BPP			    4
#define		LENGTH		    1024     // x
#define		WIDTH		    800     // y
#define     DEPTH           800     // z
#define		PITCH		    LENGTH * 4
#define		WHITE		    0x00FFFFFF
#define		RED		        0x00FF0000
#define		GREEN           0x0000FF00
#define		BLUE            0x000000FF
#define		DEG2RAD(angle)  angle * 0.017453292  // PI/180
#define		RAPPORT			(LENGTH/WIDTH)
#define		FOCALE          800
#define		PROFONDEUR      2000


float       rotation_object[3];     // 0: x , 1: y , 2: z
float       translate_object[3];    // 0: x , 1: y , 2: z
static int  WindowsD[LENGTH*WIDTH] = { 0 };

typedef struct a
{

	float x;

	float y;

	float z;

	float cosX;
	float cosY;
	float cosZ;
	float sinX;
	float sinY;
	float sinZ;
	float x_end;
	float y_end;
	float z_end;

}a;
struct a az;
void        put_pixel(float x, float y, float z, int color, float *repere)
{

	int     offset_pixel;

	az.cosX = cos(DEG2RAD(rotation_object[0]));
	az.cosY = cos(DEG2RAD(rotation_object[1]));
	az.cosZ = cos(DEG2RAD(rotation_object[2]));
	az.sinX = sin(DEG2RAD(rotation_object[0]));
	az.sinY = sin(DEG2RAD(rotation_object[1]));
	az.sinZ = sin(DEG2RAD(rotation_object[2]));

	az.x = x;
	az.y = y;
	az.z = z;
	az.x += repere[0];
	az.y += repere[1];
	az.z += repere[2];
	az.x_end = az.x * ((az.cosY * az.cosZ)) - az.y * ((az.cosY * az.sinZ)) - az.z * (az.sinY);
	az.y_end = az.x * ((az.cosX * az.sinZ) - (az.sinX * az.sinY * az.cosZ)) + az.y * ((az.sinX * az.sinY * az.sinZ) + (az.cosX * az.cosZ)) - az.z * (az.sinX * az.cosY);
	az.z_end = az.x * ((az.cosX * az.sinY * az.cosZ) + (az.sinX * az.sinZ)) + az.y * ((az.sinX * az.cosZ) - (az.cosX * az.sinY * az.sinZ)) + az.z * (az.cosX * az.cosY);

	az.x = az.x_end;
	az.y = az.y_end;
	az.z = az.z_end;

	az.x = (az.x * FOCALE) / (az.z + PROFONDEUR);
	az.y = (az.y * FOCALE) / (az.z + PROFONDEUR);

	offset_pixel = REPERE - (LENGTH * (int)az.y) + (int)az.x;

	if (offset_pixel < LENGTH*WIDTH && offset_pixel >= 0)
		if ((az.x <= LENGTH / 2 && az.x >= -LENGTH / 2) && (az.y <= WIDTH / 2 && az.y >= -WIDTH / 2))
			WindowsD[offset_pixel] = color;
}

void        trace_circle(float rayon, float *repere)
{
	float   x, y, angle_circle;

	angle_circle = 0;
	while (angle_circle <= 360)
	{
		angle_circle += 0.1;
		x = cos(DEG2RAD(angle_circle)) * rayon;
		y = sin(DEG2RAD(angle_circle)) * rayon;
		put_pixel(x, y, 0, WHITE, repere);
	}
	/*
	rayon = 200;
	x = -rayon;
	while(x <= rayon)
	{
	y = (rayon * rayon) - (x * x);
	PutPixel(x, sqrt(y), BLANC);
	PutPixel(x, -sqrt(y), BLANC);
	SDL_Flip(screen);
	x+=0.001;
	}
	*/
}
/*
void        trace_droite2D(float lenght)
{
float   x, y, a, b, c;

a = b = c = 1;
x = -lenght;
while(x <= lenght)
{
y = -((a * x + c)/b);

put_pixel(x, y, 0, WHITE);
x+=0.1;
}
}

void        rotation_2D(float *object, int size, float yangle_add,
float xangle_add,
float zangle_add)
{
float   x, rayon, xangle_point,
y,        yangle_point,
z,        zangle_point;
int     indice = 0;

while(indice != size)
{
x = object[indice];
y = object[indice + 1];
z = object[indice + 2];

rayon = sqrt(x * x + y * y + z * z);
xangle_point = acos(x/rayon);
yangle_point = asin(y/rayon);
zangle_point = asin(z/rayon);

x = rayon * cos(xangle_point + xangle_add);
y = rayon * sin(yangle_point + yangle_add);
z = rayon * sin(zangle_point + zangle_add);
if (zangle_add !=0)
{
x = rayon * cos(xangle_point + zangle_add);
y = rayon * sin(xangle_point + zangle_add);
}
put_pixel(x, y, z, WHITE);
put_pixel(-x, -y, -z, WHITE);

indice+=3;
}
}

void        make_voxel_weapon(float *cube, float size)
{
float   x, z;
float   cote_voxel_cube = size;
int     indice;
float   angle;

x = cote_voxel_cube;
indice = angle = 0;
while (x > -cote_voxel_cube) //fill_cube_volume
{
z = cote_voxel_cube;
while (z > -cote_voxel_cube) //fill_cube_line
{
angle++;
cube[indice + 0] = cos(DEG2RAD*angle)*x * 8;
cube[indice + 1] = sin(DEG2RAD*angle) *x;
cube[indice + 2] = z  ;
cube[indice + 3] = BLUE ;
indice+=4;
z--;
}
x--;
}

}

*/
void        put_object(float *object, int size, float *repere)
{
	while (size--)
	{
		put_pixel(*(object + 0), *(object + 1), *(object + 2), *(object + 3), repere);
		object += 4;
	}
}

void        clear_screen(void)
{
	int     loop = 0;
	while (loop < LENGTH*WIDTH)
	{
		WindowsD[loop++] = 0;
	}
}
void        reset_phi()
{
	if (rotation_object[0] >= 360)
		rotation_object[0] = 0;

	if (rotation_object[1] >= 360)
		rotation_object[1] = 0;

	if (rotation_object[2] >= 360)
		rotation_object[2] = 0;
}

void        scale_object(float *object, int size_object, float size_scale)
{
	while (size_object--)
	{
		*(object + 0) *= size_scale;    //x
		*(object + 1) *= size_scale;    //y
		*(object + 2) *= size_scale;    //z
		object += 4;
	}
}

void        make_voxel_cube(float *cube, float size)
{
	float   x, y, z;
	float   cote_voxel_cube = size;
	int     indice;

	x = cote_voxel_cube;
	indice = 0;
	while (x > -cote_voxel_cube) //fill_cube_volume
	{
		y = cote_voxel_cube;
		while (y > -cote_voxel_cube) //fill_cube_surface
		{
			z = cote_voxel_cube;
			while (z > -cote_voxel_cube) //fill_cube_line
			{
				cube[indice + 0] = x * 20;
				cube[indice + 1] = y * 20;
				cube[indice + 2] = z * 20;
				cube[indice + 3] = BLUE;
				indice += 4;
				z--;
			}
			y--;
		}
		x--;
	}
}

void        make_voxel_map(float *cube, float size)
{
	float   x, z;
	float   cote_voxel_cube = size;
	int     indice;
	float   angle;

	x = cote_voxel_cube;
	indice = angle = 0;
	while (x > -cote_voxel_cube) //fill_cube_volume
	{
		z = cote_voxel_cube;
		while (z > -cote_voxel_cube) //fill_cube_line
		{
			angle += 5;
			cube[indice + 0] = x * 8;
			cube[indice + 1] = sin(DEG2RAD(angle)) * 20;
			cube[indice + 2] = z * 8;
			cube[indice + 3] = BLUE;
			indice += 4;
			z--;
		}
		angle = 0;
		x--;
	}
}

void        make_repere(float *repere)
{
	float   x, y, z;
	int     indice;

	x = -(LENGTH / 2);
	y = -(WIDTH / 2);
	z = -(DEPTH / 2);
	indice = 0;
	while (x < LENGTH / 2)
	{
		repere[indice + 0] = x++;
		repere[indice + 1] = 0;
		repere[indice + 2] = 0;
		repere[indice + 3] = BLUE;
		indice += 4;
	}

	while (y < WIDTH / 2)
	{
		repere[indice + 0] = 0;
		repere[indice + 1] = y++;
		repere[indice + 2] = 0;
		repere[indice + 3] = GREEN;
		indice += 4;
	}

	while (z < DEPTH / 2)
	{
		repere[indice + 0] = 0;
		repere[indice + 1] = 0;
		repere[indice + 2] = z++;
		repere[indice + 3] = RED;
		indice += 4;
	}
}

int         main(int argc, char** argv)
{
	int             FPS_COUNTER_DATA[4] = { 0 };    // 0: fps , 1: compteur_boucle , 2: last_time , 3: current_time
	int             lock_rotation;
	int             lock_translate;
	float           repere_object[3];
	float           repere_object2[3];
	int             quit = 1;
	static float    voxel_rhino[13998 * 4] = {
#include "Rhino.obj"
	};
	static float    voxel_cube[50 * 50 * 50 * 4];
	static float    voxel_map[300 * 300 * 4];
	static float    repere[(LENGTH + WIDTH + 0) * 4];
	static float    cube[8 * 4] = { /// Façe avant du cube
		///		   X	   Y       Z 	 color
		-100.0, -100.0, 100.0, WHITE  /// Le point en haut à gauche de la façe avant du cube
		, -100.0, 100.0, 100.0, WHITE  /// Le point en bas à gauche de la façe avant du cube
		, 100.0, -100.0, 100.0, WHITE  /// Le point en bas à droite de la façe avant du cube
		, 100.0, 100.0, 100.0, WHITE  /// Le point en haut à droite de la façe avant du cube
		/// Façe arriere du cube
		, -100.0, -100.0, -100.0, WHITE  /// Le point en haut à gauche de la façe arriere du cube
		, -100.0, 100.0, -100.0, WHITE  /// Le point en bas à gauche de la façe arriere du cube
		, 100.0, -100.0, -100.0, WHITE  /// Le point en haut à droite de la façe arriere du cube
		, 100.0, 100.0, -100.0, WHITE  /// Le point en bas à droite de la façe arriere du cube
	};

	// Redirigé la sortie vers la console
	//freopen_s("CON", "w", stdout);

	SDL_Window* WindowsA;
	SDL_Event event;

	SDL_Init(SDL_INIT_VIDEO);
	WindowsA = SDL_CreateWindow("Ma première application SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, LENGTH, WIDTH, SDL_WINDOW_SHOWN);

	SDL_Renderer *WindowsB = SDL_CreateRenderer(WindowsA, -1, SDL_RENDERER_ACCELERATED);
	SDL_Texture *WindowsC = SDL_CreateTexture(WindowsB, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, LENGTH, WIDTH);

	make_voxel_map(voxel_map, 150);
	make_repere(repere);
	scale_object(voxel_rhino, 13997, 450);

	rotation_object[0] = rotation_object[1] = rotation_object[2] = 0;
	translate_object[0] = translate_object[1] = translate_object[2] = 0;
	repere_object[0] = repere_object[1] = repere_object[2] = 0;
	repere_object2[0] = 0;
	repere_object2[1] = 0;
	repere_object2[2] = 0;
	rotation_object[0] = -20;

	while (quit)
	{
		rotation_object[1]++;
		put_object(voxel_map, 300 * 300, repere_object);
		put_object(voxel_rhino, 13998, repere_object);
		/*
		put_object(repere, LENGTH + WIDTH, repere_object);
		trace_circle(100, repere_object);
		trace_circle(100, repere_object2);
		*/

		SDL_UpdateTexture(WindowsC, NULL, WindowsD, PITCH);
		SDL_RenderCopy(WindowsB, WindowsC, NULL, NULL);
		SDL_RenderPresent(WindowsB);
		clear_screen();

		SDL_PollEvent(&event);
		switch (event.type)
		{
		case SDL_QUIT:
			quit = 0;
			break;
		}
		reset_phi();

	calculate_fps:
		{
			FPS_COUNTER_DATA[3] = SDL_GetTicks();
			FPS_COUNTER_DATA[1]++;
			if (FPS_COUNTER_DATA[3] - FPS_COUNTER_DATA[2] >= 1000)
			{
				FPS_COUNTER_DATA[0] = FPS_COUNTER_DATA[1];
				FPS_COUNTER_DATA[1] = 0;
				FPS_COUNTER_DATA[2] = SDL_GetTicks();
			}
			printf("FPS = %d\n", FPS_COUNTER_DATA[0]);
		}
	}
	SDL_DestroyWindow(WindowsA);
	SDL_DestroyRenderer(WindowsB);
	SDL_DestroyTexture(WindowsC);
	SDL_Quit();

	return 0;
}
/*
void    create_window()
{
asm("xorl %eax, %eax        \n"
"xorl %ebx, %ebx        \n"
"xorl %ecx, %ecx        \n"
"xorl %edx, %edx        \n"
"pushl %ecx             \n" //$0x0
"pushl $0x20206c6c      \n" //"  ll"
"pushl $0x642e3233      \n" //"d.23"
"pushl $0x72657375      \n" //"resu"
"movl %esp, %ecx        \n" //store "user32.dll" address in %ecx
"movl $0x7c801d7b, %ebx \n" //store address of LoadLibraryA in %ebx
"pushl %ecx             \n"
"call *%ebx             \n"
"movl $0xef30675e, %ecx \n"
"addl $0x11111111, %ecx \n"
"pushl %ecx             \n"
"pushl $0x42656761      \n"
"pushl $0x7373654d      \n"
"movl %esp, %ecx        \n"
"pushl %ecx             \n"
"pushl %eax             \n"
"movl $0x7c80ae40, %ebx \n"
"call *%ebx             \n"
"movl %esp, %ecx        \n"
"xorl %edx, %edx        \n"
"pushl %edx             \n"
"pushl %ecx             \n"
"pushl %ecx             \n"
"pushl %edx             \n"
"call *%eax             \n"
"xorl %eax, %eax        \n"
"pushl %eax             \n"
"movl $0x7c81cb12, %eax \n"
"call *%eax             \n"
);
}
*/
