//! 局面履歴管理
//!
//! [`StateInfo`] は `apply_move32` 時に生成される差分情報を保持し、
//! `undo_move32` で局面を完全に復元するために使用されます。
//!
//! # 保持する情報
//!
//! - 取られた駒（undo 時の復元用）
//! - Zobrist ハッシュキー（盤面・持ち駒）
//! - 千日手検出用カウンタ
//! - 王手駒のキャッシュ（[`checkers`](StateInfo::checkers)）
//! - ピン情報（[`pinners`](StateInfo::pinners), [`blockers_for_king`](StateInfo::blockers_for_king)）
//! - 王手候補マス（[`check_squares`](StateInfo::check_squares)）
//!
//! [`StateStack`] は `StateInfo` のスタックで、局面ツリーの
//! 探索中に履歴を管理します。

#![allow(clippy::inline_always)]

use super::position::{Ply, Position};
use crate::board::zobrist::ZobristKey;
use crate::types::Bitboard;
use crate::types::{Color, Hand, Move32, Piece, PieceType, RepetitionState};
use std::ops::{Index, IndexMut};

/// `StateInfo` 内でのスタック位置を表す型
pub type StateIndex = usize;

// ANCHOR: state_info_struct
/// 局面の履歴情報を保持する構造体
///
/// `apply_move32` 時の差分情報を記録し、`undo_move32` で完全に復元できるようにする。
/// また、千日手検出や王手情報のキャッシュも管理する。
#[derive(Clone, Debug, Default)]
pub struct StateInfo {
    /// 取られた駒（なければ `Piece::NONE` ）
    pub captured: Piece,

    /// 盤面Zobrist（board_key）
    pub board_key: ZobristKey,

    /// 持ち駒Zobrist（hand_key）
    pub hand_key: ZobristKey,

    /// 同一局面の登場回数（千日手検出用）
    pub repetition_counter: i32,

    /// 同一局面までの距離（千日手検出用）
    /// 4回目以降の繰り返しは負の値で表す。
    pub repetition_distance: i32,

    /// 同一局面の繰り返し回数 - 1
    pub repetition_times: i32,

    /// null moveからの手数
    pub plies_from_null: Ply,

    /// 王手をかけている駒の集合（高速化のためキャッシュ）
    pub checkers: Bitboard,

    /// 色別にピンしている敵の大駒
    pub pinners: [Bitboard; Color::COUNT],

    /// 色別の玉を守っているブロッカー集合
    pub blockers_for_king: [Bitboard; Color::COUNT],

    /// 各駒種について、その駒を配置すると敵玉に王手となるマスのビットボード
    /// YaneuraOu互換: MovePicker の check bonus 計算に使用
    pub check_squares: [Bitboard; PieceType::COUNT],

    /// 連続王手カウンタ（[先手, 後手]）
    /// 王手でない手を指すと0にリセットされる
    pub continuous_check: [u16; 2],

    /// 現局面の手番側の持ち駒
    pub hand: Hand,

    /// 千日手の状態（キャッシュ用）
    pub repetition_type: RepetitionState,

    /// 直前の指し手
    pub last_move: Move32,

    /// 直前に動かした駒種
    pub last_moved_piece_type: PieceType,
}
// ANCHOR_END: state_info_struct

impl StateInfo {
    /// 新しい空の `StateInfo` を作成
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// 探索開始時のリセット（ゼロクリア）
    pub fn reset_sfen(&mut self) {
        *self = Self::default();
    }

    /// 捕獲された駒を取得
    #[must_use]
    pub fn captured(&self) -> Option<Piece> {
        if self.captured == Piece::NONE {
            None
        } else {
            Some(self.captured)
        }
    }
}

// ANCHOR: state_stack_struct
/// 可変長の `StateInfo` スタック
///
/// YaneuraOu の `StateList` に合わせて動的に拡張する。
#[derive(Debug)]
pub struct StateStack {
    /// `StateInfo` のリスト
    entries: Vec<StateInfo>,

    /// 現在のスタックトップのインデックス
    head: StateIndex,
}
// ANCHOR_END: state_stack_struct

impl StateStack {
    /// 新しい `StateStack` を作成
    #[must_use]
    pub fn new() -> Self {
        let mut entries = Vec::with_capacity(128);
        entries.push(StateInfo::default());
        Self { entries, head: 0 }
    }

    /// 探索開始時のリセット（headを0に戻すだけで内容はクリアしない）
    pub fn reset_sfen(&mut self) {
        self.head = 0;
        self.entries.truncate(1);
        self.entries[0].reset_sfen();
    }

