from bitboard import *
from ldata import *
from pychess.Utils.const import *

#
# Caveat: Many functions in this module has very similar code. If you fix a
# bug, or write a perforance enchace, please update all functions. Apologies
# for the inconvenience
#

def isAttacked (board, cord, color):
    """ To determine if cord is attacked by any pieces from color. """
    pboards = board.boards[color]
    
    # Knights
    if pboards[KNIGHT] & moveArray[KNIGHT][cord]:
        return True
    
    rayto = fromToRay[cord]
    blocker = board.blocker
    
    # Bishops & Queens
    bisque = (pboards[BISHOP] | pboards[QUEEN]) & moveArray[BISHOP][cord]
    others = ~bisque & blocker
    for t in iterBits(bisque):
        # If there is a path and no other piece stand in our way
        ray = rayto[t]
        if ray and not ray & others:
            return True

    # Rooks & Queens
    rooque = (pboards[ROOK] | pboards[QUEEN]) & moveArray[ROOK][cord]
    others = ~rooque & blocker
    for t in iterBits(rooque):
        # If there is a path and no other piece stand in our way
        ray = rayto[t]
        if ray and not ray & others:
            return True
    
    # Pawns
    # Would a pawn of the opposite color, standing at out kings cord, be able
    # to attack any of our pawns?
    ptype = color == WHITE and BPAWN or PAWN
    if pboards[PAWN] & moveArray[ptype][cord]:
        return True
    
    # King
    if pboards[KING] & moveArray[KING][cord]:
        return True
    
    return False
    
def getAttacks (board, cord, color):
    """ To create a bitboard of pieces of color, which attacks cord """
    
    pieces = board.boards[color]
    
    # Knights
    bits = pieces[KNIGHT] & moveArray[KNIGHT][cord]
    
    # Kings
    bits |= pieces[KING] & moveArray[KING][cord]
    
    # Pawns
    bits |= pieces[PAWN] & moveArray[color == WHITE and BPAWN or PAWN][cord]
    
    rayto = fromToRay[cord]
    blocker = board.blocker
    
    # Bishops and Queens
    bisque = (pieces[BISHOP] | pieces[QUEEN]) & moveArray[BISHOP][cord]
    for c in iterBits(bisque):
        ray = rayto[c]
        if ray and not clearBit(ray & blocker, c):
            bits |= bitPosArray[c]
    
    # Rooks and queens
    rooque = (pieces[ROOK] | pieces[QUEEN]) & moveArray[ROOK][cord]
    for c in iterBits(rooque):
        ray = rayto[c]
        if ray and not clearBit(ray & blocker, c):
            bits |= bitPosArray[c]
    
    return bits

def getPieceMoves (board, cord, color, piece):
    """ To create a bitboard of specified pieces of color, which can move to
        cord """
    
    color = board.color
    pieces = board.boards[color]
    
    if piece == KNIGHT or piece == KING:
        return pieces[piece] & moveArray[piece][cord]
    
    rayto = fromToRay[cord]
    blocker = board.blocker
    
    if sliders[piece]:
        cords = pieces[piece] & moveArray[piece][cord]
        bits = 0
        for c in iterBits(cords):
            ray = rayto[c]
            if ray and not clearBit(ray & blocker, c):
                bits |= bitPosArray[c]
        return bits
    
    if piece == PAWN:
        pawns = pieces[PAWN]
        bits = pawns & moveArray[color == WHITE and BPAWN or PAWN][cord]
        bits |= pawns & bitPosArray[cord + (color == WHITE and -8 or 8)]
        if not blocker & bitPosArray[cord + (color == WHITE and -8 or 8)]:
            bits |= pawns & rankBits[color == WHITE and 1 or 6]
        return bits

def pinnedOnKing (board, cord, color):
    # Determine if the piece on cord is pinned against its colors king.
    # In chess, a pin is a situation in which a piece is forced to stay put
    # because moving it would expose a more valuable piece behind it to
    # capture.
    # Caveat: pinnedOnKing should only be called by genCheckEvasions().
    
    kingCord = board.kings[color]
    
    dir = directions[kingCord][cord]
    if dir == -1: return False

    opcolor = 1 - color
    blocker = board.blocker
 
    #  Path from piece to king is blocked, so no pin
    if clearBit(fromToRay[kingCord][cord], cord) & blocker:
       return False
    
    b = (rays[kingCord][dir] ^ fromToRay[kingCord][cord]) & blocker
    if not b: return False
    
    cord1 = cord > kingCord and firstBit (b) or lastBit (b)

    #  If diagonal
    if dir <= 3 and	bitPosArray[cord1] & \
            (board.boards[opcolor][QUEEN] | board.boards[opcolor][BISHOP]):
        return True
   
    #  Rank / file
    if dir >= 4 and bitPosArray[cord1] & \
            (board.boards[opcolor][QUEEN] | board.boards[opcolor][ROOK]):
        return True

    return False

