use super::*;
use crate::cache::pawns::PHTable;
use crate::engine::stats::SearchStats;
use crate::state::representation::Board;
use crate::utils::assert_fast;
use crate::utils::bithelpers::BitHelpers;
use crate::utils::dev;
use std::cmp;
#[cfg(feature = "dev")]
use crate::tuning::tuner::TunerCoeff;
pub struct PawnsData {
    doubled_pawns: u8,
    isolated_pawns: u8,
    chained_pawns: u8,
    passed_pawns: u8,
    backward_pawns_open_file: u8,
    backward_pawns_closed_file: u8,
    opened_files: u8,
    pawn_shield: u8,
}
pub fn evaluate(board: &Board, phtable: &PHTable, stats: &mut SearchStats) -> PackedEval {
    match phtable.get(board.state.pawn_hash) {
        Some(entry) => {
            dev!(stats.phtable_hits += 1);
            return PackedEval::new(entry.score_opening, entry.score_ending);
        }
        None => {
            dev!(stats.phtable_misses += 1);
        }
    }
    let white_eval = evaluate_color(board, WHITE);
    let black_eval = evaluate_color(board, BLACK);
    let eval = white_eval - black_eval;
    phtable.add(board.state.pawn_hash, eval.get_opening(), eval.get_ending());
    dev!(stats.phtable_added += 1);
    eval
}
pub fn evaluate_without_cache(board: &Board) -> PackedEval {
    evaluate_color(board, WHITE) - evaluate_color(board, BLACK)
}
fn evaluate_color(board: &Board, color: usize) -> PackedEval {
    assert_fast!(color < 2);
    let mut result = PackedEval::default();
    let pawns_data = get_pawns_data(board, color);
    result += params::DOUBLED_PAWN[pawns_data.doubled_pawns.min(7) as usize];
    result += params::ISOLATED_PAWN[pawns_data.isolated_pawns.min(7) as usize];
    result += params::CHAINED_PAWN[pawns_data.chained_pawns.min(7) as usize];
    result += params::PASSED_PAWN[pawns_data.passed_pawns.min(7) as usize];
    result += params::BACKWARD_PAWN_OPEN_FILE[pawns_data.backward_pawns_open_file.min(7) as usize];
    result += params::BACKWARD_PAWN_CLOSED_FILE[pawns_data.backward_pawns_closed_file.min(7) as usize];
    result += params::PAWN_SHIELD[pawns_data.pawn_shield.min(7) as usize];
    result += params::PAWN_SHIELD_OPEN_FILE[pawns_data.opened_files.min(7) as usize];
    result
}
fn get_pawns_data(board: &Board, color: usize) -> PawnsData {
    assert_fast!(color < 2);
    let mut doubled_pawns = 0;
    let mut isolated_pawns = 0;
    let mut chained_pawns = 0;
    let mut passed_pawns = 0;
    let mut backward_pawns_open_file = 0;
    let mut backward_pawns_closed_file = 0;
    let mut pawn_shield = 0;
    let mut opened_files = 0;
    for file in ALL_FILES {
        let pawns_on_file = patterns::get_file(file) & board.pieces[color][PAWN];
        if pawns_on_file != 0 {
            let pawns_on_file_count = pawns_on_file.bit_count() as u8;
            if pawns_on_file_count > 1 {
                doubled_pawns += pawns_on_file_count - 1;
            }
            if (patterns::get_rail(file) & board.pieces[color][PAWN]) == 0 {
                isolated_pawns += 1;
            }
        }
    }
    let mut pawns_bb = board.pieces[color][PAWN];
    while pawns_bb != 0 {
        let square_bb = pawns_bb.get_lsb();
        let square = square_bb.bit_scan();
        pawns_bb = pawns_bb.pop_lsb();
        chained_pawns += ((patterns::get_front(color ^ 1, square) & patterns::get_diagonals(square) & board.pieces[color][PAWN]) != 0) as u8;
        passed_pawns += ((patterns::get_front(color, square) & board.pieces[color ^ 1][PAWN]) == 0) as u8;
        let offset = if color == WHITE { 8 } else { -8 };
        let stop_square_bb = if color == WHITE { square_bb << 8 } else { square_bb >> 8 };
        let front = patterns::get_front(color, square) & !patterns::get_file(square);
        let front_backward = patterns::get_front(color ^ 1, (square as i8 + offset) as usize) & !patterns::get_file((square as i8 + offset) as usize);
        let not_isolated = (front & board.pieces[color][PAWN]) != 0;
        let no_pawns_behind = (front_backward & board.pieces[color][PAWN]) == 0;
        let stop_square_attacked = (stop_square_bb & board.pawn_attacks[color ^ 1]) != 0;
        let open_file = (patterns::get_file(square) & board.pieces[color ^ 1][PAWN]) == 0;
        if not_isolated && no_pawns_behind && stop_square_attacked {
            if open_file {
                backward_pawns_open_file += 1;
            } else {
                backward_pawns_closed_file += 1;
            }
        }
    }
    let king_bb = board.pieces[color][KING];
    let king_square = king_bb.bit_scan();
    let king_square_file = (king_square & 7) as i8;
    pawn_shield = (patterns::get_box(king_square) & board.pieces[color][PAWN]).bit_count() as u8;
    for file in cmp::max(0, king_square_file - 1)..=(cmp::min(7, king_square_file + 1)) {
        if (patterns::get_file(file as usize) & board.pieces[color][PAWN]) == 0 {
            opened_files += 1;
        }
    }
    PawnsData { doubled_pawns, isolated_pawns, chained_pawns, passed_pawns, backward_pawns_open_file, backward_pawns_closed_file, pawn_shield, opened_files }
}
#[cfg(feature = "dev")]
pub fn get_coeffs(board: &Board, index: &mut u16, coeffs: &mut Vec<TunerCoeff>, indices: &mut Vec<u16>) {
    let white_pawns_data = get_pawns_data(board, WHITE);
    let black_pawns_data = get_pawns_data(board, BLACK);
    get_array_coeffs(white_pawns_data.doubled_pawns, black_pawns_data.doubled_pawns, 8, index, coeffs, indices);
    get_array_coeffs(white_pawns_data.isolated_pawns, black_pawns_data.isolated_pawns, 8, index, coeffs, indices);
    get_array_coeffs(white_pawns_data.chained_pawns, black_pawns_data.chained_pawns, 8, index, coeffs, indices);
    get_array_coeffs(white_pawns_data.passed_pawns, black_pawns_data.passed_pawns, 8, index, coeffs, indices);
    get_array_coeffs(white_pawns_data.backward_pawns_open_file, black_pawns_data.backward_pawns_open_file, 8, index, coeffs, indices);
    get_array_coeffs(white_pawns_data.backward_pawns_closed_file, black_pawns_data.backward_pawns_closed_file, 8, index, coeffs, indices);
    get_array_coeffs(white_pawns_data.pawn_shield, black_pawns_data.pawn_shield, 8, index, coeffs, indices);
    get_array_coeffs(white_pawns_data.opened_files, black_pawns_data.opened_files, 8, index, coeffs, indices);
}