///////////////////////////////////////////////////////////////////////////
//
// File: referee.cc
//
// Purpose: Implementation of Referee class.
//
// Authors:
//   txe  Travis Emmitt
//
// Modifications:
//   14-APR-1998  txe  Initial creation
//   15-APR-1998  txe  Added QuitGame(), comments
//   16-APR-1998  txe  Calls players' Feedback() methods after each turn
//   19-APR-1998  txe  Replaced cout with DEBUG()
//   21-APR-1998  txe  Added Feedback(), NewGame(), NewMatch()
//   22-APR-1998  txe  Debugging, added random goes_first
//   23-APR-1998  txe  Using static debug, changed constructor
//   24-APR-1998  txe  Removed ability
//   05-APR-1998  txe  Removed QuitGame (no longer used)
//
///////////////////////////////////////////////////////////////////////////

#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "board.h"
#include "player.h"
#include "referee.h"

///////////////////////////////////////////////////////////////////////////

Referee::Referee (char *name)
       : Player  (name, 0) {

  quit        = 0;
  clean_board = NULL;
}

////////////////////////////////////////////////////////////////////////////

void Referee::Feedback (int winner) {
  int i;

  if (winner) {
    for (i = 1; i <= num_players; i++) {
      players[i]->Feedback (i == winner ? WIN : LOSE);
    }
    num_losses++;     // ref "loses" if a player won
  }
  
  else {
    for (i = 1; i <= num_players; i++) {
      players[i]->Feedback (DRAW);
    }
    num_wins++;       // ref "wins" if there's a draw
  }
}

////////////////////////////////////////////////////////////////////////////

int Referee::GetMove () {
  ASSERT (board != NULL);
  return board->MovesLeft ();
}

////////////////////////////////////////////////////////////////////////////

int Referee::Move (int x, int y, int color) {
  ASSERT (board != NULL);

  if (x < 0 || x >= size_x || y < 0 || y >= size_y) {
    DEBUG(2) << "Move (" << x << "," << y << ") failed; coord out of bounds\n";
    return 0;
  }

  if (board->GetColor (x, y) != EMPTY) {
    DEBUG(2) << "Move (" << x << "," << y << ") failed; coord not empty\n";
    return 0;
  }

  board->SetColor (x, y, color);
  return 1;
}

/////////////////////////////////////////////////////////////////////////

int Referee::NewGame () {
  ASSERT (board       != NULL);
  ASSERT (clean_board != NULL);

  int i, max_turns = (size_x * size_y / num_players) * 5, winner = 0;

  game++;
  first = 1;

  DEBUG(1) << "\n\nGame #" << game << " (max_turns = " << max_turns << ")\n\n";

  board->Copy (clean_board);

  for (i = 1; i <= num_players; i++) {
    ASSERT (players[i] != NULL);
    players[i]->NewGame ();
  }

  // randomly decide who goes first //

  if (num_players > 1) {
	first = random (num_players) + 1;
    ASSERT (players[first] != NULL);
    DEBUG(2) << "\nCoin toss: " << players[first]->name << " gets to go first\n";
  }

  // play game until either there's a winner, someone quiet, or max_turns //

  for (turn = 1; turn <= max_turns && board->MovesLeft() && !winner; turn++) {
    if ((winner = TakeTurn ()) == QUIT) {
      DEBUG(0) << name << " is quitting\n";
      return QUIT;
    }
  }

  DEBUG(1) << "\nFinal board state (after " << i << " turns):\n";
  board->Print (1);

  Feedback (winner);
  return winner;
}

////////////////////////////////////////////////////////////////////////////

int Referee::NewMatch () {
  game = 0;
  turn = 0;
  match++;

  DEBUG(2) << name << " starting new match...\n\n";

  for (int i = 1; i <= num_players; i++) {
    ASSERT (players[i] != NULL);
    players[i]->NewMatch ();
  }
  return 1;
}

////////////////////////////////////////////////////////////////////////////

void Referee::ResetScores () {
  num_draws  = 0;
  num_losses = 0;
  num_wins   = 0;

  for (int i = 1; i <= num_players; i++) {
    ASSERT (players[i] != NULL);
    players[i]->ResetScores ();
  }
}

////////////////////////////////////////////////////////////////////////////

void Referee::ShowPlayers (Player **players) {
  ASSERT (players != NULL);
  DEBUG(3) << name << " showing players...\n";

  ref = (Referee *) (this->players[0] = players[0]);

  int i;
  for (i = 1; i < MAX_PLAYERS && players[i] != NULL; i++) {
    this->players[i] = players[i];
    this->players[i]->ShowPlayers (players);
  }

  this->players[i] = NULL;
  num_players = i - 1;            // don't include referee in count of players
}

////////////////////////////////////////////////////////////////////////////

void Referee::ShowRules (Board *board, Board **win_boards) {
  ASSERT (board      != NULL);
  ASSERT (win_boards != NULL);

  DEBUG(3) << name << " showing rules...\n";

  this->board  = board;
  this->size_x = board->GetSizeX ();
  this->size_y = board->GetSizeY ();

  if ((clean_board = new Board ("Clean", size_x, size_y)) == NULL) {
    ERR << name << " couldn't create clean_board; out of memory!\n";
    exit (-1);
  }

  clean_board->Copy (board);

  int i;
  for (i = 0; i < MAX_WIN_BOARDS && win_boards[i] != NULL; i++) {
    this->win_boards[i] = win_boards[i];
  }

  this->win_boards[i] = NULL;
  num_win_boards = i;

  for (i = 1; i <= num_players; i++) {
   ASSERT (players[i] != NULL);
   players[i]->ShowRules (board, win_boards);
  }
}

////////////////////////////////////////////////////////////////////////////

int Referee::TakeTurn () {
  ASSERT (board      != NULL);
  ASSERT (win_boards != NULL);

  int i, j, temp = 0;

  DEBUG(2) << "\n>>> Turn #" << turn << ":\n";

  for (i = 0; i < num_players && board->MovesLeft(); i++) {
    j = (i + first - 1) % num_players + 1;

    if ((temp = players[j]->TakeTurn()) < 1) {
      if (temp == QUIT) {
	DEBUG(2) << "\n" << players[j]->GetName() << " quit\n";
	return temp;
      }
      if (temp == PASS) {
	DEBUG(2) << "\n" << players[j]->GetName() << " passed\n";
      }
    }
    
    board->Print (2);

    if ((temp = board->GetWinner (win_boards)) != EMPTY) {
      DEBUG(2) << "\n" << players[temp]->GetName() << " won!\n";
      return temp;
    }
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////

