/*
    STB Generator
    Copyright (C) 2006 Gian-Carlo Pascutto
   
    File: moves.c
    Purpose: Move generation
*/

#include "global.h"
#include "squares.h"
#include "attacks.h"
#include "moves.h"
#include "board.h"

int in_check(state_s *s) {
    if (s->white_to_move) {
        return is_attacked(s, s->wking_loc, BLACK)
    } else {
        return is_attacked(s, s->bking_loc, WHITE)
    }
}

int check_legal(state_s *s, move_s *move) {            
    if (s->white_to_move) { 
        if (is_attacked (s, s->bking_loc, WHITE)) {
            return FALSE;
        } else {
            return TRUE;
        }
    } else { 
        if (is_attacked (s, s->wking_loc, BLACK)) {
            return FALSE;
        } else {
            return TRUE;
        }
    }
}

int movegen(state_s *s, move_s *moves) {  
    static const int knight_dirs[8] = { -25, -23, -14, -10, 10, 14, 23, 25 };
    static const int bishop_dirs[4] = { -13, -11, 11, 13 };
    static const int rook_dirs[4] = { -12, -1, 1, 12 };    
    
    int a, j, i;
    int from;    
    int dirs;             
    move_s *start = moves;    
    
    for (a = 1, j = 1; (a <= s->piece_count); j++) {
        i = s->pieces[j];
      
        if (!i)
	    continue;
        else
	    a++;

        from = i;        
                                         
        if (PIECET(s, from) == pawn) {
            if (PIECESIDE(s, from) == WHITE) {                
                if (s->board[from+12] == npiece) {                        
                    moves = push_pawn(s, moves, from, from+12, FALSE);
                                        
                    if (rank[from] == 2 && s->board[from+24] == npiece)
                        moves = push_pawn_simple (s, moves, from, from+24);                        
                }               
                if ((s->board[from+13]&1) == 0 && s->board[from+13] != frame)
                    moves = push_pawn (s, moves, from, from+13, FALSE);                
                if ((s->board[from+11]&1) == 0 && s->board[from+11] != frame)
                    moves = push_pawn(s, moves, from, from+11, FALSE);                
                if (s->ep_square == from+13)
                    moves = push_pawn(s, moves, from, from+13, TRUE);                
                else if (s->ep_square == from+11)
                    moves = push_pawn(s, moves, from, from+11, TRUE);
            } else {                
                if (s->board[from-12] == npiece) {                                            
                    moves = push_pawn(s, moves, from,from-12, FALSE);
                                        
                    if (rank[from] == 7 && s->board[from-24] == npiece)
                        moves = push_pawn_simple(s, moves, from,from-24);                    
                };                
                if ((s->board[from-13] & 1) == 1 && s->board[from-13] != npiece)
                    moves = push_pawn(s, moves, from, from-13, FALSE);                
                if ((s->board[from-11]&1) == 1 && s->board[from-11] != npiece)
                    moves = push_pawn(s, moves, from, from-11, FALSE);                
                if (s->ep_square == from-13)
                    moves = push_pawn(s, moves, from, from-13, TRUE);                
                else if (s->ep_square == from-11)
                    moves = push_pawn(s, moves, from, from-11, TRUE);                    
            }                
        } else if (PIECET(s, from) == knight) {
            for (dirs = 0; dirs < 8; dirs++)                
                moves = push_knight(s, moves, from, from+knight_dirs[dirs]);                
        } else if (PIECET(s, from) == bishop) {
            for (dirs = 0; dirs < 4; dirs++)
                moves = push_slide(s, moves, from, from+bishop_dirs[dirs]);                
        } else if (PIECET(s, from) == rook) {
            for (dirs = 0; dirs < 4; dirs++)
                moves = push_slide(s, moves, from, from+rook_dirs[dirs]);                
        } else if (PIECET(s, from) == queen) {
            for (dirs = 0; dirs < 4; dirs++)
                moves = push_slide(s, moves, from, from+bishop_dirs[dirs]);
            for (dirs = 0; dirs < 4; dirs++)
                moves = push_slide(s, moves, from, from+rook_dirs[dirs]); 
        } else if (PIECET(s, from) == king) {                
            for (dirs = 0; dirs < 4; dirs++)
                moves = push_king(s, moves, from, from+bishop_dirs[dirs]);
            for (dirs = 0; dirs < 4; dirs++)
                moves = push_king(s, moves, from, from+rook_dirs[dirs]);                                           
        }
    }

    return (int)(moves - start);
}

