#include <vector>
#include <array>
#include <string>
#include <sstream>
#include <cassert>

#include "types.h"
#include "utils.h"
#include "Move.h"
#include "Board.h"

inline enum piece Board::sqPiece(const u64 sqMask) const {
    if (!(sqMask & (_bitBoard[white] | _bitBoard[black]))) {
        return static_cast<enum piece>(0);
    }
    return static_cast<enum piece>(
          (  pawn * static_cast<bool>(sqMask & _bitBoard[pawn])  )
        | (knight * static_cast<bool>(sqMask & _bitBoard[knight]))
        | (bishop * static_cast<bool>(sqMask & _bitBoard[bishop]))
        | (  rook * static_cast<bool>(sqMask & _bitBoard[rook])  )
        | ( queen * static_cast<bool>(sqMask & _bitBoard[queen]) )
        | (  king * static_cast<bool>(sqMask & _bitBoard[king])  )
    );
}
enum piece Board::piece(Index sq) const {
    return sqPiece(bitMask<Square>(sq));
}
enum piece Board::color(Index sq) const {
    u64 sqMask = bitMask<Square>(sq);
    if (sqMask & _bitBoard[white]) {
        return white;
    } else if (sqMask & _bitBoard[black]) {
        return black;
    }
    return static_cast<enum piece>(-1);
}

bool Board::castleRight(enum piece color, enum location side) const {
    assert((side == KingSide) || (side == QueenSide));
    assert((color == white) || (color == black));

    if (color == white) {
        if (side == KingSide) { return _castle.whiteKingSide; }
        else                  { return _castle.whiteQueenSide; }
    } else {
        if (side == KingSide) { return _castle.blackKingSide; }
        else                  { return _castle.blackQueenSide; }
    }
}

char Board::formatPiece(Index fileIndex, Index rankIndex) const {
    assert(fileIndex < 8);
    assert(rankIndex < 8);

    return formatPiece(8 * rankIndex + fileIndex);
}
char Board::formatPiece(Index sq) const {
    assert(sq < 64);

    u64 sqMask = bitMask<Square>(sq);

    if ((_bitBoard[white] | _bitBoard[black]) & sqMask) {
        enum piece type = piece(sq);
        switch (type) {
            case rook:   return (_bitBoard[white] & sqMask) ? 'R' : 'r';
            case knight: return (_bitBoard[white] & sqMask) ? 'N' : 'n';
            case bishop: return (_bitBoard[white] & sqMask) ? 'B' : 'b';
            case queen:  return (_bitBoard[white] & sqMask) ? 'Q' : 'q';
            case king:   return (_bitBoard[white] & sqMask) ? 'K' : 'k';
            case pawn:   return (_bitBoard[white] & sqMask) ? 'P' : 'p';
            default:
                return '?';
        }
    }

    return ' ';
}

std::string Board::fen(const bool withCounts) const {
    std::ostringstream out;

    // Part 1 - Piece Placement
    for (Index rankIndex = 0, count = 0; rankIndex < 8; rankIndex++) {
        for (Index fileIndex = 0; fileIndex < 8; fileIndex++) {
            char c = formatPiece(fileIndex, rankIndex);
            if (c == ' ') {
                count++;
            } else if (count) {
                out << static_cast<unsigned>(count) << c;
                count = 0;
            } else {
                out << c;
            }
        }
        if (count) {
            out << static_cast<unsigned>(count);
            count = 0;
        }
        out << ((rankIndex < 7) ? '/' : ' ');
    }

    // Part 2 - Side to Move
    out << (isWhiteTurn() ? 'w' : 'b') << ' ';

    // Part 3 - Castling Rights
    if (!(_castle.whiteKingSide | _castle.whiteQueenSide
        | _castle.blackKingSide | _castle.blackQueenSide)) {
        out << "- ";
    } else {
        if (_castle.whiteKingSide)  { out << 'K'; };
        if (_castle.whiteQueenSide) { out << 'Q'; };
        if (_castle.blackKingSide)  { out << 'k'; };
        if (_castle.blackQueenSide) { out << 'q'; };
        out << ' ';
    }

    // Part 4 - En passant target square
    if (_enPassant > 63) {
        out << '-';
    } else {
        out << static_cast<char>('a' + (_enPassant % 8))    // file
            << static_cast<char>('8' - (_enPassant / 8));   // rank
    }

    if (withCounts) {
        // Part 5 - Half Move Clock
        out << ' ' << _halfMoveClock << ' ';

        // Part 6 - Full Move counter
        out << ((_plyCount + 1U) / 2U);
    }

    return out.str();
}

/**
 * The hash lookup consists of
 * - 6 pieces for each color and each square             -> 6 * 2 * 64 = 768
 * - en passant target file (only file)                  ->                8
 * - king and queen side castling rights for each color  ->      2 * 2 =   4
 * - and the side to move                                ->                1
 * resulting in 781 table entries                                  sum = 781
 * the castling rights are index by 776 + [ 0 | 1 | 2 | 3 ]
 * and the side to move is just 780
 */
u64 Board::rehash() const {
    u64 hash = 0;

    // Hash all the pieces on the board
    for (enum piece piece : {pawn, rook, knight, bishop, queen, king}) {
        for (u64 sq = _bitBoard[piece]; sq; sq &= sq - 1) {
            Index sqIndex = bitScanLS(sq);
            hash ^= hashTable[hashIndex(this->piece(sqIndex), color(sqIndex), sqIndex)];
        }
    }

    // E.p. target file hash
    if (_enPassant < static_cast<Index>(64)) {
        hash ^= hashTable[hashIndex(_enPassant)];
    }

    // Next, the castling rights
    if (_castle.whiteKingSide)  { hash ^= hashTable[776];    }
    if (_castle.whiteQueenSide) { hash ^= hashTable[776 + 1]; }
    if (_castle.blackKingSide)  { hash ^= hashTable[776 + 2]; }
    if (_castle.blackQueenSide) { hash ^= hashTable[776 + 3]; }

    // Finally, side to move (for white to move)
    if (isWhiteTurn()) {
        hash ^= hashTable[780];
    }

    return hash;
}

