//! 将棋盤面の管理
//!
//! このモジュールは [`Position`] 構造体を中心に、将棋の盤面状態を管理します。
//!
//! # Position
//!
//! `Position` は以下の情報を保持します：
//!
//! - 盤面の駒配置（81マス）
//! - 持ち駒（先手・後手）
//! - 手番
//! - 手数
//! - Zobrist ハッシュキー
//! - 王手情報のキャッシュ
//!
//! # 局面の構築
//!
//! ```
//! use rshogi::board::{self, Position, position_from_sfen, hirate_position};
//!
//! board::init();
//!
//! // 平手初期局面
//! let pos = hirate_position();
//!
//! // SFEN から構築
//! let pos = position_from_sfen(
//!     "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1"
//! ).unwrap();
//! ```
//!
//! # 指し手の適用と取り消し
//!
//! ```
//! use rshogi::board::{self, Position};
//! use rshogi::types::Move;
//!
//! board::init();
//! let mut pos = rshogi::board::hirate_position();
//!
//! // 指し手を適用
//! let mv16 = Move::from_usi("7g7f").unwrap();
//! let m = pos.move32_from_move(mv16);
//! pos.apply_move32(m);
//!
//! // 局面が更新される
//! assert_eq!(pos.game_ply(), 2);
//!
//! // 指し手を取り消し
//! pos.undo_move32(m);
//! assert_eq!(pos.game_ply(), 1);
//! ```
//!
//! # 局面の状態取得
//!
//! ```
//! use rshogi::board::{self, Position};
//! use rshogi::types::{Color, Square, PieceType};
//!
//! board::init();
//! let pos = rshogi::board::hirate_position();
//!
//! // 手番
//! assert_eq!(pos.turn(), Color::BLACK);
//!
//! // 特定のマスの駒
//! let sq = Square::from_file_rank(
//!     rshogi::types::File::FILE_5,
//!     rshogi::types::Rank::RANK_1
//! );
//! let piece = pos.piece_on(sq);
//! assert_eq!(piece.piece_type(), PieceType::KING);
//!
//! // 王手されているか
//! assert!(!pos.is_in_check());
//! ```
//!
//! # Zobrist ハッシュ
//!
//! 局面の同一性判定やトランスポジションテーブルで使用します。
//!
//! ```
//! use rshogi::board::{self, Position};
//!
//! board::init();
//! let pos1 = rshogi::board::hirate_position();
//! let pos2 = rshogi::board::hirate_position();
//!
//! // 同じ局面は同じハッシュキー
//! assert_eq!(pos1.key(), pos2.key());
//! ```
//!
//! # PackedSfen
//!
//! 局面をコンパクトなバイナリ形式で表現します。
//! cshogi の `PackedSfen` と互換性があります。
//!
//! ```
//! use rshogi::board::{self, Position, PackedSfen};
//!
//! board::init();
//! let pos = rshogi::board::hirate_position();
//!
//! // PackedSfen に変換
//! let psfen = pos.to_packed_sfen();
//!
//! // PackedSfen から復元
//! let mut pos2 = Position::empty();
//! pos2.set_packed_sfen(&psfen, false, pos.game_ply()).unwrap();
//! assert_eq!(pos.to_sfen(None), pos2.to_sfen(None));
//! ```

mod accessors;
mod attacks;
mod cache;
mod constructors;
mod legality;
mod maintenance;
mod packed_sfen;
mod parser;
mod rules;

mod types;
mod updates;
mod hash;
mod validation;

use super::bitboard_set::BitboardSet;
use crate::board::zobrist::ZobristKey;
use crate::board::StateIndex;
use crate::types::{Color, EnteringKingRule, File, Hand, Piece, Rank, Square};
use std::fmt;

use crate::board::StateStack;
#[cfg(test)]
use crate::types::Move32;
#[cfg(test)]
use crate::types::PieceType;

pub use super::parser::{MissingFieldKind, SfenError};
pub use packed_sfen::{PackedSfen, PackedSfenError};

pub use types::{BoardArray, MoveError, PackedPiece, Ply, ValidationError};

// ANCHOR: position_struct
/// 将棋の盤面状態を管理する構造体
///
/// キャッシュ効率のため、頻繁にアクセスされるデータを先頭に配置
#[repr(C, align(64))]
#[derive(Clone)]
pub struct Position {
    /// `Piece` を81要素並べた盤面
    pub(super) board: BoardArray,

    /// 駒種別・先後別のビットボード集合
    pub(super) bitboards: BitboardSet,

    /// 持ち駒カウント配列
    pub(super) hands: [Hand; Color::COUNT],

    /// 現局面のZobristハッシュ
    pub(super) zobrist: ZobristKey,

    /// 盤面ハッシュ（手番込み）
    pub(super) board_key: ZobristKey,

    /// 持ち駒ハッシュ
    pub(super) hand_key: ZobristKey,

    /// 手番
    pub(super) side_to_move: Color,

    /// 入玉ルール設定
    pub(super) entering_king_rule: EnteringKingRule,

    /// 入玉判定に必要な駒点
    pub(super) entering_king_point: [i32; Color::COUNT],

    /// USIで定義される手数（1から始まる）
    pub(super) ply: Ply,

    /// 現局面に対応するStateInfoのインデックス
    pub(super) st_index: StateIndex,

    /// 局面履歴（YaneuraOu互換のStateList）
    pub(super) state_stack: StateStack,

    /// 玉の位置（色別）
    pub(super) king_square: [Square; Color::COUNT],
}
// ANCHOR_END: position_struct

impl Default for Position {
    fn default() -> Self {
        Self::empty()
    }
}

impl fmt::Display for Position {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // 盤面を表示
        writeln!(f, "  9 8 7 6 5 4 3 2 1")?;
        writeln!(f, "+-------------------+")?;

        for rank_idx in 0..9 {
            let rank_value = i8::try_from(rank_idx).expect("rank index within range");
            let rank = Rank::new(rank_value);
            write!(f, "|")?;
            for file_idx in (0..9).rev() {
                let file_value = i8::try_from(file_idx).expect("file index within range");
                let sq = Square::from_file_rank(File::new(file_value), rank);
                let piece = self.piece_on(sq);

                if piece == Piece::NONE {
                    write!(f, " .")?;
                } else {
                    // 簡易表示（実際はもっと詳細な表示が必要）
                    write!(f, " *")?;
                }
            }
            let rank_char =
                char::from(b'a' + u8::try_from(rank_idx).expect("rank index fits in u8"));
            writeln!(f, "|{rank_char}")?;
        }

        writeln!(f, "+-------------------+")?;
        writeln!(f, "Side to move: {:?}", self.turn())?;
        writeln!(f, "Ply: {}", self.ply)?;

        Ok(())
    }
}

impl Position {
    /// `StateStack` への参照を取得
    #[must_use]
    pub fn state_stack(&self) -> &StateStack {
        &self.state_stack
    }

    /// `StateStack` への可変参照を取得
    pub fn state_stack_mut(&mut self) -> &mut StateStack {
        &mut self.state_stack
    }
}

#[cfg(test)]
mod tests;
