/*--------------------------------------------------------------------------
    Pawny 0.3, chess engine (source code).
    Copyright (C) 2009 - 2011 by Mincho Georgiev.
    
    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 3 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, see <http://www.gnu.org/licenses/>.
    
    contact: pawnychess@gmail.com 
    web: http://www.pawny.netii.net/
----------------------------------------------------------------------------*/

#include "data.h"
#include "bits.h"

bool move_undo()
{
	int color,hply;
	sq_t from,to;
	uint8 type,p;
	state_t *state = &board->state;
	
	if(!board->hply) return false;
	board->hply--; board->ply--;
	board->side ^= 1; board->xside ^= 1;
	hply = board->hply;
	from = hs[hply].m.to;
	to = hs[hply].m.from;
	color = board->side;
	p = hs[hply].moved;
	type = hs[hply].m.type;
	
	if(type & CASTLE)
	{	if(type & WHITE_OO)
		{	PLM(F1, H1);
			square[H1] = WR;
			square[F1] = EMPTY_SQUARE;
		}
		if(type & WHITE_OOO)
		{	PLM(D1, A1);
			square[A1] = WR;
			square[D1] = EMPTY_SQUARE;
		}
		if(type & BLACK_OO)
		{	PLM(F8, H8);
			square[H8] = BR;
			square[F8] = EMPTY_SQUARE;
		}
		if(type & BLACK_OOO)
		{	PLM(D8, A8);
			square[A8] = BR;
			square[D8] = EMPTY_SQUARE;
		}
	}
	if(type & PROM)
	{	PLR(from);
		PLI(p,to);
		state->material -= pval[hs[hply].m.promoted];
		state->material_value[color] -= pval[hs[hply].m.promoted];
		state->piece_count[color]--;
		state->pawns++;
		Pawns(color)++;
		state->pawn_value[color] += P_VALUE;
		state->presence[color][GetType(hs[hply].m.promoted)]--;
		bitset(&board->bb_pawns[color], rsz[to]);
	}
	else PLM(from, to);
	square[to] = p;
	square[from] = EMPTY_SQUARE;

	if(GetType(p) == PAWN)
	{	bitclear(&board->bb_pawns[color],rsz[from]);
		bitset(&board->bb_pawns[color],rsz[to]);
	}
	
	if(type & CAP)
	{	if(type & EP)
		{	if(color == WHITE) from -= 0x10;
			if(color == BLACK) from += 0x10;
		}
		PLI(hs[hply].captured,from);
		square[from] = hs[hply].captured;
		
		if(GetType(hs[hply].captured) == PAWN)
		{	state->pawns++;
			Pawns(color^1)++;
			state->pawn_value[color ^ 1] += P_VALUE;
			bitset(&board->bb_pawns[color^1],rsz[from]);
		}
		else
		{	state->material += pval[hs[hply].captured];
			state->material_value[color ^ 1] += pval[hs[hply].captured];
			state->piece_count[color ^ 1]++;
			state->presence[color ^ 1][GetType(hs[hply].captured)]++;
		}
	}
	board->en_passant = hs[hply].old_ep;
	if(GetType(p) != PAWN && (!hs[hply].captured)) board->mr50 --;
	if(hs[hply].flags) board->castle = hs[hply].flags;
	board->fullmove -= board->side; 
	board->hash = hs[hply].hash;
	board->phash = hs[hply].phash;
	return true;
}


