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

#ifndef PIECE_H
#define PIECE_H

#include <fstream>
#include <iostream>
#include <vector>
#include <string>

#include "base.h"

struct BlokishPiece
{
public:
  BlokishPiece(BlokishID c = none, std::string name = "", int nRotations = 0, int nFlips = 0)
  {
    mBlokishID        = c;
    mName             = name;
    mnUniqueRotations = nRotations;
    mnSides           = nFlips;
  }

  BlokishPiece(const BlokishPiece &second)
  {
    mvSquares = second.mvSquares;
    mBlokishID = second.mBlokishID;
    mName = second.mName;
    mnSides = second.mnSides;
    mnUniqueRotations = second.mnUniqueRotations;
  }

  virtual ~BlokishPiece()
  {
    mvSquares.clear();
  }

  bool operator==(const BlokishPiece &second)
  {
    if(mName == second.mName)
      return true;
    else
      return false;
  }

  const std::vector<Square>& GetSquares()
  {
    return mvSquares;
  }

  BlokishID GetOwner()
  {
    return mBlokishID;
  }

  virtual void ReflectVertical()
  {
    for(unsigned nSquare = 0; nSquare < mvSquares.size(); nSquare++)
    {
      mvSquares[nSquare].mY *= -1;
    }
  }
  
  virtual void ReflectHorizontal()
  {
    for(unsigned nSquare = 0; nSquare < mvSquares.size(); nSquare++)
    {
      mvSquares[nSquare].mX *= -1;
    }
  }

  virtual void RotateClockwise()
  {
    for(unsigned nSquare = 0; nSquare < mvSquares.size(); nSquare++)
    {
      int x = mvSquares[nSquare].mX;
      int y = mvSquares[nSquare].mY;

      mvSquares[nSquare].mX = y;
      mvSquares[nSquare].mY = x * -1;
    }
  }

  virtual void RotateCounterClockwise()
  {
    for(unsigned nSquare = 0; nSquare < mvSquares.size(); nSquare++)
    {
      int x = mvSquares[nSquare].mX;
      int y = mvSquares[nSquare].mY;

      mvSquares[nSquare].mX = y * -1;
      mvSquares[nSquare].mY = x;
    }
  }

  int Value()
  {
    return int(mvSquares.size());
  }

  void AddSquare(int x, int y)
  {
    mvSquares.push_back(Square(x, y));
  }

  void Write(std::ofstream &out)
  {
    int size = mvSquares.size();
    out.write((char*)&size, sizeof(size));
    out.write((char*)&mBlokishID, sizeof(mBlokishID));
    out.write((char*)&mnUniqueRotations, sizeof(mnUniqueRotations));
    out.write((char*)&mnSides, sizeof(mnSides));
    
    for(int i = 0; i < size; i++)
    {
      out.write((char*)&(mvSquares[i].mX), sizeof(mvSquares[i].mX));
      out.write((char*)&(mvSquares[i].mY), sizeof(mvSquares[i].mY));
    }
    
    size = mName.size();
    out.write((char*)&size, sizeof(size));
    for(int i = 0; i < size; i++)
    {
			char character = mName[i];
      out.write((char*)&character, sizeof(character));
    }
  }

  void Read(std::ifstream &in)
  {
    int size;
    in.read((char*)&size, sizeof(size));
    in.read((char*)&mBlokishID, sizeof(mBlokishID));
    in.read((char*)&mnUniqueRotations, sizeof(mnUniqueRotations));
    in.read((char*)&mnSides, sizeof(mnSides));
    
    for(int i = 0; i < size; i++)
    {
      Square temp(0, 0);
      in.read((char*)&(temp.mX), sizeof(temp.mX));
      in.read((char*)&(temp.mY), sizeof(temp.mY));
      mvSquares.push_back(temp);
    }

    in.read((char*)&size, sizeof(size));
    for(int i = 0; i < size; i++)
    {
			char character;
      in.read((char*)&character, sizeof(character));
			mName += character;
    }
  }

  void Print()
  {
    for(unsigned i = 0; i < mvSquares.size(); i++)
    {
      std::cout << "(" << mvSquares[i].mX << ", " << mvSquares[i].mY << ") ";
    }
    std::cout << "\n" << std::flush;
  }

  int                 mnUniqueRotations;
  int                 mnSides;
  std::string         mName;
  BlokishID            mBlokishID;

protected:
  std::vector<Square> mvSquares;
};

struct SingleSquare:public BlokishPiece
{
public:
  SingleSquare(BlokishID c = none):BlokishPiece(c, "SingleSquare", 1, 1)
  {
    AddSquare(0, 0);
  }
};

struct TwoSquares:public BlokishPiece
{
  TwoSquares(BlokishID c = none):BlokishPiece(c, "TwoSquares", 4, 1)
  {
    AddSquare(0, 0);
    AddSquare(1, 0);
  }
};