void Board::init(const std::string& fen, bool& parseError) {
    // first check length (conservative boarders, TODO: tighten)
    if ((fen.length() < 10) || (200 < fen.length())) {
        parseError = true; return;
    }

    // split FEN into its parts
    std::vector<std::string> parts = split(fen);
    // check if there are exactly 6 parts
    if (parts.size() != 6) {
        parseError = true; return;
    }

    // (re)set all bit boards to zero
    for (Index i = 0; i < 8; i++) {
        _bitBoard[i] = static_cast<u64>(0);
    }

    // Part 1 - Piece Placement
    Index rankIndex = 0, fileIndex = 0;
    Index whiteKing = 0, blackKing = 0;
    for (char c : parts[0]) {
        if (c == '/') {
            if (fileIndex != 8) { parseError = true; return; }
            rankIndex++;
            fileIndex = 0;
            continue;
        } else if ('0' < c && c < '9') {
            fileIndex += static_cast<Index>(c - '0');
            continue;
        }

        enum piece piece_color = isUpper(c) ? white : black;
        enum piece piece_type;
        switch (toUpper(c)) {
            case 'P': piece_type = pawn;   break;
            case 'R': piece_type = rook;   break;
            case 'B': piece_type = bishop; break;
            case 'N': piece_type = knight; break;
            case 'K': piece_type = king;   break;
            case 'Q': piece_type = queen;  break;
            default: parseError = true; return;
        }

        if (piece_type == king) {
            if (piece_color == white) whiteKing++;
            if (piece_color == black) blackKing++;
        }

        _bitBoard[piece_color] |= bitMask<Square>(fileIndex, rankIndex);
        _bitBoard[piece_type]  |= bitMask<Square>(fileIndex, rankIndex);

        fileIndex++;
    }
    if ((rankIndex != 7) || (whiteKing != 1) || (blackKing != 1)) {
        parseError = true; return;
    }

    // Part 2 - Side to Move (ply being odd/even)
         if (parts[1] == "w") { _plyCount = 1; }
    else if (parts[1] == "b") { _plyCount = 2; }
    else {
        parseError = true; return;
    }

    // Part 3 - Castling ability
    _castle.whiteKingSide  = false;
    _castle.whiteQueenSide = false;
    _castle.blackKingSide  = false;
    _castle.blackQueenSide = false;
    if (parts[2] != "-") {
        for (char c : parts[2]) {
            switch (c) {
                case 'K': _castle.whiteKingSide  = true; break;
                case 'Q': _castle.whiteQueenSide = true; break;
                case 'k': _castle.blackKingSide  = true; break;
                case 'q': _castle.blackQueenSide = true; break;
                default: parseError = true; return;
            }
        }
    }

    // Part 4 - En passant target square
    if (parts[3] == "-") {
        _enPassant = static_cast<Index>(64);
    } else {
        // _enPassant = UCI::parseSquare(parts[3], parseError);
        if (parts[3].length() != 2) {
            parseError = true;
            return;
        }
        if (parts[3][0] < 'a' || 'h' < parts[3][0]
        ||  parts[3][1] < '1' || '8' < parts[3][1]) {
            parseError = true;
            return;
        }

        Index fileIndex = static_cast<Index>(parts[3][0] - 'a');
        Index rankIndex = static_cast<Index>('8' - parts[3][1]);

        _enPassant = 8 * rankIndex + fileIndex;
    }

    // Part 5 - Half Move Clock
    _halfMoveClock = parseUnsigned<unsigned>(parts[4], parseError);
    if (parseError) { return; }

    // Part 6 - Full Move counter
    unsigned moveCounter = parseUnsigned<unsigned>(parts[5], parseError);
    if (parseError || (moveCounter < 1)) { parseError = true; return; }
    _plyCount += 2U * (moveCounter - 1U);

    // Set new hash
    _hash = rehash();
}

Move Board::isLegal(const Move move) const {
    // First check the move itself (null move check)
    if (move.from() == move.to()) { return Move(0); }

    // Now, simple check if the move is in the set of legal moves
    MoveList allLegalMoves;
    moves(allLegalMoves);
    for (Move legalMove : allLegalMoves) {
        if ((move.from()    == legalMove.from())
        &&  (move.to()      == legalMove.to())
        &&  (move.promote() == legalMove.promote()))
        {
            return legalMove;
        }
    }

    // move not found in the legal moves -> NOT legal
    return Move(0);
}


