/*! \file    Domineering.c
**  \author  Matthew Mead
**  \par     email: mmead\@digipen.edu
**  \date    2/3/2013
**  \brief   Implementation of Domineering rule set
*
* See:
*  http://en.wikipedia.org/wiki/Domineering
*  http://www.jonhurtado.com/?p=25
*******************************************************************************/
#include <string.h> /* strlen, strcpy, strchr */
#include <stdlib.h> /* malloc, calloc, free   */
#include <stdio.h>  /* printf                 */

#include "PluginCommon.h"
#include "PluginInterface.h"

#include "Domineering.h"

/*****************************************************************************/
/* Private                                                                   */
/*****************************************************************************/
/*****************************************************************************/

typedef int bool;
#define true 1
#define false 0
#define DEBUG_PLUGINx

static char Symbols[] = {'0', '-', '|', 'X'};


#ifdef DEBUG_PLUGIN
static void dump_board(struct Game *game) 
{
  unsigned row, col;
  for (row = 0; row < game->rows; row++)
  {
    for (col = 0; col < game->cols; col++)
    {
      int index1 = row * game->cols + col;
      int index2 = game->board[index1];
      char c = Symbols[index2];
      if (c)
        printf("%c", c);
      else
        printf(".");
    }
    printf("\n");
  }
  printf("\n");
}
#endif

/*
 * 
 * Checks to see if the move (row,col) for the player is a valid move.
 * 
 */
static bool is_legal_move(struct Game* game, int player, unsigned row, unsigned column)
{
    /* The move is off of the board */
  if (row >= game->rows || column >= game->cols)
    return false;
    
    /* The cell position is already occupied */
  if (game->board[row * game->cols + column])
    return false;

  #ifdef DEBUG_PLUGIN
    printf("***PLUGIN: A: 1 is %i, 2 is %i\n", game->player1_dir, game->player2_dir);
    printf("***PLUGIN: gameboard at %i,%i is %c (%i)\n", row, column, game->board[row * game->cols + column], game->board[row * game->cols + column]);
    printf("***PLUGIN: gameboard at %i,%i is %c (%i)\n", row + 1, column, game->board[row * game->cols + column + 1], game->board[row * game->cols + column + 1]);
  #endif
    
    /* Now look to see if the move extends into an occupied cell */
  if (player == 1)
  {
      /* player 1 is horizontal */
    if (game->player1_dir == ddHORIZONTAL) 
    {
      if (column + 1 >= game->cols)
        return false;
      if (game->board[row * game->cols + column + 1])
        return false;
    }
    else /* player 1 is vertical */
    {
      if (row + 1 >= game->rows)
        return false;
      if (game->board[(row + 1) * game->cols + column])
        return false;
    }
  }
  else /* player 2 */
  {
      /* player 2 is vetical */
    if (game->player2_dir == doVERTICAL)
    {
      if (row + 1 >= game->rows)
        return false;
      if (game->board[(row + 1) * game->cols + column])
        return false;
    }
    else /* player 2 is horizontal */
    {
      if (column + 1 >= game->cols)
        return false;
      if (game->board[row * game->cols + column + 1])
        return false;
    }
  }
  
    /* If we get this far, it's a valid move */
  return true;
}

/*
 * Check if this player can move. If this player can't move, then the
 * other player has won the game.
 * 
 */
static bool player_can_move(struct Game *game, int player)
{ 
    /* Try every spot to see if at least one is valid */
  unsigned i, j;
  for (i = 0; i < game->rows; i++)
    for (j = 0; j < game->cols; j++)
      if (is_legal_move(game, player, i, j))
      {
        #ifdef DEBUG_PLUGIN
          printf("***PLUGIN: Player %i can move to %i,%i.\n", player, i, j);
          fflush(stdout);
        #endif
        return true;
      }
        
  #ifdef DEBUG_PLUGIN
    printf("***PLUGIN: Player %i can't move.\n", player);
  #endif
  return false;
}

/*
 * Given a NULL-terminated string of cell positions, mark them as occupied. 
 * The string will look something like this:
 * 
 *   "r.c,r.c,r.c,r.c..."
 * 
 * where r.c is row.column. For example:
 * 
 *   "0.0,0.1,1.2.1.3"
 * 
 * TODO: Add more error checking for invalid strings.
 */
