#include "game_ext.h"

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

#include "game_struct.h"

// Declaration of functions present in game_aux.c
void verify_p(void* p, char* msg);
void verify_val_range(uint val, uint min, uint max, char* msg);

game game_new_ext(uint nb_rows, uint nb_cols, shape* shapes,
                  direction* orientations, bool wrapping) {
  // Create an uninitialised game
  game g = malloc(sizeof(struct game_s));
  // Check that g is allocated
  verify_p((void*)g, "Not enough memory !");

  verify_val_range(nb_rows, 1, -1, "Wrong size of matrix (rows).");
  verify_val_range(nb_cols, 1, -1, "Wrong size of matrix (col).");
  g->rows = nb_rows;
  g->cols = nb_cols;
  g->wrapping = wrapping;

  g->shapes = malloc(sizeof(shape) * (g->rows * g->cols));
  g->directions = malloc(sizeof(direction) * (g->rows * g->cols));
  // Check that shapes and orientations are allocated
  verify_p((void*)g->shapes, "Not enough memory !");
  verify_p((void*)g->directions, "Not enough memory !");

  // If  there are shapes or directions set them
  if (shapes) memcpy(g->shapes, shapes, sizeof(shape) * (g->rows * g->cols));
  if (orientations)
    memcpy(g->directions, orientations,
           sizeof(direction) * (g->rows * g->cols));
  if (!shapes || !orientations) {
    // Go around the 2D grid
    for (unsigned int i = 0; i < g->rows; i++) {
      for (unsigned int j = 0; j < g->cols; j++) {
        // If the values of shape or orientation is NULL set it to default
        if (!shapes) {
          g->shapes[i * g->cols + j] = EMPTY;
        }
        if (!orientations) {
          g->directions[i * g->cols + j] = NORTH;
        }
      }
    }
  }
  g->history = malloc(sizeof(struct history_s));
  // Check that history is allocated
  verify_p((void*)g->history, "Not enough memory !");
  g->history->i = 0;
  g->history->j = 0;
  g->history->nb_turns = 0;
  g->history->previous = NULL;
  g->history->next = NULL;
  return g;
}

game game_new_empty_ext(uint nb_rows, uint nb_cols, bool wrapping) {
  return game_new_ext(nb_rows, nb_cols, NULL, NULL, wrapping);
}

uint game_nb_rows(cgame g) {
  // Check that g is a valid pointer
  verify_p((void*)g, "Invalid game pointer for game_nb_rows !");
  verify_val_range(g->rows, 1, -1, "Wrong size of matrix (rows).");
  return g->rows;
}

uint game_nb_cols(cgame g) {
  // Check that g is a valid pointer
  verify_p((void*)g, "Invalid game pointer for game_nb_cols !");
  verify_val_range(g->cols, 1, -1, "Wrong size of matrix (col).");
  return g->cols;
}

bool game_is_wrapping(cgame g) {
  // Check that g is a valid pointer
  verify_p((void*)g, "Invalid game pointer for game_is_wrapping!");
  return g->wrapping;
}

void game_undo(game g) {
  verify_p((void*)g, "Invalid game pointer for game_undo!");
  verify_p((void*)g->history, "Invalid history pointer for game_undo !");
  if (g->history->previous != NULL) {
    direction d = game_get_piece_orientation(g, g->history->i, g->history->j);
    game_set_piece_orientation(g, g->history->i, g->history->j,
                               (d - g->history->nb_turns + NB_DIRS) % NB_DIRS);
    g->history = g->history->previous;
    printf("> The game has been undone\n");
  } else {
    printf("> There is nothing to undo\n");
  }
}

void game_redo(game g) {
  verify_p((void*)g, "Invalid game pointer for game_redo !");
  verify_p((void*)g->history, "Invalid history pointer for game_redo !");
  if (g->history->next != NULL) {
    g->history = g->history->next;
    direction d = game_get_piece_orientation(g, g->history->i, g->history->j);
    game_set_piece_orientation(g, g->history->i, g->history->j,
                               (d + g->history->nb_turns + NB_DIRS) % NB_DIRS);
    printf("> The game has been redone\n");
  } else {
    printf("> There is nothing to redo\n");
  }
}