// NOTE: Assumes a legal move, otherwise undefined behavior!
void Board::make(const Move move) {

    assert(this->piece(move.from()) == move.piece());
    assert((
           (move.piece() == pawn)
        && (move.to() == _enPassant)
        && (move.victim() == pawn)
    ) || (
        this->piece(move.to()) == move.victim()
    ));

    // move piece (and perform capture)
    const u64 from = bitMask<Square>(move.from());
    const u64 to   = bitMask<Square>(move.to());
    const enum piece piece = move.piece();
    const enum piece color = isWhiteTurn() ? white : black;
    const enum piece enemy = (color == white) ? black : white;
    bool isCapture;

    if (isWhiteTurn()) {
        // check if is a capture (for half move clock, not considering e.p.
        // capture since irrelevant, half move clock is reset on pawn move)
        isCapture = static_cast<bool>(_bitBoard[black] & to);
        // move pieces
        _bitBoard[white] = (_bitBoard[white] & ~from) | to;
        _bitBoard[black] &= ~to;
        // finish castling (king already moved -> move rook)
        if (piece == king) {
            if ((from >> 1) & (to << 1)) { // queen side castling
                _bitBoard[rook]  ^= (bitMask<Square>(a1) | bitMask<Square>(d1));
                _bitBoard[white] ^= (bitMask<Square>(a1) | bitMask<Square>(d1));
                _hash ^= hashTable[hashIndex(rook, white, a1)];
                _hash ^= hashTable[hashIndex(rook, white, d1)];
            }
            if ((from << 1) & (to >> 1)) { // king side castling
                _bitBoard[rook]  ^= (bitMask<Square>(f1) | bitMask<Square>(h1));
                _bitBoard[white] ^= (bitMask<Square>(f1) | bitMask<Square>(h1));
                _hash ^= hashTable[hashIndex(rook, white, f1)];
                _hash ^= hashTable[hashIndex(rook, white, h1)];
            }
        }
        // finish en passant capture (remove captured pawn)
        if ((piece == pawn) && (move.to() == _enPassant)) {
            _bitBoard[pawn]  ^= bitMask<Square>(_enPassant) << 8;
            _bitBoard[black] ^= bitMask<Square>(_enPassant) << 8;
            _hash ^= hashTable[hashIndex(pawn, black, _enPassant + 8)];
        }
    } else { // black's turn
        // check if is a capture (for half move clock, not considering e.p.
        // capture since irrelevant, half move clock is reset on pawn move)
        isCapture = static_cast<bool>(_bitBoard[white] & to);
        // move pieces
        _bitBoard[black] = (_bitBoard[black] & ~from) | to;
        _bitBoard[white] &= ~to;
        // finish castling (king already moved -> move rook)
        if (piece == king) {
            if ((from >> 1) & (to << 1)) { // queen side castling
                _bitBoard[rook]  ^= bitMask<Square>(a8) | bitMask<Square>(d8);
                _bitBoard[black] ^= bitMask<Square>(a8) | bitMask<Square>(d8);
                _hash ^= hashTable[hashIndex(rook, black, a8)];
                _hash ^= hashTable[hashIndex(rook, black, d8)];
            }
            if ((from << 1) & (to >> 1)) { // king side castling
                _bitBoard[rook]  ^= bitMask<Square>(f8) | bitMask<Square>(h8);
                _bitBoard[black] ^= bitMask<Square>(f8) | bitMask<Square>(h8);
                _hash ^= hashTable[hashIndex(rook, black, f8)];
                _hash ^= hashTable[hashIndex(rook, black, h8)];
            }
        }
        // finish en passant capture (remove captured pawn)
        if ((piece == pawn) && (move.to() == _enPassant)) {
            _bitBoard[pawn]  ^= bitMask<Square>(_enPassant) >> 8;
            _bitBoard[white] ^= bitMask<Square>(_enPassant) >> 8;
            _hash ^= hashTable[hashIndex(pawn, white, _enPassant - 8)];
        }
    }
    // update hash
    _hash ^= hashTable[hashIndex(piece, color, move.from())];
    _hash ^= hashTable[hashIndex(piece, color, move.to())];
    if (isCapture) {
        _hash ^= hashTable[hashIndex(move.victim(), enemy, move.to())];
    }
    // and move (including capture) the piece
    _bitBoard[rook]   &= ~(from | to);
    _bitBoard[knight] &= ~(from | to);
    _bitBoard[bishop] &= ~(from | to);
    _bitBoard[queen]  &= ~(from | to);
    _bitBoard[king]   &= ~(from | to);
    _bitBoard[pawn]   &= ~(from | to);
    _bitBoard[piece] |= to;

    // increment half move clock (+1 iff no pawn advance or capture)
    _halfMoveClock = (_halfMoveClock + 1) * (piece != pawn) * !isCapture;

    // write en passant target square (and remove previous from hash)
    if (_enPassant < static_cast<Index>(64)) {
        _hash ^= hashTable[hashIndex(_enPassant)];
    }
    if (piece == pawn) {
        u64 epMask = ((from >> 8) & (to << 8)) | ((from << 8) & (to >> 8));
        _enPassant = static_cast<Index>(epMask ? bitScanLS(epMask) : 64);
        if (_enPassant < static_cast<Index>(64)) {
            _hash ^= hashTable[hashIndex(_enPassant)];
        }
    } else {
        _enPassant = static_cast<Index>(64);
    }

    // handle promotions
    if (move.promote()) {
        _bitBoard[pawn] &= ~to;
        _bitBoard[move.promote()] |= to;
        _hash ^= hashTable[hashIndex(pawn, color, move.to())];
        _hash ^= hashTable[hashIndex(move.promote(), color, move.to())];
    }

    // revoke castling rights (rook captures revoke castling rights as well)
    if (_castle.whiteKingSide
    && ((from | to) & (bitMask<Square>(e1) | bitMask<Square>(h1)))) {
        _castle.whiteKingSide = false;
        _hash ^= hashTable[776];
    }
    if (_castle.whiteQueenSide
    && ((from | to) & (bitMask<Square>(a1) | bitMask<Square>(e1)))) {
        _castle.whiteQueenSide = false;
        _hash ^= hashTable[776 + 1];
    }
    if (_castle.blackKingSide
    && ((from | to) & (bitMask<Square>(e8) | bitMask<Square>(h8)))) {
        _castle.blackKingSide = false;
        _hash ^= hashTable[776 + 2];
    }
    if (_castle.blackQueenSide
    && ((from | to) & (bitMask<Square>(a8) | bitMask<Square>(e8)))) {
        _castle.blackQueenSide = false;
        _hash ^= hashTable[776 + 3];
    }

    // (last operation, isWhiteTurn() depends on this)
    _plyCount++;
    // and toggle side to move bit string
    _hash ^= hashTable[780];
}

