#include "bitboard.h"
#include "bitmagic.h"

// if defined, if captured piece value >= capturing piece value, see returns always a value >= 0
//#define USE_FAST_SEE

// SEE piece values
static const i8 pieceValues[] = {
    0, 2*1, 2*3, 2*3, 2*5, 2*10, 0
};

// return possibly attacking mask (at pos) (not including hidden attackers)
// note: includes pawns to kings!
u64 BitBoard::attDefMask( u8 pos, const u64 &mask ) const
{
    u64 res = 0;
    u64 msk;			// temporary mask

    const u64 &tm = ONESHL( pos );			// (pos) target mask

    // black pawns
    res |= (((tm >> 9) & R1MASK) | ((tm >> 7) & L1MASK)) & bits[ bpawn ];
    // white pawns
    res |= (((tm << 9) & L1MASK) | ((tm << 7) & R1MASK)) & bits[ wpawn ];
    // knights
    res |= (knightMov[ pos ] & (bits[ wknight ] | bits[ bknight ]));

    const BitFlags &f = flags();

    // black king
    if ( kproxTab[ pos ][ f.kingPos[ black ] ] )
        res |= ONESHL( f.kingPos[ black ] );

    // white king
    if ( kproxTab[ pos ][ f.kingPos[ white ] ] )
        res |= ONESHL( f.kingPos[ white ] );

    // now: try to extract diag/ortho sliders!
    u64 queens = bits[ wqueen ] | bits[ bqueen ];
    u64 diagSliders = (bits[ wbishop ] | bits[ bbishop ] | queens);
    u64 orthoSliders = (bits[ wrook ] | bits[ brook ] | queens);
#if 0
    i32 i;
    for (i=0; i<8; i++)
    {
        u64 dm = diagMask[i][ pos ];
        if ( !(dm &= mask) )
            continue;			// nothing here
        i8 bit = (i<4) ? getMSB(dm) : getLSB(dm);
        if ( bit >= 0 )
        {
            const u64 &tmp = ONESHL( bit );
            if ( tmp & ((i & 1) ? diagSliders : orthoSliders) )
            {
                res |= tmp/*ONESHL( bit )*/;		// no need to &mask (sliders already masked)
            }
        }
    }
#else
    res |= rookAttm( pos, mask ) & orthoSliders;
    res |= bishopAttm( pos, mask ) & diagSliders;
#endif

    return res & mask;
}

// new: from => to pos! (only considers bishops to queens)
u64 BitBoard::attDefMask( u8 pos, u8 from, const u64 &mask ) const
{
    u8 df = diagFlags[ from ][ pos ];
    if ( df & ~7 )
        return 0;
    u64 queens = bits[ wqueen ] | bits[ bqueen ];
    u64 sliders;
    if ( df & 1 )
    {
        // diagsliders
        sliders = (bits[ wbishop ] | bits[ bbishop ] | queens) & mask;
    }
    else
    {
        // orthosliders
        sliders = (bits[ wrook ] | bits[ brook ] | queens) & mask;
    }
    u64 dm = diagMask[ df ][ pos ];
    if ( !(dm &= mask) )
        return 0;
    i8 bit = (df<4) ? getMSB(dm) : getLSB(dm);
    return ONESHL( bit ) & sliders;		// no need to &mask (sliders already masked)
}

// TODO: verify, purge!
i32 BitBoard::see( const BitMove &bm, bool fast ) const
{
//	return 0;
    if ( bm.flags & (benpassant|bcastling) )
        return 0;								// not handled (yet)
    u8 f = bm.from;
    u8 t = bm.to;

    const u64 &tm = ONESHL( t );				// target mask

    const BitFlags &fl = flags();

    i8 who = pieces[f] >> 7;					// who captures
	ASSERT( who == ((bits[ wall ] & ONESHL( f )) ? white : black) );
    u8 capturingPiece = pieces[f] & 127;

    i32 value = pieceValues[ capturingPiece ];
    i32 score = f == t ? 0 : pieceValues[ pieces[t] & 127 ];
//#ifdef USE_FAST_SEE
    if ( fast && f != t && value <= score )
    {
        return score - value;
    }
//#endif

    u64 all = bits[ wall ] | bits[ ball ];

    all &= NONESHL( f );

    // now generate attack mask for these pieces
    // first-round pieces (attackers/defenders)
    u64 attdefs = attDefMask( t, all );

    if ( !(attdefs & bits[ ball - who ]) )
    {
        // no defenders -- simply return score!
        return score;
    }

    u64 msk;			// temp_mask

    i32 scores[64];
    i32 si = 0;
    scores[si++] = score;

    who ^= 1;
    for (;;)
    {
        i32 i;
        for (i=pawn; i<=king; i++)
        {
            msk = ( i == king ? ONESHL( fl.kingPos[who] ) : bits[ INDEX( who, i ) ] ) & attdefs;
            if ( msk )
            {
                i8 bit = getLSB( msk );	// LSB or MSB => doesn't matter
                ASSERT( bit >= 0 );
                u64 pmsk = ONESHL( bit );
                all ^= pmsk;			// clear_bit
                attdefs ^= pmsk;		// clear_bit

                // add hidden attdefs (if any)
                if ( i != knight && i != king )
                {
                    u64 newadm = attDefMask( t, bit, all );
                    if ( newadm )
                    {
                        attdefs |= newadm;
                        /*						if ( i == knight || i == king )
                        						{
                        							logFmt("");
                        						}*/
                    }
                }

				ASSERT( si < 64 );
                scores[si] = -scores[si-1] + value;
                si++;
                value = pieceValues[ i ];
                break;
            }
        }
        who ^= 1;
        bool nextLoop = (attdefs & bits[ wall + who ]);
        if ( i == king && nextLoop )
        {
            // king captured -> illegal -> stop!
			ASSERT( si < 64 );
#if 0
			char fen[256];
			getFEN( fen );
			printf("fen: %s\n", fen);
			BitBoard tb(*this);
			tb.dump();
			bm.describeSAN( tb, fen );
			printf("move: %s\n", fen );
#endif
			scores[si++] = 2*10;
            break;
        }
        if ( !nextLoop )
            break;
    }

    // negamax score list
    while ( --si > 0 )
    {
        scores[ si-1 ] = std::min( scores[ si-1 ], -scores[ si ] );
    }

    return scores[0];
}