def swapOff (board, tcord, swaplist = [], lastval = 0, ours = -1, theirs = -1):
    
    color = board.color
    opcolor = 1-color
    boards = board.boards[color]
    opboards = board.boards[opcolor]
    
    if ours == -1:
        ours = getAttacks (board, tcord, color)
    if theirs == -1:
        theirs = getAttacks (board, tcord, opcolor)
    
    while theirs:
        for piece in range(PAWN, KING+1):
            r = theirs & opboards[piece]
            if r:
                cord = firstBit(r)
                theirs = clearBit(theirs, cord)
                if xray[piece]:
                    ours, theirs = addXrayPiece (board, tcord, cord,
                                                 color, ours, theirs)
                swaplist.append(swaplist[-1] + lastval)
                lastval = PIECE_VALUES[piece]
                break
        
        if not ours: break
        for piece in range(PAWN, KING+1):
            r = ours & boards[piece]
            if r:
                cord = firstBit(r)
                ours = clearBit(ours, cord)
                if xray[piece]:
                    ours, theirs = addXrayPiece (board, tcord, cord,
                                                 color, ours, theirs)
                swaplist.append(swaplist[-1] + lastval)
                lastval = -PIECE_VALUES[piece]
                break
    
    #  At this stage, we have the swap scores in a list.  We just need to
    #  mini-max the scores from the bottom up to the top of the list.
    
    for n in range(len(swaplist)-1, -1, -1):
        if n & 1:
            if swaplist[n] <= swaplist[n-1]:
                swaplist[n-1] = swaplist[n] 
        else:
            if swaplist[n] >= swaplist[n-1]:
                swaplist[n-1] = swaplist[n] 
    
    return swaplist[0]

xray = (False, True, False, True, True, True, False)

def addXrayPiece (board, tcord, fcord, color, ours, theirs):
    """ This is used by swapOff.
    The purpose of this routine is to find a piece which attack through
    another piece (e.g. two rooks, Q+B, B+P, etc.) Color is the side attacking
    the square where the swapping is to be done. """
    
    dir = directions[tcord][fcord]
    a = rays[fcord][dir] & board.blocker
    if not a: return ours, theirs
    
    if tcord < fcord:
        ncord = firstBit(a)
    else: ncord = lastBit(a)
    
    piece = board.arBoard[ncord]
    if piece == QUEEN or (piece == ROOK and dir > 3) or \
                         (piece == BISHOP and dir < 4):
        bit = bitPosArray[ncord]
        if bit & board.friends[color]:
            ours |= bit
        else:
            theirs |= bit
    
    return ours, theirs

def defends (board, fcord, tcord):
    """ Could fcord attack tcord if the piece on tcord wasn't on the team of
        fcord?
        Doesn't test check. """
    
    if board.friends[WHITE] & bitPosArray[fcord]:
        color = WHITE
    else: color = BLACK
    opcolor = 1-color
    
    boards = board.boards[color]
    opboards = board.boards[opcolor]
    
    board.lock.acquire()
    try:
        # To see if we now defend the piece, we have to "give" it to the other team
        piece = board.arBoard[tcord]
        
        backup = boards[piece]
        opbackup = opboards[piece]
        
        boards[piece] &= notBitPosArray[tcord]
        opboards[piece] |= bitPosArray[tcord]
        board.friends[color] &= notBitPosArray[tcord]
        board.friends[opcolor] |= bitPosArray[tcord]
        
        # Can we "attack" the piece now?
        backupColor = board.color
        board.setColor(color)
        from lmove import newMove
        from validator import validateMove
        islegal = validateMove (board, newMove(fcord, tcord))
        board.setColor(backupColor)
        
        # Set board back
        boards[piece] = backup
        opboards[piece] = opbackup
        board.friends[color] |= bitPosArray[tcord]
        board.friends[opcolor] &= notBitPosArray[tcord]
    finally:
        board.lock.release()
    
    return islegal
