/* Copyright 2018 Romain "Artefact2" Dal Maso <artefact2@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <assert.h>
#include "gumble.h"
#include "moves.h"
#include "lut.h"

#define CCH_TEST_PIN(KING_DIR, PIECE_DIR) do {							\
		for(tsq = KING_DIR(sq); CCH_IS_SQUARE_VALID(tsq); tsq = KING_DIR(tsq)) { \
			if((cur = CCH_GET_SQUARE(b, tsq))) break;				\
		}																\
		if(cur != king) return false;									\
		for(tsq = PIECE_DIR(sq); CCH_IS_SQUARE_VALID(tsq); tsq = PIECE_DIR(tsq)) { \
			if((cur = CCH_GET_SQUARE(b, tsq))) break;					\
		}																\
		return cur == want || cur == want2;								\
	} while(0)

#define CCH_TEST_PIN2(AFTER_DIR, BEFORE_DIR) do {						\
		if(sq < kingsq) {												\
			/* king is after (square order) */							\
			CCH_TEST_PIN(AFTER_DIR, BEFORE_DIR);						\
		} else {														\
			/* king is before */										\
			CCH_TEST_PIN(BEFORE_DIR, AFTER_DIR);						\
		}																\
	} while(0)

bool cch_is_square_pinned(const cch_board_t* b, cch_square_t sq) {
	cch_square_t tsq;
	cch_square_t kingsq = CCH_OWN_KING(b);
	cch_piece_t king = CCH_MAKE_OWN_PIECE(b, CCH_KING);
	cch_piece_t cur, want, want2 = CCH_MAKE_ENEMY_PIECE(b, CCH_QUEEN);

	want = CCH_MAKE_ENEMY_PIECE(b, CCH_ROOK);
	if(rook_lut[sq] & (b->pieces[want] | b->pieces[want2])) {
		if(CCH_FILE(sq) == CCH_FILE(kingsq)) {
			CCH_TEST_PIN2(CCH_NORTH, CCH_SOUTH);
		} else if(CCH_RANK(sq) == CCH_RANK(kingsq)) {
			CCH_TEST_PIN2(CCH_EAST, CCH_WEST);
		}
	}

	want = CCH_MAKE_ENEMY_PIECE(b, CCH_BISHOP);
	if(bishop_lut[sq] & (b->pieces[want] | b->pieces[want2])) {
		if(CCH_DIAG1(sq) == CCH_DIAG1(kingsq)) {
			CCH_TEST_PIN2(CCH_NORTHEAST, CCH_SOUTHWEST);
		} else if(CCH_DIAG2(sq) == CCH_DIAG2(kingsq)) {
			CCH_TEST_PIN2(CCH_SOUTHEAST, CCH_NORTHWEST);
		}
	}

	return false;
}

#define CCH_ADD_POTENTIAL_ATTACKER(DIRECTION) do {						\
		csq = DIRECTION(sq);											\
		if(CCH_IS_SQUARE_INVALID(csq) || CCH_GET_SQUARE(b, csq) != want) break; \
		return true;													\
	} while(0)

#define CCH_ADD_POTENTIAL_RAY_ATTACKER(DIRECTION) do {					\
		for(cch_square_t rsq = DIRECTION(sq); CCH_IS_SQUARE_VALID(rsq); rsq = DIRECTION(rsq)) {	\
			cur = CCH_GET_SQUARE(b, rsq);								\
			if(!cur) continue;											\
			if(cur == want || cur == want2) {							\
				return true;											\
			}															\
			break; /* Obstacle reached */								\
		}																\
	} while(0)

bool cch_is_square_checked(const cch_board_t* b, cch_square_t sq) {
	assert(CCH_IS_SQUARE_VALID(sq));
	cch_piece_t cur, want, want2;
	cch_square_t csq;

	/* XXX: still improvable */

	if(knight_lut[sq] & b->pieces[CCH_MAKE_ENEMY_PIECE(b, CCH_KNIGHT)]) return true;
	if(king_lut[sq] & b->pieces[CCH_MAKE_ENEMY_PIECE(b, CCH_KING)]) return true;

	/* Pawns: 2 candidates */
	want = CCH_MAKE_ENEMY_PIECE(b, CCH_PAWN);
	if(b->side) {
		CCH_ADD_POTENTIAL_ATTACKER(CCH_NORTHWEST);
		CCH_ADD_POTENTIAL_ATTACKER(CCH_NORTHEAST);
	} else {
		CCH_ADD_POTENTIAL_ATTACKER(CCH_SOUTHWEST);
		CCH_ADD_POTENTIAL_ATTACKER(CCH_SOUTHEAST);
	}

	want2 = CCH_MAKE_ENEMY_PIECE(b, CCH_QUEEN);

	/* File/rank attackers */
	want = CCH_MAKE_ENEMY_PIECE(b, CCH_ROOK);
	if(rook_lut[sq] & (b->pieces[want] | b->pieces[want2])) {
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_NORTH);
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_SOUTH);
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_WEST);
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_EAST);
	}

	/* Diagonal attackers */
	want = CCH_MAKE_ENEMY_PIECE(b, CCH_BISHOP);
	if(bishop_lut[sq] & (b->pieces[want] | b->pieces[want2])) {
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_NORTHWEST);
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_NORTHEAST);
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_SOUTHWEST);
		CCH_ADD_POTENTIAL_RAY_ATTACKER(CCH_SOUTHEAST);
	}

	return false;
}

bool cch_is_pseudo_legal_move_legal(cch_board_t* b, const cch_move_t* m) {
	cch_undo_move_state_t undo;
	cch_return_t r;
	bool ret;
	r = cch_play_legal_move(b, m, &undo);
	assert(r == CCH_OK);
	b->side = !b->side;
	ret = CCH_IS_OWN_KING_CHECKED(b);
	b->side = !b->side;
	r = cch_undo_move(b, m, &undo);
	assert(r == CCH_OK);
	return !ret;
}
