/***************************************************************************
 *   Copyright (C) 2009 by Stefan Bühler                                   *
 *   stbuehler@web.de                                                      *
 *                                                                         *
 *   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.             *
 ***************************************************************************/
#ifndef TORUSCHESSTORUSCHESS_H
#define TORUSCHESSTORUSCHESS_H

#include <QObject>
#include <QList>
#include <QThread>
#include <QMetaType>

/**
	@author Stefan Bühler <stbuehler@web.de>
*/
namespace toruschess {

	class Pos;
	class Field;
	class Game;
	class GameAIThread;
	
	typedef enum { BLACK = -1, NOPLAYER = 0, WHITE = 1 } Player;
	typedef enum { NOPIECE = 0, PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 } Piece;
	typedef enum { TURN_WHITE, TURN_BLACK, WON_WHITE, WON_BLACK, DRAW } GameState;
	typedef enum { HUMAN_HUMAN, HUMAN_COMPUTER, COMPUTER_HUMAN, COMPUTER_COMPUTER } GameMode;

	inline Player place2player(int place) {
		return (place < 0) ? BLACK : (place > 0) ? WHITE : NOPLAYER;
	}

	inline Piece place2piece(int place) {
		return (Piece) qAbs(place);
	}

	QString state2string(GameState state);

	class Pos {
	public:
		Pos() : m_x(0), m_y(0) { }
		Pos(int x, int y) : m_x(x & 7), m_y(y & 7) { }
		
		int x() const { return m_x; }
		int y() const { return m_y; }
		
		bool operator==(const Pos &other) const {
			return m_x == other.m_x && m_y == other.m_y;
		}
		
	private:
		int m_x, m_y;
	};

	class Move {
	public:
		Move();
		Move(const Field* field, const Pos &from, const Pos &to);
		
		const Pos& from() const { return m_from; }
		const Pos& to() const { return m_to; }
		int prevFrom() const { return m_prevFrom; }
		int prevTo() const { return m_prevTo; }
		Player player() const { return m_player; }
		
		bool operator==(const Move &other) const {
			return m_from == other.m_from
			    && m_to == other.m_to
			    && m_prevFrom == other.m_prevFrom
			    && m_prevTo == other.m_prevTo;
			/* player is included in m_prevFrom */
		}
		
		bool valid() const { return m_prevFrom != 0; }
		
	private:
		Pos m_from,m_to;
		int m_prevFrom, m_prevTo;
		Player m_player;
	};

	class Field {
	public:
		Field();
		
		void reset();
		
		Player player(const Pos &p) const { return place2player(place(p)); }
		Piece piece(const Pos &p) const { return place2piece(place(p)); }
		int place(const Pos &p) const { return m_places[p.y()][p.x()]; }
		int& place(const Pos &p) { return m_places[p.y()][p.x()]; }
		
		QList<Move> validMoves(const Pos &from) const;
		bool validMove(const Move &m) const; /* does not check for inCheck */
		bool move(const Move &m);
		bool undo(const Move &m);
		
		bool inCheck(Player player, const Pos &pking) const;
		bool inCheck(Player player) const;
		
		void move_unchecked(const Move &m);
		void undo_unchecked(const Move &m);
	private:
		QList<Move> simpleValidMoves(const Pos &from) const;
		
		Pos m_wking, m_bking;
		int m_places[8][8];
	};

	class Game : public QObject {
	Q_OBJECT
	public:
		Game(QObject *parent = 0);
		virtual ~Game();
		
		const Field* field() const { return m_field; }
		GameState state() const { return m_state; }
		QList<Move> moves() const { return m_moves; }
		
		bool move(const Move &m);
		void restart();
		
		QList<Move> possibleMoves() const;
		
		GameMode gameMode() const { return m_gameMode; }
		void setGameMode(GameMode gm);
		
		int aiStrength() const { return m_aiStrength; }
		void setAiStrength(int s);
		
	signals:
		void moved(Move m);
		void updated();
		void undone(Move m);
		void changed(GameState state);
		void started();
		
		void changedMode(GameMode mode);
		void changedAIStrength(int aiStrength);
		
	private slots:
		void finishedAI(Move m);
		
	private:
		
		void checkAI();
		
		Field *m_field;
		GameState m_state;
		GameMode m_gameMode;
		int m_aiStrength;
		
		QList<Move> m_moves;
		
		GameAIThread *m_aiThread;
		bool m_ignoreAI;
	};
	
	class GameAIThread : public QThread {
	Q_OBJECT
	public:
		GameAIThread(const Field &field, Player curp, int depth, QObject *parent = 0);
		
		void run();
	
	signals:
		void aiFinished(Move m);
		
	private:
		Field m_field;
		Player m_curp;
		int m_depth;
	};
	
}

Q_DECLARE_METATYPE(toruschess::Move)
Q_DECLARE_METATYPE(toruschess::Pos)

#endif