    /// 局面に同期した初期StateInfoを設定する
    pub fn reset_with_position(&mut self, pos: &mut Position) {
        self.reset_sfen();
        {
            let state = self.current_mut();
            state.board_key = pos.board_key;
            state.hand_key = pos.hand_key;
            pos.compute_caches_for_state(state);
            state.hand = pos.hand(pos.turn());
        }
        pos.st_index = self.current_index();
    }

    /// 現在の（最新の）`StateInfo` への参照を取得
    #[must_use]
    #[inline(always)]
    pub fn current(&self) -> &StateInfo {
        &self.entries[self.head]
    }

    /// 現在の`StateInfo`のインデックスを取得
    #[must_use]
    #[inline(always)]
    pub const fn current_index(&self) -> StateIndex {
        self.head
    }

    /// 現在の（最新の）`StateInfo` への可変参照を取得
    #[inline(always)]
    pub fn current_mut(&mut self) -> &mut StateInfo {
        &mut self.entries[self.head]
    }

    /// 新しい空の `StateInfo` をプッシュし、そのインデックスを返す
    /// 呼び出し側で内容を埋める責任がある
    #[inline(always)]
    pub fn push_empty(&mut self) -> StateIndex {
        self.head += 1;
        let head = self.head;

        if head == self.entries.len() {
            self.entries.push(StateInfo::default());
        }

        let entry = &mut self.entries[head];
        entry.reset_sfen();

        self.head
    }

    /// 現在の`StateInfo`を複製して新しいエントリをプッシュする
    ///
    /// YaneuraOu の apply_move32 相当の memcpy 動作に合わせるために用いる。
    #[inline(always)]
    pub fn push_clone_from_prev(&mut self) -> StateIndex {
        let prev_index = self.head;
        self.head += 1;
        let head = self.head;

        let prev_entry = self.entries[prev_index].clone();
        if head == self.entries.len() {
            self.entries.push(prev_entry);
        } else {
            self.entries[head] = prev_entry;
        }

        self.head
    }

    /// apply_move32 用に、必要なフィールドだけをコピーして新しいエントリをプッシュする
    ///
    /// YaneuraOu の do_move でコピーされる範囲（`offsetof(StateInfo, board_key)` まで）に合わせて
    /// 最小限のフィールドを引き継ぐ。残りのフィールドは呼び出し側で全て上書きされるため、
    /// ゼロクリア（`..StateInfo::default()`）を行わない。
    #[inline(always)]
    pub fn push_for_move(&mut self) -> StateIndex {
        let prev_index = self.head;
        self.head += 1;
        let head = self.head;

        // 前の局面から引き継ぐフィールドを先に読み出す（借用の衝突回避）
        let prev_plies = self.entries[prev_index].plies_from_null;
        let prev_check = self.entries[prev_index].continuous_check;

        if head == self.entries.len() {
            // 初回到達深度のみ新しいエントリを追加
            self.entries.push(StateInfo::default());
        }

        // 既存エントリを再利用し、引き継ぎフィールドのみ設定
        // 残りのフィールドは apply_move32_with_gives_check_for で全て上書きされる
        let entry = &mut self.entries[head];
        entry.plies_from_null = prev_plies;
        entry.continuous_check = prev_check;

        self.head
    }

    /// スタックをポップし、ポップしたエントリのインデックスを返す
    /// スタックが空（ルート局面のみ）の場合はNone
    #[inline(always)]
    pub fn pop(&mut self) -> Option<StateIndex> {
        if self.head == 0 {
            return None;
        }

        let popped = self.head;
        self.head = popped - 1;
        Some(popped)
    }

    /// スタックをポップし、ポップしたエントリのインデックスを返す（境界チェックなし）。
    ///
    /// # Safety
    /// - `head > 0` が成り立つこと。
    #[inline(always)]
    pub unsafe fn pop_unchecked(&mut self) -> StateIndex {
        let popped = self.head;
        self.head = popped - 1;
        popped
    }

    /// 現在の深さ（スタックに積まれている `StateInfo` の数）を取得
    #[must_use]
    pub const fn depth(&self) -> usize {
        self.head
    }
}

impl Index<StateIndex> for StateStack {
    type Output = StateInfo;

    fn index(&self, idx: StateIndex) -> &Self::Output {
        &self.entries[idx]
    }
}

impl IndexMut<StateIndex> for StateStack {
    fn index_mut(&mut self, idx: StateIndex) -> &mut Self::Output {
        &mut self.entries[idx]
    }
}

impl Clone for StateStack {
    fn clone(&self) -> Self {
        Self { entries: self.entries.clone(), head: self.head }
    }
}

impl Default for StateStack {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests;
