/*
  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
*/

/*	Contains all board info and controls all of the board logic: whether a piece can be placed, 
 *	where, etc.
 */


#ifndef BOARD_H
#define BOARD_H

#include <iostream>
#include "base.h"
#include "piece.h"

typedef std::vector<Square>       SquareVector;
typedef std::vector<BlokishPiece*> PieceVector;

class BlokishBoard
{
public:
  struct Move
  {
    BlokishPiece piece;
    int row;
    int col;
    bool firstPlaced;
    int rating;
    int round;

    Move(){}
 
    Move(BlokishPiece &p, int r = 0, int c = 0, int s = 0)
    {
      piece = p;
      row = r;
      col = c;
      rating = s;
    }

    bool operator<(const Move &second) const
    {
      return rating < second.rating;
    }
  };

  BlokishBoard();
  
  //// I/O operations.  Saves to & reads from save file.
  void Write(std::ofstream &out);
  void Read(std::ifstream &in);

  //// Accessors.
  int GetHeight() const
  {
    return mnBoardHeight;
  }

  int GetWidth() const
  {
    return mnBoardWidth;
  }

  float GetRound() const
  {
    return mnRound;
  }

  BlokishID GetSpaceID(int row, int col) const
  {
    if(row < 0 || row >= mnBoardHeight || col < 0 || col >= mnBoardWidth)
      return invalid;
    else
      return maaBoard[row][col];
  };

  int GetNumCorners(BlokishID player, const PieceVector &pieces, int numPieces = 1)
  {
    return FindUsableSpaces(player, pieces, numPieces);
  }
  ////

  // Returns what round a space was filled.
  int GetAge(int row, int col) const
  {
    if(row < 0 || row >= mnBoardHeight || col < 0 || col >= mnBoardWidth)
      return 0;
    else
      return maaAge[row][col];
  }
  // Resets the board.  All pieces are deleted.
  void Reset();

	// Places a piece on the board.  Returns false if it can't be done.
  bool PlacePiece(BlokishPiece *piece, int row, int col);

	// Remove the last piece placed.  Used mainly in the AI class.  Could be used to
  // implement Undo later.
  void UndoPlacePiece();

	// Checks whether or not a piece with a specific rotation and reflection 
  // can be placed at a particular position.
  bool PieceCanBePlaced(BlokishPiece *piece, int row, int col) const;
  
  // Prints out the IDs of the things on the board.
  void Print();

	// Checks if the player is dead by trying out all possible combinations until a valid move is found.
  bool IsDead(BlokishID player, PieceVector &piecesRemaining) const;

	// Checks if the space is usable.  Make sure to call FindUsableSpaces first.
  bool IsUsableSpace(int row, int col) const
  {
    if(row < 0 || row >= mnBoardHeight || col < 0 || col >= mnBoardWidth)
      return none;
    else
      return maaPossible[row][col];
  }

  // Finds all the places on the board where a piece could legally fit.
  // numPieces = how many of the player's pieces to try fitting to the board
  int FindUsableSpaces(BlokishID player, const PieceVector &pieces, int numPieces = 1);

  // Calculates how many spaces the enemy can access.
  int GetNumEnemyCorners(BlokishID player, const std::vector<PieceVector*> &pieces, int numPieces = 1);
  
protected:
  // Checks if a piece can legally touch a corner
  bool PieceTouchesBoardCorner(BlokishPiece *piece, int row, int col) const;

  // Checks if it touches the corner of another piece
  bool PieceTouchesPieceCorner(BlokishPiece *piece, int row, int col) const;

  // Check if it doesn't touch any of this player's other pieces side-wise
  bool PieceDoesNotConflict(BlokishPiece *piece, int row, int col) const;

  // Check if the piece will occupy only currently empty squares
  bool PieceFits(BlokishPiece *piece, int row, int col) const;

  int                 mnBoardWidth;
  int                 mnBoardHeight;

  bool                mbvFirstPlaced[4];
  
  int                 maaAge[20][20];
  bool                maaPossible[20][20];
  BlokishID           maaBoard[20][20];
  int                 mnRound;

  std::vector< Move > mMoveHistory;
};


#endif
