// Copyright 1996 by Jon Dart.  All Rights Reserved.

#include "stdafx.h"
#include "attacke.h"
#include "bearing.h"
#include "util.h"
#include "constant.h"
#include "debug.h"

static void addXRay(const Board &board, Bitmap & attacks,Square attack_square,
  Square square,ColorType side)
{
    int dir = Board::Direction(attack_square,square);
    if (dir == 0)
       return;
    const int r = attack_square.Rank(Black)-1;
    const int f = attack_square.File()-1;
    switch(dir)
    {
       case 1:
       {
         // Find attacks on the rank
         // shift the occupied bits so that the rank that 
         // attack_square occupies is in bits 0-7.
#ifdef FAST_SHIFT
         byte atcks = (FAST_SHIFT_R(board.occupied[side],r<<3)).byte_value();
#else
         byte atcks = Bitmap::shr(board.occupied[side],(r<<3)).byte_value();
#endif
         // find the next piece on the rank
         Bitmap occ((uint64)Bearing::next_bit_left[atcks][f]);
         // shift back up
#ifdef FAST_SHIFT
         occ.shl_byte(r);
#else
         occ.shl(r<<3);
#endif         
         // See if any rooks or queens are there
         occ.and(board.rooksqueens[side]);
         attacks.or(occ);
       }
       break;
       case -1:
       {
         // Find attacks on the rank
         // shift the occupied bits so that the rank that 
         // attack_square occupies is in bits 0-7.
#ifdef FAST_SHIFT
         byte atcks = (FAST_SHIFT_R(board.occupied[side],(r<<3))).byte_value();
#else
         byte atcks = Bitmap::shr(board.occupied[side],(r<<3)).byte_value();
#endif
         // find the next piece on the rank
         Bitmap occ((uint64)Bearing::next_bit_right[atcks][f]);
         // shift back up
#ifdef FAST_SHIFT
         occ.shl_byte(r);
#else
         occ.shl(r<<3);
#endif         
         // See if any rooks or queens are there
         occ.and(board.rooksqueens[side]);
         attacks.or(occ);
       }
       break;
       case 8:
       {
          Bitmap occ(board.occupiedR90[side]);
          // shift the occupied bits so that the file that sq occupies are
          // in bits 0-7.
          int shift = (7-f)<<3;
#ifdef FAST_SHIFT          
          byte atcks = (FAST_SHIFT_R(occ,shift)).byte_value();
#else
          byte atcks = Bitmap::shr(occ,shift).byte_value();
#endif          
          // find the pieces nearest to sq on the file, then
          // "unrotate" the result and shift it back up
          // See if any rooks or queens are there
          attacks.or(Bitmap::and(Bearing::unrot90[Bearing::next_bit_left[atcks][r]][7-f],board.rooksqueens[side]));
        }
        break;
        case -8:
        {
          Bitmap occ(board.occupiedR90[side]);
          // shift the occupied bits so that the file that sq occupies are
          // in bits 0-7.
          int shift = (7-f)<<3;
#ifdef FAST_SHIFT
          byte atcks = (FAST_SHIFT_R(occ,shift)).byte_value();
#else
          byte atcks = Bitmap::shr(occ,(7-f)<<3).byte_value();
#endif          
          // find the pieces nearest to sq on the file, then
          // "unrotate" the result and shift it back up
          // See if any rooks or queens are there
          attacks.or(Bitmap::and(Bearing::unrot90[Bearing::next_bit_right[atcks][r]][7-f],board.rooksqueens[side]));
        }
        break;
        case 7:
        {
          Bitmap occ(board.occupiedDa1[side]);
          // Shift so that the relevant bits are in bits 0-7 of the bitmap.
          // Note that we also need to mask out only those bits that are in
          // the diagonal occupied by 'sq'.
          int shift = Bitmap::da1[attack_square]>>3;
#ifdef FAST_SHIFT
          int val = (FAST_SHIFT_R(occ,8*shift)).byte_value()
              & Bitmap::da1_mask[attack_square];
#else
          int val = Bitmap::and(Bitmap::shr(occ,8*shift),Bitmap::da1_mask[attack_square]).byte_value();
#endif
          // find the pieces nearest to sq on the diagonal    
          int x = Bitmap::da1[attack_square] % 8;
          // Now we need to unrotate the bits
          Bitmap result(Bearing::unrotda1[Bearing::next_bit_left[val][x]][shift]);
          // See if any bishops or queens are there
          result.and(board.bishopsqueens[side]);
          attacks.or(result);
        }
        break;
        case -7:
        {
          Bitmap occ(board.occupiedDa1[side]);
          // Shift so that the relevant bits are in bits 0-7 of the bitmap.
          // Note that we also need to mask out only those bits that are in
          // the diagonal occupied by 'sq'.
          int shift = Bitmap::da1[attack_square]>>3;
#ifdef FAST_SHIFT
          int val = (FAST_SHIFT_R(occ,8*shift)).byte_value()
              & Bitmap::da1_mask[attack_square];
#else
          int val = Bitmap::and(Bitmap::shr(occ,8*shift),Bitmap::da1_mask[attack_square]).byte_value();
#endif
          // find the pieces nearest to sq on the diagonal    
          int x = Bitmap::da1[attack_square] % 8;
          Bitmap result(Bearing::unrotda1[Bearing::next_bit_right[val][x]][shift]);
          // See if any bishops or queens are there
          result.and(board.bishopsqueens[side]);
          attacks.or(result);
        }
        break;
        case 9:
        {
          Bitmap occ(board.occupiedDa8[side]);
          // Shift so that the relevant bits are in bits 0-7 of the bitmap.
          // Note that we also need to mask out only those bits that are in
          // the diagonal occupied by 'sq'.
          int shift = Bitmap::da8[attack_square]>>3;
#ifdef FAST_SHIFT
           int val = (FAST_SHIFT_R(occ,8*shift)).byte_value() &
             Bitmap::da8_mask[attack_square];
#else
           int val = Bitmap::and(Bitmap::shr(occ,8*shift),Bitmap::da8_mask[attack_square]).byte_value();
#endif
          // find the pieces nearest to sq on the diagonal    
          int x = Bitmap::da8[attack_square] % 8;
          // Now we need to unrotate the bits
          Bitmap result(Bearing::unrotda8[Bearing::next_bit_left[val][x]][shift]);
          // See if any bishops or queens are there
          result.and(board.bishopsqueens[side]);
          attacks.or(result);
        }
        break;
        case -9:
        {
          Bitmap occ(board.occupiedDa8[side]);
          // Shift so that the relevant bits are in bits 0-7 of the bitmap.
          // Note that we also need to mask out only those bits that are in
          // the diagonal occupied by 'sq'.
          int shift = Bitmap::da8[attack_square]>>3;
#ifdef FAST_SHIFT
           int val = (FAST_SHIFT_R(occ,8*shift)).byte_value() &
             Bitmap::da8_mask[attack_square];
#else
           int val = Bitmap::and(Bitmap::shr(occ,8*shift),Bitmap::da8_mask[attack_square]).byte_value();
#endif
          // find the pieces nearest to sq on the diagonal    
          int x = Bitmap::da8[attack_square] % 8;
          // Now we need to unrotate the bits
          Bitmap result(Bearing::unrotda8[Bearing::next_bit_right[val][x]][shift]);
          result.and(board.bishopsqueens[side]);
          // See if any bishops or queens are there
          attacks.or(result);
        }
        break;
       default:  
       break;
    }
}