// BitMask of all squares the king is NOT allowed to be
//
// @param enemy opponents color
// @param xRayEmpty empty bitboard with king (of moving color) removed
u64 Board::attacks(const enum piece enemy, const u64 empty) const {
    u64 attackeMask = 0;
    // begin with pawn attacks
    u64 attacker = _bitBoard[enemy] & _bitBoard[pawn];
    if (enemy == white) {
        attackeMask |= shift<RightUp>(attacker);
        attackeMask |= shift<LeftUp> (attacker);
    } else {
        attackeMask |= shift<RightDown>(attacker);
        attackeMask |= shift<LeftDown>(attacker);
    }
    // add knight attacks
    attacker = _bitBoard[enemy] & _bitBoard[knight];
    for (; attacker; attacker &= attacker - 1) {
        attackeMask |= knightMoveLookup[bitScanLS(attacker)];
    }
    // Add left-right up-down sliding piece attacks (rooks, queens)
    attacker = _bitBoard[enemy] & (_bitBoard[rook] | _bitBoard[queen]);
    attackeMask |= fill7dump<Up>   (attacker, empty)
                 | fill7dump<Down> (attacker, empty)
                 | fill7dump<Left> (attacker, empty)
                 | fill7dump<Right>(attacker, empty);
    // and the diagonal sliding attacks
    attacker = _bitBoard[enemy] & (_bitBoard[bishop] | _bitBoard[queen]);
    attackeMask |= fill7dump<LeftUp>   (attacker, empty)
                 | fill7dump<LeftDown> (attacker, empty)
                 | fill7dump<RightUp>  (attacker, empty)
                 | fill7dump<RightDown>(attacker, empty);
    // finally, the enemy king
    attackeMask |= kingMoveLookup[bitScanLS(_bitBoard[enemy] & _bitBoard[king])];

    return attackeMask;
}

u64 Board::pinned(const enum piece enemy, const u64 cKing, const u64 empty) const {
    const enum piece color = (enemy == white) ? black : white;

    u64 pinMask = 0;
    u64 ray, attacker;
    // rook & queen sliding attacks
    attacker = _bitBoard[enemy] & (_bitBoard[rook] | _bitBoard[queen]);
    ray = fill7dump<Up>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<Down>(attacker, empty);
    ray = fill7dump<Down>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<Up>(attacker, empty);
    ray = fill7dump<Left>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<Right>(attacker, empty);
    ray = fill7dump<Right>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<Left>(attacker, empty);
    // bishop & queen sliding attacks
    attacker = _bitBoard[enemy] & (_bitBoard[bishop] | _bitBoard[queen]);
    ray = fill7dump<LeftUp>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<RightDown>(attacker, empty);
    ray = fill7dump<RightDown>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<LeftUp>(attacker, empty);
    ray = fill7dump<LeftDown>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<RightUp>(attacker, empty);
    ray = fill7dump<RightUp>(cKing, empty);
    pinMask |= ray & _bitBoard[color] & fill7dump<LeftDown>(attacker, empty);

    return pinMask;
}

u64 Board::checkers(const enum piece enemy, const u64 cKing, const u64 empty) const {
    // Initialize checkers with knights giving check
    u64 checkers = knightMoveLookup[bitScanLS(cKing)] & _bitBoard[enemy] & _bitBoard[knight];

    u64 ray, attacker;
    // rook & queen sliding attacks
    attacker = _bitBoard[enemy] & (_bitBoard[rook] | _bitBoard[queen]);
    ray = fill7dump<Up>(cKing, empty);
    checkers |= ray & attacker;
    ray = fill7dump<Down>(cKing, empty);
    checkers |= ray & attacker;
    ray = fill7dump<Left>(cKing, empty);
    checkers |= ray & attacker;
    ray = fill7dump<Right>(cKing, empty);
    checkers |= ray & attacker;
    // bishop & queen sliding attacks
    attacker = _bitBoard[enemy] & (_bitBoard[bishop] | _bitBoard[queen]);
    ray = fill7dump<LeftUp>(cKing, empty);
    checkers |= ray & attacker;
    ray = fill7dump<RightDown>(cKing, empty);
    checkers |= ray & attacker;
    ray = fill7dump<LeftDown>(cKing, empty);
    checkers |= ray & attacker;
    ray = fill7dump<RightUp>(cKing, empty);
    checkers |= ray & attacker;

    // Finish checkers by pawns (if the king would be a pawn, he attacks
    // check giving enemy pawns)
    attacker = _bitBoard[enemy] & _bitBoard[pawn];
    if (enemy == black) {
        checkers |= shift<RightUp>(cKing) & attacker;
        checkers |= shift<LeftUp>(cKing) & attacker;
    } else {
        checkers |= shift<RightDown>(cKing) & attacker;
        checkers |= shift<LeftDown>(cKing) & attacker;
    }

    return checkers;
}

void Board::moves(MoveList& moveList, bool capturesOnly) const {
    moves(moveList, isWhiteTurn() ? white : black, capturesOnly);
}