static bool mark_off_cells(struct Game *game, const char *string)
{
  char *buffer = (char *)malloc(strlen(string) + 1);
  char *delim = ",";
  char *token;
  
  strcpy(buffer, string);
  #ifdef DEBUG_PLUGIN
    printf("***PLUGIN: mark off |%s|\n", buffer);
  #endif
  
    /* First token */
  token = strtok(buffer, delim);
  while (token)
  {
    int row, col;
    char *pt;
    strcpy(buffer, token);
    
      /* Look for the . separator */
    pt = strchr(buffer, '.');
    
      /* Sanity check */
    if (!pt)
      continue;
      
      /* Separate the row and column and extract each one */
    *pt = 0;
    row = atoi(buffer);
    col = atoi(pt + 1);
    *pt = *delim;
    
    #ifdef DEBUG_PLUGIN
      printf("***PLUGIN: |%s| (%i,%i)\n", token, row, col);
    #endif

      /* Update the board with the "dummy" piece */
    game->board[row * game->cols + col] = Symbols[3];
    
    /* Next token */
    token = strtok(NULL, delim);
  }
  free(buffer);
  return false;
}

/*****************************************************************************/
/* Interface                                                                 */
/*****************************************************************************/
/*****************************************************************************/

int Init(void)
{
  /* Nothing to do, init is done in CreateGame */
  return 0;
}

void Shutdown(void)
{
}

const char* Name(void)
{
  return "Domineering";
}

const char* Author(void)
{
  return "Matthew Mead (mmead@digipen.edu)";
}

const char* Type(void)
{
  return "Domineering";
}

const char* Version(void)
{
  return "0.3";
}

const char* APIVersion(void)
{
  return "0.1";
}

int NumberOfPlayers(void)
{
  return 2;
}

void* CreateGame(void)
{
  struct Game* newGame = calloc(1, sizeof(struct Game));
  if(newGame)
    newGame->status = PLUGIN_GAME_STATUS_CREATED;
  return newGame;
}

/*
 * To specify which player is horizontal
 * ORH:1 (player 1) or ORH:2 (player 2) 
 * 
 * To specify dimension of the board
 * DIM:RxC (R is rows, C is columns, e.g. DIM:4x8) 
 * 
 * To remove or mark off cells as in use
 * MRK:r.c,r.c,... (r is row, c is column) 
 *                  example: MRK:0.0,0.1,0.2,1.1,1.2,2.3
 *                   
 */
int SetGameOption(void* game, const char* option)
{
  struct Game* igame = (struct Game*)game;

  if (!option)
    return 0;
    
  if (!igame)
    return 0;
  
    /* Sanity check */
  if ((strlen(option) < 4) || (option[3] != ':'))
    return 0;
    
    /* Horizontal orientation, e.g. ORH:1 */
  if (!strncmp(option, "ORH", 3))   
  {
    if (option[4] == '1')
    {
      igame->player1_dir = ddHORIZONTAL;
      igame->player2_dir = doVERTICAL;
      #ifdef DEBUG_PLUGIN
        printf("***PLUGIN: Player 1 is moving horizontal\n");
      #endif
    }
    else
    {
      igame->player1_dir = doVERTICAL;
      igame->player2_dir = ddHORIZONTAL;
      #ifdef DEBUG_PLUGIN
        printf("***PLUGIN: Player 1 is moving vertical\n");
      #endif
    }
  }
  else if (!strncmp(option, "DIM", 3))
  {
    char *buffer, *x;
    
    if (!strchr(option, 'x'))
      return 0;
      
    buffer = (char *)malloc(strlen(option) + 1);
    strcpy(buffer, option);
    
    x = strchr(option, 'x');
    *x = 0;
    
    igame->rows = atoi(option + 4);
    igame->cols = atoi(x + 1);
    
    igame->board = (char *)calloc(igame->rows * igame->cols, sizeof(char));
    
    free(buffer);
    #ifdef DEBUG_PLUGIN
      printf("***PLUGIN: SetGameOption: %i x %i\n", igame->rows, igame->cols);
      dump_board(igame);
    #endif
  }
  else if (!strncmp(option, "MRK", 3))
  {
    if (!igame->board)
    {
      printf("***PLUGIN: Can't MRK because game->board is NULL\n");
      return 0;
    }
    mark_off_cells(igame, option + 4);
  }

  return 1;
}