int attack_estimate( const Board &board, const ExtendedMove &emove )
{
    ASSERT(!emove.IsNull());
    ColorType my_side = emove.PieceMoved().Color();
    ColorType side = my_side;
    Square square = (emove.Special() == ExtendedMove::EnPassant) ?
         board.EnPassantSq(OppositeColor(side)) : emove.DestSquare();
    Square attack_square = emove.StartSquare();
    Piece on_square = board[square];
    ASSERT(!on_square.IsEmpty());
    Piece attacker = board[attack_square];
    ASSERT(!attacker.IsEmpty());
    int score_count = 0;
    int score_list[20];
    int swap_score = 0;
    int gain;
    Bitmap opp_attacks(board.calc_attacks(square,OppositeColor(side)));
    if (opp_attacks.is_clear()) // piece is undefended
    {
       if (emove.Special() == ExtendedMove::Promotion)
          return on_square.Value() + Piece::Value(emove.PromoteTo()) - 64;
       else
          return on_square.Value();
    }
    Bitmap my_attacks(board.calc_attacks(square,side));
    my_attacks.clear(attack_square); // remove attacking piece
    addXRay(board,my_attacks,attack_square,square,side);
    int count = 0;
    for (;;)
    {
        attacker = board[attack_square];
        /**
        cout << " " << Piece::Image(attacker.Type())
             << " on " <<
             attack_square.FileImage() <<
             attack_square.RankImage() <<
             " takes " << Piece::Image(on_square.Type())
             << endl;
        **/
        gain = on_square.Value();
	if (attacker.Type() == Piece::Pawn &&
	    square.Rank(side) == 8)
	{
            if (count == 0) // initial capture is a promotion too
            {
              gain += Piece::Value(emove.PromoteTo()) - 64;
              on_square = Piece(emove.PromoteTo(),side);
            }
            else
            {
   	      gain += 576-64; //Piece::Value(Piece::Queen) - Piece::Value(Piece::Pawn);
              on_square = Piece(Piece::Queen,side);
            }
	}
        else
   	    on_square = attacker;
	if (side == my_side)
	    swap_score += gain;
	else
	    swap_score -= gain;
        //if (score_count > 15)
        //    printf("bad score count\n");
	ASSERT(score_count < 20);
	score_list[score_count++] = swap_score;
	side = OppositeColor(side);
    	if (side == my_side)
        {
	   attack_square = board.min_attacker(my_attacks,side).FirstOne();
           if (attack_square.IsInvalid()) 
               break;
           else
           {
               my_attacks.clear(attack_square);
               addXRay(board,my_attacks,attack_square,square,side);
           }
        }
        else
        {
	   attack_square = board.min_attacker(opp_attacks,side).FirstOne();
           if (attack_square.IsInvalid()) 
               break;
           else
           {
               opp_attacks.clear(attack_square);
               addXRay(board,opp_attacks,attack_square,square,side);
           }
        }
        ++count;
    }
    if (score_count > 1 && score_count % 2 == 0)
    {
        // last capture is by other side, can't avoid it
	score_list[score_count-2] = score_list[score_count-1];
	--score_count; 
    }
    int i = score_count;
    while (i >= 3)
    {
	int max_score = Util::Max(score_list[i-1],score_list[i-2]);
	if (max_score < score_list[i-3])
	    score_list[i-3] = max_score;
	i -= 2;
    }
    return score_list[0];
}