void Board::moves(MoveList& moveList, const enum piece color,
    bool capturesOnly) const
{
    assert(!(_bitBoard[white] & _bitBoard[black]));
    assert(bitCount(_bitBoard[black] & _bitBoard[king]) == 1);
    assert(bitCount(_bitBoard[white] & _bitBoard[king]) == 1);
    assert((
        _bitBoard[white] | _bitBoard[black]
    ) == (
          _bitBoard[pawn]
        ^ _bitBoard[rook]
        ^ _bitBoard[bishop]
        ^ _bitBoard[knight]
        ^ _bitBoard[queen]
        ^ _bitBoard[king]
    ));

    // Get the opponents color
    const enum piece enemy = (color == white) ? black : white;

    const u64 cKing = _bitBoard[color] & _bitBoard[king];
    const Index cKingSq = bitScanLS(cKing);
    const u64 occupied = _bitBoard[white] | _bitBoard[black];
    const u64 empty    = ~occupied;

    /****** Compute danger squares for the king (when moving to in check) ******/
    // All fields under attack of an enemy piece (or pawn) with a see-through king
    u64 dangerMask = attacks(enemy, ~(occupied ^ cKing));
    // Restricts possible move target squares (except king movement). Also used
    // in check to restrict moves to check evading moves for non-king pieces.
    u64 toMask = capturesOnly ? _bitBoard[enemy] : -1;
    // Pinned pieces (all pieces from the kings perspective which by see through
    // discover enemy sliding pieces in that direction)
    u64 pinMask = pinned(enemy, cKing, empty);
    // Initialize checkers with none, iff in check compute checkers
    u64 checkers = 0;

    /***** Determine (Double) Check and check giving pieces (aka checkers) *****/
    // If the king is on a danger square he is in check
    bool isCheck = static_cast<bool>(cKing & dangerMask);
    bool isDoubleCheck = false;

    if (isCheck) {
        // Compute checkers (bitmask of check giving pieces)
        checkers = this->checkers(enemy, cKing, empty);
        // Check for double check (by testing for at least two bits in checkers)
        isDoubleCheck = static_cast<bool>(checkers & (checkers - 1));
        // compute move target square mask
        if (!isDoubleCheck) {
            toMask &= checkers; // one square

            // if check giving piece is a sliding piece, it can be blocked
            if (checkers & (_bitBoard[rook] | _bitBoard[bishop] | _bitBoard[queen])) {
                u64 attacker = fill7dump<Up>   (cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
                attacker = fill7dump<Down>     (cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
                attacker = fill7dump<Left>     (cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
                attacker = fill7dump<Right>    (cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
                attacker = fill7dump<LeftUp>   (cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
                attacker = fill7dump<LeftDown> (cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
                attacker = fill7dump<RightUp>  (cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
                attacker = fill7dump<RightDown>(cKing, empty);
                toMask |= attacker * static_cast<bool>(attacker & checkers);
            }
        }
    }

    /***************************** Generate moves *****************************/
    { // King Moves
        u64 to = kingMoveLookup[cKingSq] &~ (dangerMask | _bitBoard[color]);
        // King moves can not be handled with the toMask trick
        if (capturesOnly) {
            to &= _bitBoard[enemy];
        }
        for (; to; to &= to - 1) {
            moveList.emplace_back(color, king, cKingSq, bitScanLS(to),
                                  sqPiece(to & -to));
        }
    }
    // if in double check, the king must move (-> stop move generation)
    if (isDoubleCheck) { return; }
    // and castling
    if (!isCheck && !capturesOnly) {
        // king is not allowed to move through or to an attacked square,
        // but the rook is allowed to and might also land on an attacked square
        // (at the b file when queen side while castling)
        u64 castlingDanger = (dangerMask &~ bitMask<File>(b1)) | occupied;
        if (color == white) {
            constexpr u64 kingSide = bitMask<Square>(f1) | bitMask<Square>(g1);
            constexpr u64 queenSide = bitMask<Square>(b1) | bitMask<Square>(c1)
                                    | bitMask<Square>(d1);
            if (_castle.whiteKingSide && !(castlingDanger & kingSide)) {
                moveList.emplace_back(white, king, e1, g1, none);
            }
            if (_castle.whiteQueenSide && !(castlingDanger & queenSide)) {
                moveList.emplace_back(white, king, e1, c1, none);
            }
        } else {
            constexpr u64 kingSide = bitMask<Square>(f8) | bitMask<Square>(g8);
            constexpr u64 queenSide = bitMask<Square>(b8) | bitMask<Square>(c8)
                                    | bitMask<Square>(d8);
            if (_castle.blackKingSide && !(castlingDanger & kingSide)) {
                moveList.emplace_back(black, king, e8, g8, none);
            }
            if (_castle.blackQueenSide && !(castlingDanger & queenSide)) {
                moveList.emplace_back(black, king, e8, c8, none);
            }
        }
    }

    { // Rook Moves
        u64 rooks = _bitBoard[color] & _bitBoard[rook];
        for (; rooks; rooks &= rooks - 1) {
            Index fromSq = bitScanLS(rooks);
            u64 from = rooks & -rooks;
            u64 to = fill7dump<Left> (from, empty)
                   | fill7dump<Right>(from, empty)
                   | fill7dump<Up>   (from, empty)
                   | fill7dump<Down> (from, empty);
            // check if pinned and restrict `to` accordingly
            if (from & pinMask) {
                to &= lineMask(cKingSq, fromSq);
            }
            to &= toMask & ~_bitBoard[color];
            for (; to; to &= to - 1) {
                moveList.emplace_back(color, rook, fromSq, bitScanLS(to),
                                      sqPiece(to & -to));
            }
        }
    }

    { // Bishop Moves
        u64 bishops = _bitBoard[color] & _bitBoard[bishop];
        for (; bishops; bishops &= bishops - 1) {
            Index fromSq = bitScanLS(bishops);
            u64 from = bishops & -bishops;
            u64 to = fill7dump<LeftUp>   (from, empty)
                   | fill7dump<RightUp>  (from, empty)
                   | fill7dump<LeftDown> (from, empty)
                   | fill7dump<RightDown>(from, empty);
            // check if pinned and restrict `to` accordingly
            if (from & pinMask) {
                to &= lineMask(cKingSq, fromSq);
            }
            to &= toMask & ~_bitBoard[color];
            for (; to; to &= to - 1) {
                moveList.emplace_back(color, bishop, fromSq, bitScanLS(to),
                                      sqPiece(to & -to));
            }
        }
    } 

    { // Queen Moves
        u64 queens = _bitBoard[color] & _bitBoard[queen];
        for (; queens; queens &= queens - 1) {
            Index fromSq = bitScanLS(queens);
            u64 from = queens & -queens;
            u64 to = fill7dump<Left>     (from, empty)
                   | fill7dump<Right>    (from, empty)
                   | fill7dump<Up>       (from, empty)
                   | fill7dump<Down>     (from, empty)
                   | fill7dump<LeftUp>   (from, empty)
                   | fill7dump<RightUp>  (from, empty)
                   | fill7dump<LeftDown> (from, empty)
                   | fill7dump<RightDown>(from, empty);
            // check if pinned and restrict `to` accordingly
            if (from & pinMask) {
                to &= lineMask(cKingSq, fromSq);
            }
            to &= toMask & ~_bitBoard[color];
            for (; to; to &= to - 1) {
                moveList.emplace_back(color, queen, fromSq, bitScanLS(to),
                                      sqPiece(to & -to));
            }
        }
    }

    { // Knight Moves
        u64 knights = _bitBoard[color] & _bitBoard[knight];
        for (; knights; knights &= knights - 1) {
            u64 from = knights & -knights;
            // check if pinned (pinned knights can't move)
            if (from & pinMask) { continue; }
            Index fromSq = bitScanLS(knights);
            u64 to = toMask & knightMoveLookup[fromSq] & ~_bitBoard[color];
            for (; to; to &= to - 1) {
                moveList.emplace_back(color, knight, fromSq, bitScanLS(to),
                                      sqPiece(to & -to));
            }
        }
    }

    { // Pawn moves
        u64 pawns = _bitBoard[color] & _bitBoard[pawn]; // for all pawns
        u64 targets = _bitBoard[enemy];                 // capture targets
        for (; pawns; pawns &= pawns - 1) {
            Index fromSq = bitScanLS(pawns);
            u64 from = pawns & -pawns;
            // Compute pawn push, double pawn push as well as left and right
            // capture target squares. Also set a promotion flag if reaching
            // the final rank (rank 8 for white and rank 1 for black)
            u64 to = 0;
            bool isPromotion = false;
            if (color == white) {
                to = (from >> 8) & empty;
                to |= (  (to &  bitMask<Rank>(a3)) >> 8) & empty;
                to |= ((from &~ bitMask<File>(a1)) >> 9) & targets;
                to |= ((from &~ bitMask<File>(h1)) >> 7) & targets;
                isPromotion = static_cast<bool>(from & bitMask<Rank>(a7));
            } else {
                to = (from << 8) & empty;
                to |= (  (to &  bitMask<Rank>(a6)) << 8) & empty;
                to |= ((from &~ bitMask<File>(a1)) << 7) & targets;
                to |= ((from &~ bitMask<File>(h1)) << 9) & targets;
                isPromotion = static_cast<bool>(from & bitMask<Rank>(a2));
            }
            to &= toMask;
            if (from & pinMask) {
                to &= lineMask(cKingSq, fromSq);
            }
            if (isPromotion) {
                for (; to; to &= to - 1) {
                    Index toSq = bitScanLS(to);
                    enum piece victim = sqPiece(to & -to);
                    moveList.emplace_back(color, pawn, fromSq, toSq, victim, rook);
                    moveList.emplace_back(color, pawn, fromSq, toSq, victim, knight);
                    moveList.emplace_back(color, pawn, fromSq, toSq, victim, bishop);
                    moveList.emplace_back(color, pawn, fromSq, toSq, victim, queen);
                }
            } else {
                for (; to; to &= to - 1) {
                    moveList.emplace_back(color, pawn, fromSq, bitScanLS(to),
                                          sqPiece(to & -to));
                }
            }
        }
        // and en passant captures (handled separately, cause of special cases.
        // Most of them are simply handled by adding the e.p. target square to
        // targets, but it misinterprets some situations)
        if (_enPassant < 64) {
            u64 to = bitMask<Square>(_enPassant);
            u64 target;
            pawns = 0;
            if (color == white) {
                pawns |= shift<LeftDown> (to);
                pawns |= shift<RightDown>(to);
                target = shift<Down>(to);
            } else {
                pawns |= shift<LeftUp>(to);
                pawns |= shift<RightUp>(to);
                target = shift<Up>(to);
            }
            pawns &= _bitBoard[color] & _bitBoard[pawn];

            // now a few situations can occur
            // 1. No check
            //   a. legal e.p. capture
            //   b. illegal due to discovered check
            // 2. In check (case of double check already handled)
            //   a. e.p. capture resolved check (e.p. target pawn gives check)
            //      but can still lead to a discovered check
            //   b. illegal, e.p. does not resolve check
            // In ether case, we perform an check test if the move is legal
            // which should be OK with the hope that e.p. captures are rare
            // enough (does not slows down computation too much^^)
            if (!isCheck || (isCheck && (target & checkers))) {
                for (; pawns; pawns &= pawns - 1) { // max twice
                    Index fromSq = bitScanLS(pawns);
                    u64 from = pawns & -pawns;
                    // perform reduced check test, through an e.p. capture, only
                    // sliding pieces could are involved in discovered check (and
                    // double check is was handled already)
                    u64 futureOccupied = occupied ^ (from | target | to);
                    bool discoveredCheck = false;

                    u64 attacker = _bitBoard[enemy] & (_bitBoard[rook] | _bitBoard[queen]);
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<Up>   (cKing, ~futureOccupied));
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<Down> (cKing, ~futureOccupied));
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<Left> (cKing, ~futureOccupied));
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<Right>(cKing, ~futureOccupied));

                    attacker = _bitBoard[enemy] & (_bitBoard[bishop] | _bitBoard[queen]);
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<LeftUp>   (cKing, ~futureOccupied));
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<LeftDown> (cKing, ~futureOccupied));
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<RightUp>  (cKing, ~futureOccupied));
                    discoveredCheck |= static_cast<bool>(attacker & fill7dump<RightDown>(cKing, ~futureOccupied));

                    if (!discoveredCheck) {
                        moveList.emplace_back(color, pawn, fromSq, _enPassant, pawn);
                    }
                }
            }
        }
    }
}

