/*
  Copyright (C) 2005 Dan Alcantara (dfalcantara@ucdavis.edu, http://wwwcsif.cs.ucdavis.edu/~alcantar)
  Based on the board game "Blokus" by Educational Insights, Inc.
  
  This program is free software; you can redistribute 
  it and/or modify it under the terms of the GNU General 
  Public License as published by the Free Software Foundation; 
  either version 2 of the License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful, but 
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  more details.
  
  You should have received a copy of the GNU General Public License along 
  with this program; if not, write to the Free Software Foundation, Inc., 
  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  
  Full license visible at http://www.opensource.org/licenses/gpl-license.php
*/

// This file includes the logic required to run the game.

#include "game.h"

void BlokishGame::Write(std::ofstream &out)
{
  std::string blokus_save_game = "blokus_save_game";
  for(unsigned i = 0; i < blokus_save_game.size(); i++)
  {
    out.write((char*) &blokus_save_game[i], sizeof(blokus_save_game[i]));
  }

  mBoard.Write(out);
  
  out.write((char*) &mnCurrentPlayer, sizeof(mnCurrentPlayer));

  for(int i = 0; i < 4; i++)
  {
    bool isDead = mbvPlayerDead[i];
    out.write((char*) &isDead, sizeof(isDead));
  }

	for(int i = 0; i < 4; i++)
	{
    mvPlayers[i].Write(out);
	}
}

bool BlokishGame::Read(std::ifstream &in)
{
  std::string blokus_save_game = "0123456789012345";

  /// Get the unique identifier
  for(int i = 0; i < 16; i++)
  {
    in.read((char*) &blokus_save_game[i], sizeof(blokus_save_game[i]));
  }

  if(blokus_save_game != "blokus_save_game")
  {
    return false;
  }
  ///

  mBoard.Read(in);
  
  in.read((char*) &mnCurrentPlayer, sizeof(mnCurrentPlayer));

  for(int i = 0; i < 4; i++)
  {
    bool isDead;
    in.read((char*) &isDead, sizeof(isDead));
    mbvPlayerDead[i] = isDead;
  }

	for(int i = 0; i < 4; i++)
	{
    mvPlayers[i].Read(in);
	}

  mpSelectedPiece = NULL;
  return true;
}
////

void BlokishGame::Reset()
{
  for(int i = 0; i < 4; i++)
  {
    mvPlayers[i].Reset();
    mvPlayers[i].SetID(BlokishID(i));
    mbvPlayerDead[i] = false;
  }

  mnCurrentPlayer = 0;
  mpSelectedPiece = NULL;
  mBoard.Reset();
  mBoard.FindUsableSpaces(BlokishID(mnCurrentPlayer), GetUnplayedPlayerPieces(BlokishID(mnCurrentPlayer)), 21);
  
  if(mbvComputerControlled[0])
  {
    mOutcome = BeginAIThread;
  }
  else
    mOutcome = GetPlayerInput;
}

// Reset the AI. Set it up for the current player.
BlokishGame::TurnOutcome BlokishGame::ResetAI()
{

  if(IsDead(BlokishID(mnCurrentPlayer), false))
  {
    mpSelectedPiece = NULL;
    return FinishTurn();
  }
  
  std::vector<PieceVector*> pieces;
  pieces.push_back(&(GetUnplayedPlayerPieces(first)));
  pieces.push_back(&(GetUnplayedPlayerPieces(second)));
  pieces.push_back(&(GetUnplayedPlayerPieces(third)));
  pieces.push_back(&(GetUnplayedPlayerPieces(fourth)));
  mAI.Reset(mAIOptions[mnCurrentPlayer], BlokishID(mnCurrentPlayer), pieces);
  mOutcome = WaitForAI;
  return WaitForAI;
}

// Steps the AI.  Returns true if it completed.
bool BlokishGame::TakeAIStep()
{
  AI::StepOutcome outcome = mAI.Step(mChosenMove);
  if(outcome == AI::Complete)
  {
    PlacePiece(&(mChosenMove.piece), mChosenMove.row, mChosenMove.col);
    mpSelectedPiece = &(mChosenMove.piece);
    mOutcome = FinishAIThread;
    return true;
  }
  else if(outcome == AI::Incomplete)
  {
    mBoard.FindUsableSpaces(BlokishID(mnCurrentPlayer), 
                            GetUnplayedPlayerPieces(BlokishID(mnCurrentPlayer)), 
                            21);
    return false;
  }
  else if(outcome == AI::MustReset)
  {
    ResetAI();
    return false;
  }

  return false;
}

// Finishes a turn.  Deletes a piece from a player's reserve, and finds the next live player.
BlokishGame::TurnOutcome BlokishGame::FinishTurn()
{
  if(mpSelectedPiece != NULL)
  {
    // Delete the selected piece from the player's reserve.
    mvPlayers[mnCurrentPlayer].DeletePiece(mpSelectedPiece);
    mpSelectedPiece = NULL;
  }
  else
  { // The player couldn't find a piece to play.  Possibly a bug somewhere else.
    std::cerr << __FILE__ << ", " << __LINE__ << ": No pieces were played.\n";
  }
  
  mBoard.FindUsableSpaces(BlokishID(mnCurrentPlayer), GetUnplayedPlayerPieces(BlokishID(mnCurrentPlayer)), 21);
  IsDead(BlokishID(mnCurrentPlayer), false);

  // Find the next non-dead player
  for(int i = 0; i < 4; i++)
  {
    mnCurrentPlayer = ((mnCurrentPlayer + 1) % 4);
    if(false == IsDead(BlokishID(mnCurrentPlayer), false))
    {
      mBoard.FindUsableSpaces(BlokishID(mnCurrentPlayer), GetUnplayedPlayerPieces(BlokishID(mnCurrentPlayer)), 21);
      if(mbvComputerControlled[mnCurrentPlayer])
      {
        mOutcome = BeginAIThread;
      }
      else
      {
        mOutcome = GetPlayerInput;
      }

      return mOutcome;
    }
  }

  // No live players.  Should end the game.
  mOutcome = GameOver;
  return mOutcome;
}

// Checks whether or not a player is dead.
bool BlokishGame::IsDead(BlokishID player, bool quickly)
{
  if(mbvPlayerDead[player])
    return true;
   
  // If quickly, avoid the check.
  if(quickly)
  {
    return false;
  }

  // Do not call this while the AI is running!
  if(mBoard.IsDead(player, GetUnplayedPlayerPieces(player)))
  {
    mbvPlayerDead[player] = true;
    return true;
  }

  return false;
}
