#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "game.h"
#include "game_aux.h"
#include "game_ext.h"
#include "game_tools.h"

/* TEST Dummy */

bool test_dummy(void) { return true; }

/* TEST game_print */
bool test_game_print() {
  game g = game_default();
  if (g == NULL) return false;
  game_print(g);
  game_delete(g);
  // Test for a rectangle game, with every direction for every piece
  g = game_new_empty_ext(NB_SHAPES, NB_DIRS, true);
  for (int s = 0; s < NB_SHAPES; s++) {
    for (int d = 0; d < NB_DIRS; d++) {
      game_set_piece_orientation(g, s, d, d);
      game_set_piece_shape(g, s, d, s);
    }
  }
  game_print(g);
  game_delete(g);
  return true;
}

/* TEST game_delete */
bool test_game_delete() {
  game g = game_default();
  if (g == NULL) return false;
  game_delete(g);

  g = game_new_empty_ext(4, 3, true);
  if (g == NULL) return false;
  // We play multiple moves to verify if everything's alrigth about the history
  for (int i = 0; i < 5; i++) {
    game_play_move(g, 0, 0, 1);
  }
  game_delete(g);
  return true;
}

/*TEST game_new */
bool test_game_new() {
  shape shapes[DEFAULT_SIZE * DEFAULT_SIZE] = {EMPTY};
  direction orientations[DEFAULT_SIZE * DEFAULT_SIZE] = {NORTH};
  game g = game_new(shapes, orientations);
  if (g == NULL) return false;
  game_print(g);
  game_delete(g);
  return true;
}

/*TEST game_is_well_paired */
// Already have this test (cf game_test_pricaud), is updated there
bool test_game_is_well_paired() {
  game g = game_default_solution();
  if (g == NULL) return false;

  bool result = game_is_well_paired(g);
  printf("Well paired: %s\n", result ? "Yes" : "No");
  game_delete(g);
  return result;
}

/*TEST game_has_half_edge*/
bool test_game_has_half_edge() {
  game g = game_default_solution();
  if (g == NULL) return false;
  bool result_south = game_has_half_edge(g, 0, 0, SOUTH);
  bool result_north = game_has_half_edge(g, 0, 0, NORTH);
  game_delete(g);

  shape s[6] = {TEE, TEE};
  direction d[6] = {WEST, EAST};
  g = game_new_ext(1, 2, s, d, true);
  bool test_ext =
      game_has_half_edge(g, 0, 0, NORTH) && game_has_half_edge(g, 0, 0, WEST) &&
      game_has_half_edge(g, 0, 0, SOUTH) && game_has_half_edge(g, 0, 1, EAST);
  game_delete(g);
  return result_south && !result_north && test_ext;
}

/*TEST game_get_piece_shape*/
// Test already done (cf game_test_niclespinass), updated there
bool test_game_get_piece_shape() {
  game g = game_default_solution();
  if (g == NULL) return false;
  shape s = game_get_piece_shape(g, 0, 0);
  printf("Piece shape at (0,0): %d\n", s);
  game_delete(g);
  return s != EMPTY;
}

bool test_game_is_wrapping() {
  game g = game_new_empty_ext(5, 4, true);
  bool test_t = game_is_wrapping(g);
  game_delete(g);

  g = game_new_empty_ext(4, 5, false);
  bool test_f = game_is_wrapping(g);
  game_delete(g);
  return test_t && !test_f;
}

bool test_game_new_empty_ext() {
  game g = game_new_empty_ext(3, 4, true);
  if (game_nb_rows(g) != 3 || game_nb_cols(g) != 4 ||
      game_is_wrapping(g) != true) {
    return false;
  }
  for (int row = 0; row < game_nb_rows(g); row++) {
    for (int col = 0; col < game_nb_cols(g); col++) {
      if (game_get_piece_shape(g, row, col) != EMPTY ||
          game_get_piece_orientation(g, row, col) != NORTH) {
        return false;
      }
    }
  }
  game_delete(g);
  return true;
}

bool test_game_new_ext() {
  int nb_rows = 3, nb_cols = 2;
  shape shapes[] = {CORNER, ENDPOINT, TEE, SEGMENT, CORNER, CROSS};
  direction directions[] = {NORTH, EAST, SOUTH, WEST, NORTH, SOUTH};
  game g = game_new_ext(nb_rows, nb_cols, shapes, directions, true);

  if (game_nb_rows(g) != nb_rows || game_nb_cols(g) != nb_cols ||
      game_is_wrapping(g) != true) {
    return false;
  }
  for (int row = 0; row < nb_rows; row++) {
    for (int col = 0; col < nb_cols; col++) {
      if (game_get_piece_shape(g, row, col) !=
              shapes[row * game_nb_cols(g) + col] ||
          game_get_piece_orientation(g, row, col) !=
              directions[row * game_nb_cols(g) + col]) {
        return false;
      }
    }
  }
  game_delete(g);
  return true;
}

/*TEST game_save*/