bool Board::isCheck() const {
    const enum piece color = isWhiteTurn() ? white : black;
    const enum piece enemy = (color == white) ? black : white;
    const u64 cKing = _bitBoard[color] & _bitBoard[king];

    u64 empty = ~(_bitBoard[white] | _bitBoard[black]);
    return static_cast<bool>(attacks(enemy, empty) & cKing);
}

bool Board::isQuiet() const {
    const enum piece color = sideToMove();
    const enum piece enemy = static_cast<enum piece>(!color);
    const u64 empty = ~(_bitBoard[white] | _bitBoard[black]);

    return !static_cast<bool>(attacks(color, empty) & _bitBoard[enemy]);
}

bool Board::isTerminal() const {
    // check 50 move rule
    if (_halfMoveClock >= 100) {
        return true;
    }
    // check if there are any moves, then the game is NOT over
    MoveList moveList;
    moves(moveList);
    return moveList.empty();
}

bool Board::isInsufficient() const {
    switch (bitCount(_bitBoard[white] | _bitBoard[black])) {
        case 2: return true;
        case 3: return _bitBoard[knight] | _bitBoard[bishop];
        case 4:
            // Two opposite bishops of the same color (ignoring promoted bishops)
            return (
                (_bitBoard[bishop] == (_bitBoard[bishop] & bitMask<WhiteSquares>()))
                ||
                (_bitBoard[bishop] == (_bitBoard[bishop] & bitMask<BlackSquares>()))
            );
        default: break;
    };
    return false;
}