bool move_make(move_t move)
{
	int color,hply,flags;
	uint64 hashkey,pawnkey;
	uint8 p,cap = 0;
	sq_t from, to, e_p_capture = 0;
	state_t *state = &board->state;
	
	hply = board->hply;
	from = move.from;
	to = move.to;
	color = board->side;
	p = PieceType(from);
	hashkey = board->hash;
	pawnkey = board->phash;
	flags = 0;

	if(move.type & CASTLE)
	{	if(move.type & WHITE_OO)
		{	if(is_in_check(WHITE) 
			|| is_sq_attacked(F1, BLACK)
			|| is_sq_attacked(G1, BLACK))
				return false;
			PLM(H1, F1);
			square[F1] = WR;
			square[H1] = EMPTY_SQUARE;
			hashkey ^= zobrist_psq[WHITE_ROOK][H1];
			hashkey ^= zobrist_psq[WHITE_ROOK][F1];
		}
		if(move.type & WHITE_OOO)
		{	if(is_in_check(WHITE) 
			|| is_sq_attacked(D1, BLACK)
			|| is_sq_attacked(C1, BLACK))
				return false;
			PLM(A1, D1);
			square[D1] = WR;
			square[A1] = EMPTY_SQUARE;
			hashkey ^= zobrist_psq[WHITE_ROOK][A1];
			hashkey ^= zobrist_psq[WHITE_ROOK][D1];
		}
		if(move.type & BLACK_OO)
		{	if(is_in_check(BLACK) 
			|| is_sq_attacked(F8, WHITE)
			|| is_sq_attacked(G8, WHITE))
				return false;
			PLM(H8, F8);
			square[F8] = BR;
			square[H8] = EMPTY_SQUARE;
			hashkey ^= zobrist_psq[BLACK_ROOK][H8];
			hashkey ^= zobrist_psq[BLACK_ROOK][F8];
		}
		if(move.type & BLACK_OOO)
		{	if(is_in_check(BLACK) 
			|| is_sq_attacked(D8, WHITE)
			|| is_sq_attacked(C8, WHITE))
				return false;
			PLM(A8, D8);
			square[D8] = BR;
			square[A8] = EMPTY_SQUARE;
			hashkey ^= zobrist_psq[BLACK_ROOK][A8];
			hashkey ^= zobrist_psq[BLACK_ROOK][D8];
		}
	}
	if(move.type & CAP)
	{	if(move.type & EP)
		{	if(color == WHITE) e_p_capture = to - 0x10;
			if(color == BLACK) e_p_capture = to + 0x10;
			to = e_p_capture;
		}
		cap = PieceType(to);
		
		if(GetType(cap) == PAWN)
		{	pawnkey ^= zobrist_psq[cap][to];
			state->pawns--;
			state->pawn_value[color ^ 1] -= P_VALUE;
			Pawns(color ^ 1)--;
			bitclear(&board->bb_pawns[color^1], rsz[to]);
			
		}
		else
		{	state->material -= pval[cap];
			state->material_value[color ^ 1] -= pval[cap];
			state->piece_count[color ^ 1]--;
			state->presence[color ^ 1][GetType(cap)]--;
		}
		hashkey ^= zobrist_psq[cap][to];
		PLR(to);
		square[to] = EMPTY_SQUARE;
		to = move.to;
	}
	PLM(from, to);
	square[to] = p;
	square[from] = EMPTY_SQUARE;
	hashkey ^= zobrist_psq[p][from];
	hashkey ^= zobrist_ep[board->en_passant];	
	hs[hply].old_ep = board->en_passant;
	board->en_passant = 0; 
	if((p == WHITE_PAWN) && ((to - from) == 32)) board->en_passant = to - 16;
	if((p == BLACK_PAWN) && ((to - from) == -32))board->en_passant = to + 16;
	hashkey ^= zobrist_ep[board->en_passant];
	if(move.promoted)
	{	PLR(to);
		PLI(move.promoted, to);
		square[to] = move.promoted;
		hashkey ^= zobrist_psq[move.promoted][to];
		pawnkey ^= zobrist_psq[p][from];
		state->pawns--;
		state->pawn_value[color] -= P_VALUE;
		Pawns(color)--;
		state->material += pval[move.promoted];
		state->material_value[color] += pval[move.promoted];
		state->piece_count[color]++;
		state->presence[color][GetType(move.promoted)]++;
		bitclear(&board->bb_pawns[color], rsz[from]);
	}
	else
	{	hashkey ^= zobrist_psq[p][to];
		if(GetType(p) == PAWN)
		{	pawnkey ^= zobrist_psq[p][from];
			pawnkey ^= zobrist_psq[p][to];
			bitclear(&board->bb_pawns[color], rsz[from]);
			bitset(&board->bb_pawns[color], rsz[to]);
		}
	}
		
	if(GetType(p) != PAWN && (!cap)) board->mr50 ++;
	else board->mr50 = 0;
	if(move.type & CASTLE)
	{	flags = board->castle;
		board->castle &= ~((WHITE_OO | WHITE_OOO) << (color * 2));
	}
	else
	{	if((GetType(p) == KING) && (board->castle != 0))
		{	flags = board->castle;
			board->castle &= ~((WHITE_OO | WHITE_OOO) << (color * 2)) ;
		}
		if((GetType(p) == ROOK) && (board->castle != 0))
		{	if((calc_rank(from)) == (RANK_8 * color))
			{	switch(calc_file(from))
				{	case FILE_A:
					flags = board->castle;
					board->castle &= ~((WHITE_OOO) << (color * 2));
					break;
					case FILE_H:
					flags = board->castle;
					board->castle &= ~((WHITE_OO) << (color * 2));
					break;
				}
			}
		}
	}
	board->fullmove += board->side;
	hs[hply].m.p = move.p;
	hs[hply].moved = p;
	hs[hply].flags = (uint8)flags;
	hs[hply].hash = board->hash;
	hs[hply].phash = board->phash;
	hs[hply].captured = cap;
	board->ply++;board->hply++;
	board->side ^= 1;board->xside ^= 1;
	if(flags)
	{	hashkey ^= zobrist_castle[flags];
		hashkey ^= zobrist_castle[board->castle];
	}
	hashkey ^= zobrist_side[board->xside];
	hashkey ^= zobrist_side[board->side];

	board->hash = hashkey;
	board->phash = pawnkey;
	if(is_in_check(board->xside))
	{
		move_undo();
		return false;
	}
	return true;
}

void move_make_null(nullstack_t *ns)
{
	ns->side = board->side;
	ns->xside = board->xside;
	ns->fifty = board->mr50;
	ns->flags = board->castle;
	ns->old_ep = board->en_passant;
	ns->hash = board->hash;
	board->en_passant = 0;
	board->mr50 = 0;
	board->side ^= 1;
	board->xside ^= 1;
	board->hash ^= zobrist_side[ns->side];
	board->hash ^= zobrist_side[ns->xside];
	board->hash ^= zobrist_ep[ns->old_ep];
	board->ply++;
}

void move_undo_null(nullstack_t *ns)
{
	board->side = ns->side;
	board->xside = ns->xside;
	board->mr50 = (sint8)ns->fifty;
	board->castle = ns->flags;
	board->en_passant = ns->old_ep;
	board->hash = ns->hash;
	board->ply--;
}

bool make_if_legal(move_t m)
{	
	int i;
	int move_count; 
	bool found = 0; 
	int index = 0; 
	move_t ms[MOVE_STACK];
	
	move_count = move_gen(&ms[0]);
	for(i = 0; i < move_count; i++)
	{	//.type is omitted since this is a
		//direct input (whithout type).
		if(m.from == ms[i].from
		&& m.to == ms[i].to
		&& m.promoted == ms[i].promoted)
		{	found = true;
			index = i;
			break;
		}
	}
	if(found && move_make(ms[index])) return true;
	return false;
}