struct ThreeSquares:public BlokishPiece
{
  ThreeSquares(BlokishID c = none):BlokishPiece(c, "BlokishPiece", 2, 1)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
  }
};

struct BentThreeSquares:public BlokishPiece
{
  BentThreeSquares(BlokishID c = none):BlokishPiece(c, "BentThreeSquares", 4, 1)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
  }
};

struct FourSquares:public BlokishPiece
{
  FourSquares(BlokishID c = none):BlokishPiece(c, "FourSquares", 4, 1)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
    AddSquare( 2, 0);
  }
};

struct FiveSquares:public BlokishPiece
{
  FiveSquares(BlokishID c = none):BlokishPiece(c, "FiveSquares", 2, 1)
  {
    AddSquare(-2, 0);
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
    AddSquare( 2, 0);
  }
};

struct BentFourSquares:public BlokishPiece
{
  BentFourSquares(BlokishID c = none):BlokishPiece(c, "BentFourSquares", 4, 2)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
    AddSquare( 1, 1);
  }
};

struct LFiveSquares:public BlokishPiece
{
  LFiveSquares(BlokishID c = none):BlokishPiece(c, "LFiveSquares", 4, 1)
  {
    AddSquare(-2, 0);
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 0, 2);
  }
};

struct GunFiveSquares:public BlokishPiece
{
  GunFiveSquares(BlokishID c = none):BlokishPiece(c, "GunFiveSquares", 4, 2)
  {
    AddSquare(-1, 0);
    AddSquare( 0,-1);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
    AddSquare( 1, 1);
  }
};

struct ZeeFiveSquares:public BlokishPiece
{
  ZeeFiveSquares(BlokishID c = none):BlokishPiece(c, "ZeeFiveSquares", 2, 2)
  {
    AddSquare(-1, -1);
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
    AddSquare( 1, 1);
  }
};

struct UFiveSquares:public BlokishPiece
{
  UFiveSquares(BlokishID c = none):BlokishPiece(c, "UFiveSquares", 4, 1)
  {
    AddSquare(-1, 0);
    AddSquare(-1, 1);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
    AddSquare( 1, 1);
  }
};

struct BentFiveSquares:public BlokishPiece
{
  BentFiveSquares(BlokishID c = none):BlokishPiece(c, "BentFiveSquares", 4, 2)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 1, 0);
    AddSquare( 2, 0);
    AddSquare( 2, 1);
  }
};

struct TeeFourSquares:public BlokishPiece
{
  TeeFourSquares(BlokishID c = none):BlokishPiece(c, "TeeFourSquares", 4, 1)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 1, 0);
  }
};

struct TeeFiveSquares:public BlokishPiece
{
  TeeFiveSquares(BlokishID c = none):BlokishPiece(c, "TeeFiveSquares", 4, 1)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 0, 2);
    AddSquare( 1, 0);
  }
};

struct CrossFiveSquares:public BlokishPiece
{
  CrossFiveSquares(BlokishID c = none):BlokishPiece(c, "CrossFiveSquares", 1, 1)
  {
    AddSquare(-1, 0);
    AddSquare( 0,-1);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 1, 0);
  }
};

struct BatonFiveSquares:public BlokishPiece
{
  BatonFiveSquares(BlokishID c = none):BlokishPiece(c, "BatonFiveSquares", 4, 2)
  {
    AddSquare(-1, 0);
    AddSquare(0, 0);
    AddSquare(0, 1);
    AddSquare(1, 0);
    AddSquare(2, 0);
  }
};

struct SquareFourSquares:public BlokishPiece
{
  SquareFourSquares(BlokishID c = none):BlokishPiece(c, "SquareFourSquares", 4, 1)
  {
    AddSquare(0, 0);
    AddSquare(0, 1);
    AddSquare(1, 0);
    AddSquare(1, 1);
  }
};

struct PFiveSquares:public BlokishPiece
{
  PFiveSquares(BlokishID c = none):BlokishPiece(c, "PFiveSquares", 4, 2)
  {
    AddSquare(-1, 0);
    AddSquare(-1, 1);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 1, 1);
  }
};

struct SkewedFourSquares:public BlokishPiece
{
  SkewedFourSquares(BlokishID c = none):BlokishPiece(c, "SkewedFourSquares", 4, 2)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 1, 1);
  }
};

struct SkewedFiveSquares:public BlokishPiece
{
  SkewedFiveSquares(BlokishID c = none):BlokishPiece(c, "SkewedFiveSquares", 4, 2)
  {
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 1, 1);
    AddSquare( 2, 1);
  }
};

struct StairsFiveSquares:public BlokishPiece
{
  StairsFiveSquares(BlokishID c = none):BlokishPiece(c, "StairsFiveSquares", 4, 1)
  {
    AddSquare(-1,-1);
    AddSquare(-1, 0);
    AddSquare( 0, 0);
    AddSquare( 0, 1);
    AddSquare( 1, 1);
  }
};
#endif