BOOL en_prise( const Board &board, const Square sq )
{
//    ASSERT(!emove.IsNull());
    ColorType my_side = board[sq].Color();
    ColorType side = my_side;
    Square square(sq);
//    Square attack_square = emove.StartSquare();
    Piece on_square = board[square];
    ASSERT(!on_square.IsEmpty());
//    Piece attacker = board[attack_square];
//    ASSERT(!attacker.IsEmpty());
    int score_count = 0;
    int score_list[20];
    int swap_score = 0;
    int gain;
    Bitmap opp_attacks(board.calc_attacks(square,OppositeColor(side)));
    if (opp_attacks.is_clear()) // piece is not under attack
      return FALSE;
    Bitmap my_attacks(board.calc_attacks(square,side));
    if (my_attacks.is_clear()) // we are not defended
      return TRUE;
    Square attack_square = board.min_attacker(opp_attacks,side).FirstOne();
    opp_attacks.clear(attack_square);
    addXRay(board,opp_attacks,attack_square,square,side);
    side = OppositeColor(side);
    Piece attacker;
    for (;;)
    {
        attacker = board[attack_square];
        /**
        cout << " " << Piece::Image(attacker.Type())
             << " on " <<
             attack_square.FileImage() <<
             attack_square.RankImage() <<
             " takes " << Piece::Image(on_square.Type())
             << endl;
        **/
        gain = on_square.Value();
	if (attacker.Type() == Piece::Pawn &&
	    square.Rank(side) == 8)
	{
	    gain += 576-64; //Piece::Value(Piece::Queen) - Piece::Value(Piece::Pawn);
	}
	if (side == my_side)
	    swap_score += gain;
	else
	    swap_score -= gain;
        //if (score_count > 15)
        //    printf("bad score count\n");
	ASSERT(score_count < 20);
	score_list[score_count++] = swap_score;
	side = OppositeColor(side);
	on_square = attacker;
    	if (side == my_side)
        {
	   attack_square = board.min_attacker(my_attacks,side).FirstOne();
           if (attack_square.IsInvalid()) 
               break;
           else
           {
               my_attacks.clear(attack_square);
               addXRay(board,my_attacks,attack_square,square,side);
           }
        }
        else
        {
	   attack_square = board.min_attacker(opp_attacks,side).FirstOne();
           if (attack_square.IsInvalid()) 
               break;
           else
           {
               opp_attacks.clear(attack_square);
               addXRay(board,opp_attacks,attack_square,square,side);
           }
        }
    }
    if (score_count > 1 && score_count % 2 == 0)
    {
        // last capture is by other side, can't avoid it
	score_list[score_count-2] = score_list[score_count-1];
	--score_count; 
    }
    int i = score_count;
    while (i >= 3)
    {
	int max_score = Util::Max(score_list[i-1],score_list[i-2]);
	if (max_score < score_list[i-3])
	    score_list[i-3] = max_score;
	i -= 2;
    }
    return score_list[0] < 0;
}