move_s* add_move(move_s *move,
              int Pfrom,
              int Ptarget,
	      int Ppromoted)
{
    move->from = Pfrom;
    move->target = Ptarget;
    move->captured = npiece;    
    move->promoted = Ppromoted;
    move->ep = FALSE;

    return ++move;	
}

move_s* add_capture(move_s *move,
                 int Pfrom,
                 int Ptarget,
		 int Pcaptured,
		 int Ppromoted,
		 int Pep)
{
    move->from = Pfrom;
    move->target = Ptarget;
    move->captured = Pcaptured;    
    move->promoted = Ppromoted;
    move->ep = Pep;
    
    return ++move;
}

move_s* push_king (state_s *s, move_s *move, int from, int target) {              
    if (s->board[target] == frame) {
        return move;
    }
        
    if (s->board[target] == npiece) {
        return add_move(move, from, target, 0);        
    } else if ((s->board[target]&1) != (s->board[from] & 1)) {
        return add_capture(move, from, target, s->board[target], 0, FALSE);    
    }
        
    return move;
}


move_s* push_knight(state_s *s, move_s *move, int from, int target) {    
    if (s->board[target] == frame) {
        return move;
    }
        
    if (s->board[target] == npiece) {    
        return add_move(move, from, target, 0);
    } else if ((s->board[target]&1) != (s->board[from] & 1)) {    
        return add_capture(move, from, target, s->board[target], 0, FALSE);
    }
       
    return move;
}


move_s* push_pawn (state_s *s, move_s *move, int from, int target, int is_ep) {    
    int captured_piece;
        
    if (is_ep) {
        if (s->board[from] == wpawn) {          
            return add_capture(move, from, target, bpawn, 0, TRUE);
        }
        else {          
            return add_capture(move, from, target, wpawn, 0, TRUE);
        }
    }
        
    captured_piece = s->board[target];
        
    if (s->board[from] == wpawn && rank[from] == 7) {
        add_capture(move, from, target, captured_piece, wqueen, FALSE);
        add_capture(move+1, from, target, captured_piece, wrook, FALSE);
        add_capture(move+2, from, target, captured_piece, wbishop, FALSE);           
        return add_capture(move+3, from, target, captured_piece, wknight, FALSE);
    } else if (s->board[from] == bpawn && rank[from] == 2) {
        add_capture(move, from, target, captured_piece, bqueen, FALSE);
        add_capture(move+1, from, target, captured_piece, brook, FALSE);
        add_capture(move+2, from, target, captured_piece, bbishop, FALSE);          
        return add_capture(move+3, from, target, captured_piece, bknight, FALSE);
    } else {    
        return add_capture(move, from, target, captured_piece, 0, FALSE);
    }        
}

move_s* push_pawn_simple(state_s *s, move_s *move, int from, int target) {    
    return add_move(move, from, target, 0);
}

move_s* push_slide(state_s *s, move_s *move, int from, int target) {    
    int offset;
    int mycolor;  
        
    if (s->board[target] == frame) {
        return move;
    }
        
    offset = target - from;
    mycolor = s->board[from] & 1;
        
    do {       
        if (s->board[target] == npiece) {          
            add_move(move, from, target, 0);          
            move++;          
            target += offset;
        } else if ((s->board[target]&1) != mycolor) {
            add_capture(move, from, target, s->board[target], 0, FALSE);
            move++;
            break;
        } else {
            break;
        }
    } while (s->board[target] != frame);
        
    return move;    
}