bool test_game_save() {
  game g = game_default();
  if (g == NULL) return false;

  game_save(g, "test_game_save.txt");
  game g_loaded = game_load("test_game_save.txt");
  if (g_loaded == NULL) {
    game_delete(g);
    return false;
  }

  bool result = game_equal(g, g_loaded, false);

  game_delete(g);
  game_delete(g_loaded);

  return result;
}

/*TEST game_load*/

bool test_game_load() {
  game g_loaded = game_load("test_game_save.txt");
  if (!g_loaded) {
    fprintf(stderr, "Error: Unable to load game from file.\n");
    return false;
  }

  game_delete(g_loaded);
  return true;
}

/*TEST game_solve*/

bool test_game_solve() {
  printf("[TEST] game_solve\n");

  game test_cases[4] = {
      game_random(3, 3, false, 0, 0),  // Taille réduite pour test
      game_random(3, 3, true, 0, 1),   // Avec wrapping, boucle possible
      game_random(3, 3, false, 2, 0),  // Avec empty
      game_random(3, 3, true, 2, 1)    // Wrapping + empty + boucle
  };

  for (int i = 0; i < 4; i++) {
    if (!test_cases[i]) {
      printf("Erreur : Impossible de générer le jeu %d.\n", i);
      return false;
    }

    printf("Jeu généré %d :\n", i);
    game_print(test_cases[i]);

    printf("Tentative de résolution du jeu %d...\n", i);
    fflush(stdout);

    bool solved = game_solve(test_cases[i]);

    printf("Résultat de game_solve : %s\n", solved ? "Succès" : "Échec");
    game_print(test_cases[i]);

    if (!solved) {
      printf(
          "Échec attendu : game_solve n'a pas trouvé de solution pour le jeu "
          "%d.\n",
          i);
    } else {
      printf("Succès : game_solve a trouvé une solution pour le jeu %d.\n", i);
    }
    game_delete(test_cases[i]);
  }

  printf("Succès : game_solve testé avec jeux solvables et non solvables.\n");
  return true;
}

/* TEST game_nb_solutions */

bool test_game_nb_solutions() {
  printf("[TEST] game_nb_solutions\n");

  game g = game_random(3, 3, false, 0, 0);
  if (!g) {
    printf("Erreur : Impossible de générer le jeu.\n");
    return false;
  }

  printf("Jeu généré :\n");
  game_print(g);
  fflush(stdout);

  printf("Début du calcul de solutions...\n");
  fflush(stdout);

  uint solutions = game_nb_solutions(g);

  printf("Fin du calcul. Nombre de solutions trouvées : %u\n", solutions);
  fflush(stdout);

  if (solutions == 0) {
    printf("Échec attendu : Aucune solution trouvée.\n");
  } else {
    printf("Succès : Nombre de solutions trouvées = %u.\n", solutions);
  }

  game_delete(g);
  printf(
      "Succès : game_nb_solutions testé avec jeux solvables et non "
      "solvables.\n");
  return true;
}

/*USAGE*/

bool usage(int argc, char *argv[]) {
  fprintf(stderr, "Usage: %s <testname> [<...>]\n", argv[0]);
  exit(EXIT_FAILURE);
}
/*  MAIN ROUTINE  */

int main(int argc, char *argv[]) {
  if (argc == 1) usage(argc, argv);

  // start test
  fprintf(stderr, "=> Start test \"%s\"\n", argv[1]);
  bool ok = false;
  if (strcmp("dummy", argv[1]) == 0)
    ok = test_dummy();
  else if (strcmp("game_print", argv[1]) == 0)
    ok = test_game_print();
  else if (strcmp("game_save", argv[1]) == 0)
    ok = test_game_save();
  else if (strcmp("game_load", argv[1]) == 0)
    ok = test_game_load();
  else if (strcmp("game_delete", argv[1]) == 0)
    ok = test_game_delete();
  else if (strcmp("game_new", argv[1]) == 0)
    ok = test_game_new();
  else if (strcmp("game_get_piece_shape", argv[1]) == 0)
    ok = test_game_get_piece_shape();
  else if (strcmp("game_is_well_paired", argv[1]) == 0)
    ok = test_game_is_well_paired();
  else if (strcmp("game_has_half_edge", argv[1]) == 0)
    ok = test_game_has_half_edge();
  else if (strcmp("game_new_empty_ext", argv[1]) == 0)
    ok = test_game_new_empty_ext();
  else if (strcmp("game_new_ext", argv[1]) == 0)
    ok = test_game_new_ext();
  else if (strcmp("game_is_wrapping", argv[1]) == 0)
    ok = test_game_is_wrapping();
  else if (strcmp("game_solve", argv[1]) == 0)
    ok = test_game_solve();
  else if (strcmp("game_nb_solutions", argv[1]) == 0)
    ok = test_game_nb_solutions();
  else {
    fprintf(stderr, "Error: test \"%s\" not found!\n", argv[1]);
    exit(EXIT_FAILURE);
  }

  // print test result
  if (ok) {
    fprintf(stderr, "Test \"%s\" finished: SUCCESS\n", argv[1]);
    return EXIT_SUCCESS;
  } else {
    fprintf(stderr, "Test \"%s\" finished: FAILURE\n", argv[1]);
    return EXIT_FAILURE;
  }
}