int AddPlayer(void* igame, void* player)
{
  struct Game* game = igame;
  
  if(game->players[0] == NULL)
    game->players[0] = player;
  else if(game->players[1] == NULL)
    game->players[1] = player;
  else
    return 1;
  
  return 0;
}

int RemovePlayer(void* igame, void* player)
{
  struct Game* game = igame;
  
  if(game->players[0] == player)
    game->players[0] = NULL;
  else if(game->players[1] == player)
    game->players[1] = NULL;
  else
    return 1;
  
  if(game->status == PLUGIN_GAME_STATUS_PLAYING)
    game->status = PLUGIN_GAME_STATUS_PAUSED;
    
  return 0;
}

int PlayerCount(void* igame)
{
  struct Game* game = igame;
  return (game->players[0] != 0) + (game->players[1] != 0);
}

void* GetPlayer(void* igame, int playerNumber)
{
  struct Game* game = igame;
    
  if(playerNumber == 1 || playerNumber == 2)
    return game->players[playerNumber-1];
  
  return NULL;
}

int GetPlayerNumber(void* igame, void* player)
{
  struct Game* game = igame;
  
  if(game->players[0] == player)
    return 1;
  
  if(game->players[1] == player)
    return 2;
  
  return 0;
}

int StartGame(void* igame)
{
  struct Game* game = igame;
  
  if(PlayerCount(game) != 2)
    return 1;
  
  game->nextPlayer = 1;
  game->status = PLUGIN_GAME_STATUS_PLAYING;
  
  return 0;
}

void* GetNextPlayer(void* igame)
{
  struct Game* game = igame;
  
  if(game->nextPlayer > 0)
    return game->players[game->nextPlayer-1];
  
  return NULL;
}

/*
 * The real workhorse of the interface.
 * 
 * Given a player and a move, validate it. If the move is valid, the function
 * also checks to see if the next player has a move. If the next player has
 * no move, the game is over.
 * 
 */
int ValidateMove(void* igame, void* iplayer, const char* move)
{
  struct Game* game = igame;
  int player = game->players[0] == iplayer ? 1 : 2;
  int index = atoi(move);
  int row = index / game->cols;
  int col = index % game->cols;
  bool legal;

    /* quick sanity check */  
  if(move == NULL || strlen(move) < 1 || game->status != PLUGIN_GAME_STATUS_PLAYING)
    return -1;
    
  #ifdef DEBUG_PLUGIN
    printf("***PLUGIN: ValidateMove board:\n");
    printf("***PLUGIN: Move %i is %i,%i\n", index, row, col);
    fflush(stdout);
    dump_board(game);
  #endif

    /* verify move */
  legal = is_legal_move(game, player, row, col);
  if (!legal)
  {
    #ifdef DEBUG_PLUGIN
      printf("***PLUGIN: Not legal\n");
    #endif
    return -1;
  }
  
    /* play the move */
    /* Player 1 */
  if(iplayer == game->players[0])
  {
      /* Player 1 is horizontal */
    if (game->player1_dir == ddHORIZONTAL)
    {
      game->board[row * game->cols + col] = '1';
      game->board[row * game->cols + col + 1] = '1';
      game->nextPlayer = 2;
    }
    else /* Player 1 vertical */
    {
      game->board[row * game->cols + col] = '1';
      game->board[(row + 1) * game->cols + col] = '1';
      game->nextPlayer = 2;
    }
  }
  else /* Player 2 */
  {
      /* Player 2 is vertical */
    if (game->player2_dir == doVERTICAL)
    {
      game->board[row * game->cols + col] = '2';
      game->board[(row + 1) * game->cols + col] = '2';
      game->nextPlayer = 1;
    }
    else
    {
      game->board[row * game->cols + col] = '2';
      game->board[row * game->cols + col + 1] = '2';
      game->nextPlayer = 1;
    }
  }
  
  #ifdef DEBUG_PLUGIN
    dump_board(game);
    printf("***PLUGIN: Calling player_can_move\n");
  #endif

    /* Check if this move just won the game */
  if (!player_can_move(game, game->nextPlayer))
  {
    game->nextPlayer = GetPlayerNumber(igame, iplayer);
    game->status = PLUGIN_GAME_STATUS_OVER_WINNER;
  }
  
  return 0;
}

int GetGameStatus(void* igame)
{
  struct Game* game = igame;
  return game->status;
}




void DestroyGame(void* igame)
{
  struct Game* game = igame;

  free(game->board);
  free(game);
}