#include <stdio.h>
#include <stdlib.h>
#include <graph.h>
#include <string.h>
#include "utils.h"
#include "taquin.h"
#include "graph_sup.h"

/* La marge en pixel entre chaque pièce du Taquin */
#define MARGIN 1

/* Définit le nombre d'itérations de la fonction random à faire
afin de randomiser le taquin */
#define RANDOM_ITER 50000

int OffsetX, OffsetY;
int ImageX, ImageY, PieceX, PieceY;

int Lines, Columns;
int MoveCount;
unsigned char* Taquin;

char Filename[100];

/* Dessine la partie de l'image correspondante à l'indice donné */
void DrawPiece(int index, couleur color) {
	int CoordX = (index % Columns) * PieceX;
	int CoordY = (index / Columns) * PieceY;

	int ImCoordX = (Taquin[index] % Columns) * PieceX;
	int ImCoordY = (Taquin[index] / Columns) * PieceY;

	if (Taquin[index] != 0) ChargerImage(Filename, CoordX + OffsetX, CoordY + OffsetY, ImCoordX, ImCoordY, PieceX, PieceY);
	else {
		SetColor(255, 255, 255);
		RemplirRectangle(CoordX + OffsetX, CoordY + OffsetY, PieceX, PieceY);
	}
	SetColorC(color);
	DessinerRectangle(CoordX + OffsetX, CoordY + OffsetY, PieceX - 1, PieceY - 1);
}

void UpdateTaquin() {
	int i;
	couleur color = GetColor(0, 0, 0);
	for (i = 0; i < Lines * Columns; i++) DrawPiece(i, color);
}

/* Vérifie la validité des mouvements et met à jour le taquin graphiquement
si l'argument should_update est égal à 1.*/
int MovePiece(int index, int should_update) {
	int moves[4] = {
		index + 1,
		index - 1,
		index + Columns,
		index - Columns
	};

	int i;
	couleur selected = GetColor(255, 0, 0), black = GetColor(0, 0, 0);
	for (i = 0; i < 4; i++) {
		if (moves[i] >= 0 && moves[i] < Columns * Lines) {
			if (Taquin[moves[i]] == 0) {
				Taquin[moves[i]] = Taquin[index];
				Taquin[index] = 0;

				if (should_update) {
					DrawPiece(moves[i], black);
					DrawPiece(index, selected);

					ChargerImage("./images/assets/move_count.png", 497, 600, 0, 0, 205, 85);
					SetColor(192, 205, 228);

					char str[5];
					sprintf(str, "%d", MoveCount);
					EcrireTexte(620, 650, str, 2);
					MoveCount++;
				}
				return 1;
			}
		}
	}

	return 0;
}

/* Randomise le Taquin */
void RandomizeTaquin() {
	int index = 0, last_move = -1, move, i;
	int moves[4] = {1, -1, Columns, -Columns};
	int piece_count = Lines * Columns;
	/* L'aléatoire n'est pas idéal, afin de mieux exploiter
	la fonction, on devrait éviter de faire des coups inutiles
	du genre essayer de sortir du Taquin mais elle marche
	relativement bien au delà de ces problèmes. */
	MoveCount = 0;

	for (i = 0; i < RANDOM_ITER; i++) {
		srand(Microsecondes());
		move = rand() % 4;
		if (move != last_move) {
			move = moves[move];
			if (index + move >= 0 && index + move < piece_count) {
				MovePiece(index, 0);
				index += move;
			}
		}
	}

	ChargerImage("./images/assets/move_count.png", 497, 600, 0, 0, 205, 85);
	SetColor(192, 205, 228);

	char str[5];
	sprintf(str, "%d", MoveCount);
	EcrireTexte(620, 650, str, 2);
}

/* Renvoie 1 si le taquin est en ordre */
int CheckForVictory() {
	int i;
	for (i = 0; i < Lines * Columns; i++) {
		if (Taquin[i] != i) return 0;
	}

	return 1;
}

