#include <iostream>
#include <string>
#include <GL/glut.h>

#include "CMove.h"
#include "Pieces.h"
#include "CBoard.h"
#include "CException.h"

#define NEWPIECE(piece, x, y, color)  {          \
Board[x][y] = new C##piece##(this, x, y, color); \
if (Board[x][y] == 0)                            \
  throw MEMORY_ERROR;                            \
}


void CBoard::Create()
{
  char i, j;

  for (i = 0; i < 8; i++)
    {
      NEWPIECE(Pawn, i, 1, WHITE);
      NEWPIECE(Pawn, i, 6, BLACK);

      for (j = 2; j < 6; j++)
        {
          Board[i][j] = new CEmpty(this, i, j);
          if (Board[i][j] == 0)
            throw MEMORY_ERROR;
        }
    }

  // White last row
  NEWPIECE(Castle, 0, 0, WHITE);
  NEWPIECE(Knight, 1, 0, WHITE);
  NEWPIECE(Bishop, 2, 0, WHITE);
  NEWPIECE(Queen, 3, 0, WHITE);
  NEWPIECE(King, 4, 0, WHITE);
  NEWPIECE(Bishop, 5, 0, WHITE);
  NEWPIECE(Knight, 6, 0, WHITE);
  NEWPIECE(Castle, 7, 0, WHITE);

  // Black last row
  NEWPIECE(Castle, 0, 7, BLACK);
  NEWPIECE(Knight, 1, 7, BLACK);
  NEWPIECE(Bishop, 2, 7, BLACK);
  NEWPIECE(Queen, 3, 7, BLACK);
  NEWPIECE(King, 4, 7, BLACK);
  NEWPIECE(Bishop, 5, 7, BLACK);
  NEWPIECE(Knight, 6, 7, BLACK);
  NEWPIECE(Castle, 7, 7, BLACK);

  Turn = WHITE;
  Promotion = QUEEN;
  ToDelete = 0;
}



void CBoard::Destroy()
{
  for (int i = 0; i < 8; i++)
    for (int j = 0; j < 8; j++)
      {
        if (Board[i][j] != 0)
          {
            delete Board[i][j];
            Board[i][j] = 0;
          }
      }

  if (ToDelete)
    {
      delete ToDelete;
      ToDelete = 0;
    }
}


CBoard::CBoard()
{
  try
    {
      Create();
    }
  catch (CFatalError Error)
    {
      std::cerr << Error.GetErrorString();
      exit(1);
    }
}


CBoard::CBoard(const CBoard& board)
{
  try
    {
      for (int i = 0; i < 8; i++)
        {
          for (int j = 0; j < 8; j++)
            {
              Board[i][j] = board(i,j)->GetCopy(this);
              if (Board[i][j] == 0)
                throw MEMORY_ERROR;
            }
        }
    }
  catch (CFatalError Error)
    {
      std::cerr << Error.GetErrorString();
      exit(1);
    }

  Turn = board.Turn;
  Promotion = board.Promotion;
  ToDelete = board.ToDelete;
}


CBoard& CBoard::operator = (const CBoard& board)
{
  Destroy();

  try
    {
      for (int i = 0; i < 8; i++)
        {
          for (int j = 0; j < 8; j++)
            {
              Board[i][j] = board(i,j)->GetCopy(this);
              if (Board[i][j] == 0)
                throw MEMORY_ERROR;
            }
        }
    }
  catch (CFatalError Error)
    {
      std::cerr << Error.GetErrorString();
      exit(1);
    }

  Turn = board.Turn;
  Promotion = board.Promotion;
  ToDelete = board.ToDelete;				
  return *this;
}


CBoard::~CBoard()
{
  Destroy();
}


bool CBoard::IsCheck(type_color Col) const
{
  for (int i = 0; i < 8; i++)
    for (int j = 0; j < 8; j++)
      if (Board[i][j]->IsCheck(Col))
        return true;

  return false;
}


void CBoard::Draw(void) const
{
  for (int i = 0; i < 8; i++)
    for (int j = 0; j < 8; j++)
      Board[i][j]->Draw();
}


void CBoard::Move(const CMove& move)
{
  for (char i = 0; i < 8; i++)
    for (char j = 0; j < 8; j++)
      Board[i][j]->MoveAlert(move);

  delete Board[move.GetDestinationX()][move.GetDestinationY()];

  CBasePiece* piece = Board[move.GetSourceX()][move.GetSourceY()];
  piece->SetX(move.GetDestinationX());
  piece->SetY(move.GetDestinationY());
  Board[move.GetDestinationX()][move.GetDestinationY()] = piece;

  Board[move.GetSourceX()][move.GetSourceY()] =
    new CEmpty(this, move.GetSourceX(), move.GetSourceY());

  if (Board[move.GetSourceX()][move.GetSourceY()] == 0)
    {
      std::cerr << MEMORY_ERROR.GetErrorString();
      exit(1);
    }

  if (ToDelete)
    {
      delete ToDelete;
      ToDelete = 0;
    }
}



std::ostream& operator <<(std::ostream& o, const CBoard& board)
{
  char i, j;

  o << "\\board{";

  for (j = 7; j > -1; j--)
    {
      for (i = 0; i < 8; i++)
        {
          o << board(i,j)->GetChar();
        }

      if (j != 0)
        o << "}\n      {";
      else
        o << "}\n";
    }

  return o;
}