Score Board::evalMaterial(enum piece color) const {
    Score material = 0;
    for (enum piece piece : { pawn, rook, knight, bishop, queen }) {
        material += bitCount(_bitBoard[color] & _bitBoard[piece]) * pieceValues[piece];
    }
    return material;
}
Score Board::evalPawns(enum piece color) const {
    // Penalties/Bonuses
    constexpr Score doubledPenalty = 10;
    constexpr Score isolatedPenalty = 20;
    constexpr Score backwardsPenalty = 8;
    constexpr Score passedBonus = 20;

    // Pawn position and structure score
    Score score = 0;

    // Pawn of color BitBoard
    u64 pawns = _bitBoard[color] & _bitBoard[pawn];

    // Compute doubled pawns (all pawns behind another pawn)
    // For white count less advanced and for black more advanced pawns -> equiv
    u64 doubled = pawns & shift<Down>(fill<Down>(pawns));
    score -= bitCount(doubled) * doubledPenalty;
    // Isolated pawns (pawns without any friendly pawns on adjacent files)
    u64 isolated = pawns;
    isolated &= shift<Left>( ~fill<File>(pawns)) | bitMask<File>(h1);
    isolated &= shift<Right>(~fill<File>(pawns)) | bitMask<File>(a1);
    score -= bitCount(isolated) * isolatedPenalty;

    // Sum piece square table and evaluate pawn structure
    if (color == white) {
        for (u64 sq = pawns; sq; sq &= sq - 1) {
            Index i = bitScanLS(sq);
            score += PSQT[pawn][i];
        }
        // Backwards pawns (not isolated but behind all adjacent friendly pawns)
        u64 backwards = pawns & ~isolated;
        backwards &= ~fill<Up>(shift<Left>(pawns) | shift<Right>(pawns));
        score -= bitCount(backwards) * backwardsPenalty;
        // Passed Pawns (Pawns without enemy pawns to stop it from advancing)
        u64 enemy = _bitBoard[black] & _bitBoard[pawn];
        u64 passed = pawns & ~fill<Down>(shift<LeftDown>(enemy)
                                       | enemy
                                       | shift<RightDown>(enemy));
        for (u64 sq = passed; sq; sq &= sq - 1) {
            score += (7 - rankIndex(bitScanLS(sq))) * passedBonus;
        }
    } else { // color == black
        for (u64 sq = pawns; sq; sq &= sq - 1) {
            Index i = bitScanLS(sq);
            score += PSQT[pawn][63 - i];
        }
        // Backwards pawns (not isolated but behind all adjacent friendly pawns)
        u64 backwards = pawns & ~isolated;
        backwards &= ~fill<Down>(shift<Left>(pawns) | shift<Right>(pawns));
        score -= bitCount(backwards) * backwardsPenalty;
        // Passed Pawns (Pawns without enemy pawns to stop it from advancing)
        u64 enemy = _bitBoard[white] & _bitBoard[pawn];
        u64 passed = pawns & ~fill<Up>(shift<LeftUp>(enemy)
                                     | enemy
                                     | shift<RightUp>(enemy));
        for (u64 sq = passed; sq; sq &= sq - 1) {
            score += rankIndex(bitScanLS(sq)) * passedBonus;
        }
    }

    return score;
}

Score Board::evalKingSafety(enum piece color) const {
    // Kings position
    Index kingSq = (color == white)
        ? bitScanLS(              _bitBoard[white] & _bitBoard[king])
        : bitScanLS(bitFlip<Rank>(_bitBoard[black] & _bitBoard[king]));

    Score score = PSQT[king][kingSq];

    if ((fileIndex(kingSq) < 3) || (4 < fileIndex(kingSq))) { // King is castled
        // Pawn shields are the least advanced pawns per file
        u64 pawnShield = (color == white)
            ?               _bitBoard[white] & _bitBoard[pawn]
            : bitFlip<Rank>(_bitBoard[black] & _bitBoard[pawn]);
        pawnShield &= fill7dump<Up>(bitMask<Rank>(a1), ~pawnShield);
        // hack to sub-select the three king/queen side border files
        Index kingSide = (kingSq < 3) ? 1 : 6; // ether b1 or g1
        pawnShield &= fill<File>(kingMoveLookup[kingSide]);

        for (u64 mask : { bitMask<File>(a1) | bitMask<File>(h1),
                          bitMask<File>(b1) | bitMask<File>(g1),
                          bitMask<File>(c1) | bitMask<File>(f1) }) {
            u64 pawn = pawnShield & mask;
            if (pawn) {
                switch (rankIndex(bitScanLS(pawn))) {
                    case 6: break; // intact shield
                    case 5: score -= 10; break; // pawn moved once
                    default: score -= 20;       // moved more than once
                }
            } else {
                score -= 25;    // no shield (no pawn)
            }
        }
    } else { // not castled, penalize open files
        // Select squares of open files
        u64 openFiles = ~fill<File>(_bitBoard[pawn]);
        // hack to sub select the kings and its adjacent files
        openFiles &= fill<File>(kingMoveLookup[kingSq]);
        // Penalty for number of open files
        score -= 10 * bitCount(openFiles & bitMask<Rank>(a1));
    }

    return score;
}

Score Board::evalRooks(enum piece color) const {
    constexpr Score rookOpenFileBonus = 15;
    constexpr Score rookSemiOpenFileBonus = 10;

    u64 rooks = (color == white)
        ?              (_bitBoard[white] & _bitBoard[rook])
        : bitFlip<Rank>(_bitBoard[black] & _bitBoard[rook]);

    // Open and semi-open files
    u64 openFiles = ~fill<File>(_bitBoard[pawn]);
    u64 semiOpenFiles = ~fill<File>(_bitBoard[color] & _bitBoard[pawn]);

    Score score = 0;

    for (u64 sq = rooks; sq; sq &= sq - 1) {
        Index sqIndex = bitScanLS(sq);
        // Piece square table (accounts for rook on seventh bonus)
        score += PSQT[rook][sqIndex];

        // Add bonuses for semi-open and open files
        if (bitMask<Square>(sqIndex) & openFiles) {
            score += rookOpenFileBonus;
        } else if (bitMask<Square>(sqIndex) & semiOpenFiles) {
            score += rookSemiOpenFileBonus;
        }
    }

    return score;
}