/* Bloque le programme pendant la partie, appelle les fonctions liées au coups
et aussi celles afin de randomiser et quitter le taquin. */
int TaquinRenderLoop() {
	int button_id = -1, last_id = -1;
	int controller = 0, key;

	couleur selected = GetColor(255, 0, 0), black = GetColor(0, 0, 0);

	while (1) {
		if (SourisCliquee()) {
			controller = 0;
			button_id = GetButton(_X, _Y);
			if (button_id >= 0) {
				MovePiece(button_id, 1);
				if (CheckForVictory()) return 1;
			}
			else if (button_id == -3) {
				RandomizeTaquin();
				UpdateTaquin();
			}
			else if (button_id == -2) return 0;
		}
		
		if (ToucheEnAttente()) {
			controller = 1;

			key = Touche();
			if (button_id < 0) button_id = 0;
				
			if (key == XK_Left || key == XK_q) {
				if (button_id - 1 >= 0) {
					last_id = button_id;
					button_id -= 1;
				}
			} else if (key == XK_Right || key == XK_d) {
				if (button_id + 1 < Columns * Lines) {
					last_id = button_id;
					button_id += 1;
				}
			} else if (key == XK_Up || key == XK_z) {
				if (button_id - Columns >= 0) {
					last_id = button_id;
					button_id -= Columns;
				}
			} else if (key == XK_Down || key == XK_s) {
				if (button_id + Columns < Lines * Columns) {
					last_id = button_id;
					button_id += Columns;
				}
			} else if (key == XK_space) {
				MovePiece(button_id, 1);
				if (CheckForVictory()) return 1;
			}

			if (button_id >= 0) DrawPiece(button_id, selected);
			if (last_id >= 0) DrawPiece(last_id, black);
		}

		if (DrawNextFrame()) {
			if (controller == 0) {
				SourisPosition();
				button_id = GetButton(_X, _Y);
				if (button_id != last_id) {
					if (button_id > -1) DrawPiece(button_id, selected);
					if (last_id > -1) DrawPiece(last_id, black);
					last_id = button_id;
				}
			}
		}
	}

	return 0;
}

/* L'écran de victoire */
void ShowVictoryScreen() {
	ClearButtons();
	AddButton(WIDTH/4 + 60, HEIGHT/4 + 250, 230, 78, 1);
	AddButton(WIDTH/4 + 312, HEIGHT/4 + 250, 230, 78, 2);

	int BT_ID;

	while (1) {
		if (DrawNextFrame()) {
			ChargerImage("./images/assets/win_screen.png", WIDTH/4, HEIGHT/4, 0, 0, WIDTH/2, HEIGHT/2);

			if (SourisCliquee()) {
				BT_ID = GetButton(_X, _Y);
				if (BT_ID == 1) {
					return;
				} else if (BT_ID == 2) {
					CreateTaquin(Filename, ImageX, ImageY, Lines, Columns);
					break;
				}
			}
		}
	}
}

/* Crée le Taquin et lance les fonctions liées à la logique et au graphisme du Taquin */
void CreateTaquin(char * filename, int ImageWidth, int ImageHeight, int lines, int columns) {
	/* On externalise les valeurs données afin de pouvoir facilement les utiliser plus tard. */
	int i;
	
	Lines = lines;
	Columns = columns;
	strcpy(Filename, filename);

	ImageX = ImageWidth;
	ImageY = ImageHeight;
	PieceX = ImageX / Columns;
	PieceY = ImageY / Lines;

	OffsetX = WIDTH - 50 - ImageX;
	OffsetY = (HEIGHT - ImageY) / 2 - 50;

	/* On alloue l'espace mémoire nécéssaire au Taquin et on l'initialise en premier résolu. 
	   On ajoute aussi les boutons servant à vérifier les clics souris.*/
	Taquin = calloc(Lines * Columns, sizeof(unsigned char));
	for (i = 0; i < Lines * Columns; i++) {
		AddButton(OffsetX + (i % Columns) * PieceX, OffsetY +(i / Columns) * PieceY, PieceX, PieceY, i);
		Taquin[i] = i;
	}

	ChargerImage("./images/assets/background_taquin.png", 0, 0, 0, 0, 1200, 700);
	ChargerImage(Filename, 50, OffsetY, 0, 0, ImageX, ImageY);

	AddButton(WIDTH/4 - 100, HEIGHT -100, 200, 80, -2); //Bouton Quitter
	AddButton(WIDTH/4 * 3 - 100, HEIGHT - 100, 200, 80, -3); //Bouton Randomiser

	RandomizeTaquin();
	UpdateTaquin();
	
	if (TaquinRenderLoop()) {
		ShowVictoryScreen();
	}
}
