use rand::{rngs::StdRng,SeedableRng,Rng};

use crate::{square::Square, bitboard::BitboardFns};

use super::{Board, Color, Piece, CastlingSide};


const TOTAL_PIECES:          usize = 12;
const TOTAL_SQUARES:         usize = 64;
const TOTAL_FILES:           usize = 8;
const TOTAL_CASTLING_RIGHTS: usize = 4;
const ZOBRIST_SEED_SIZE:     usize = TOTAL_SQUARES * TOTAL_PIECES + TOTAL_CASTLING_RIGHTS + TOTAL_FILES + 1;

pub type ZobristSeed = [u64; ZOBRIST_SEED_SIZE];

pub trait Zobrist {
    fn seed() -> ZobristSeed;

    /// Compute store zobrist hash of the current position
    /// https://www.chessprogramming.org/Zobrist_Hashing
    fn compute_hash(&mut self);

    fn zobrist_toggle_piece(&mut self, piece_type: Piece, square: Square);
    fn zobrist_toggle_castling_right(&mut self, color: Color, side: CastlingSide);
    fn zobrist_toggle_ep_square(&mut self, ep_square: Square);
    fn zobrist_toggle_color(&mut self);
}

impl Zobrist for Board {
    fn seed() -> ZobristSeed {
        let mut rng = StdRng::seed_from_u64(228);
        [(); ZOBRIST_SEED_SIZE].map(|_| rng.gen())
    }

    fn compute_hash(&mut self) {
        self.hash = 0;

        for piece_id in 0..self.piece_sets.len() {
            for square in self.piece_sets[piece_id].serialize() {
                self.zobrist_toggle_piece(Piece::from(piece_id), square);
            }
        }
        for color in [Color::White, Color::Black] {
            for castle_side in [CastlingSide::King, CastlingSide::Queen] {
                if self.castling_rights[color as usize][castle_side as usize] {
                    self.zobrist_toggle_castling_right(color, castle_side)
                }
            }
        }

        if let Some(square) = self.ep_target {
            self.zobrist_toggle_ep_square(square);
        }

        if self.color() == Color::Black {
            self.zobrist_toggle_color();
        }
    }

    fn zobrist_toggle_piece(&mut self, piece_type: Piece, square: Square) {
        self.hash ^= self.zobrist_seed[piece_type as usize * TOTAL_SQUARES + square as usize];
    }

    fn zobrist_toggle_castling_right(&mut self, color: Color, side: CastlingSide) {
        self.hash ^= self.zobrist_seed[(TOTAL_PIECES * TOTAL_SQUARES) + color as usize * 2 + side as usize];
    }

    fn zobrist_toggle_ep_square(&mut self, ep_square: Square) {
        self.hash ^= self.zobrist_seed[(TOTAL_PIECES * TOTAL_SQUARES + TOTAL_CASTLING_RIGHTS) + ep_square.file() as usize];
    }

    fn zobrist_toggle_color(&mut self) {
        self.hash ^= self.zobrist_seed[ZOBRIST_SEED_SIZE - 1];
    }
}