// position fen 3rr1k1/pp3pp1/1qn2np1/8/3p4/PP1R1P2/2P1NQPP/R1B3K1 b - - 0 1
// position fen 1k3b1r/ppqn1p2/2p1r1pp/4P3/8/1PN2NQ1/1PP3PP/1K1RR3 w - - 0 1

// position fen r1bq1rk1/pp2p1bp/2np4/5p2/nP3P2/N1P2N2/2B3PP/R1B1QRK1 w - - 0 4
// position fen r1bq1rk1/pp2p1bp/2np4/5B2/nP3P2/N1P2N2/6PP/R1B1QRK1 b - - 0 4
Score Board::evaluate() const {

    constexpr Score maxMaterial = 8 * pieceValues[pawn]
        + 2 * (pieceValues[rook] + pieceValues[knight] + pieceValues[bishop])
        + pieceValues[queen];

    // Start score with material values
    Score whiteMaterial = evalMaterial(white);
    Score blackMaterial = evalMaterial(black);
    Score score = whiteMaterial - blackMaterial;

    // Add piece square table values of pieces without specialized eval function
    // Note: assumes left/right symmetry of the piece square tables
    for (enum piece type : { queen, bishop, knight }) {
        // White pieces
        for (u64 sq = _bitBoard[white] & _bitBoard[type]; sq; sq &= sq - 1) {
            score += PSQT[type][bitScanLS(sq)];
        }
        // and black pieces
        for (u64 sq = _bitBoard[black] & _bitBoard[type]; sq; sq &= sq - 1) {
            score -= PSQT[type][63 - bitScanLS(sq)];
        }
    }

    // Pawn structure
    score += evalPawns(white) - evalPawns(black);

    // Rooks
    score += evalRooks(white) - evalRooks(black);

    // First the white king
    if (blackMaterial <= 1200) {
        // Endgame:
        // No king safety, but more king mobility in the center
        score += PSQT[kingEG][bitScanLS(_bitBoard[white] & _bitBoard[king])];
    } else {
        // Middle Game:
        // King safety weighted by opponents material (The less pieces the enemy
        // possesses, the less important the king safety. In other words, with
        // fewer pieces, its harder to attack.)
        score += (5 * blackMaterial * evalKingSafety(white)) / (4 * maxMaterial);
    }
    // and the same for the black king with opposite sign
    if (whiteMaterial <= 1200) {
        score -= PSQT[kingEG][63 - bitScanLS(_bitBoard[black] & _bitBoard[king])];
    } else {
        score -= (5 * whiteMaterial * evalKingSafety(black)) / (4 * maxMaterial);
    }

    return score;
}

// Static Exchange Evaluation
Score Board::see(const Move move) const {
    // Only apply to captures
    assert(!!move && move.victim());

    // Target square
    Index sq = move.to();
    u64 to = bitMask<Square>(sq);
    u64 from = bitMask<Square>(move.from());
    // Set of all empty squares (without initial attack)
    u64 empty = from | ~(_bitBoard[white] | _bitBoard[black]);

    // Compute all attackers (of the target square)
    u64 attacker = (knightMoveLookup[sq] & _bitBoard[knight])
                 | (  kingMoveLookup[sq] & _bitBoard[king]);
    // add sliding pieces
    attacker |= fill7dump<Plus> (to, empty) & (_bitBoard[queen] | _bitBoard[rook]);
    attacker |= fill7dump<Cross>(to, empty) & (_bitBoard[queen] | _bitBoard[bishop]);
    // and pawns
    attacker |= _bitBoard[pawn] &
      ( ((shift<LeftUp>  (to) | shift<RightUp>  (to)) & _bitBoard[black])
      | ((shift<LeftDown>(to) | shift<RightDown>(to)) & _bitBoard[white]));
    // Remove initial attacker (perform given move)
    attacker &= ~empty;

    // Setup swap list (there are max 32 pieces)
    Score swapList[32];
    // move count in the capture sequence
    int ply = 1;

    // Setup initial capture (aka move to evaluate)
    swapList[0] = pieceValues[move.victim()];
    swapList[1] = pieceValues[move.piece()] - swapList[0];

    // Side to move piece set
    u64 side = _bitBoard[move.color()];
    // Perform alternating sequence of captures untill there are no attackers
    // of the current side to move left
    while (u64 candidate = (attacker & (side = ~side))) {
        // Get least valuable attacker piece from candidates
        enum piece piece = none;
        for (enum piece pieceType : {pawn, knight, bishop, rook, queen, king}) {
            // Skip untill the first candidate (garantied to exist)
            if ((from = (candidate & _bitBoard[pieceType]))) {
                piece = pieceType;
                from = from & -from;    // pick arbitrary first (TODO: find a better
                break;                  // solution! For now, quick and durty)
            }
        }

        // If a king attempts a capture, check for check
        if ((piece == king) && (attacker &~ side)) {
            break; // King would move into check -> no pieces left
        }

        // Append speculative score under the assumption beeing defended
        ply++;
        swapList[ply] = pieceValues[piece] - swapList[ply - 1];

        // perform piece movement
        attacker ^= from;
        empty ^= from;

        // Update attackers with discovered sliding pieces
        attacker |= fill7dump<Plus> (to, empty) & (_bitBoard[queen] | _bitBoard[rook]);
        attacker |= fill7dump<Cross>(to, empty) & (_bitBoard[queen] | _bitBoard[bishop]);
        attacker &= ~empty;
    }

    // Final SEE evaluation by dropping loosing sub-sequences (allow stand pat)
    // Note: Ignore last entry in swapList since the last entry corresponds to
    // the piece at the end of the capture sequence (not captures)
    while (--ply) {
        swapList[ply - 1] = -std::max(-swapList[ply - 1], swapList[ply]);
    }

    return swapList[0];
}
