use super::Position;
use crate::board::parser::{generate_sfen_with_ply, parse_sfen, SfenData, SfenError};
use crate::board::InitialPosition;
use crate::types::{Color, EnteringKingRule, Piece};

impl Position {
    /// SFEN文字列で盤面を設定する（既存局面の上書き）
    pub fn set_sfen(&mut self, sfen: &str) -> Result<(), SfenError> {
        let data = parse_sfen(sfen)?;
        self.apply_sfen_data(&data);
        Ok(())
    }

    /// 平手初期局面に設定する
    pub fn set_hirate(&mut self) {
        self.set_sfen(InitialPosition::Standard.to_sfen()).expect("Invalid startpos SFEN");
    }

    fn apply_sfen_data(&mut self, data: &SfenData) {
        self.board = data.board;
        self.hands = data.hands;
        self.set_side_to_move(data.side_to_move);
        self.ply = data.ply;
        self.entering_king_rule = EnteringKingRule::None;
        self.entering_king_point = [0, 0];

        // ビットボードを再構築
        self.rebuild_bitboards();

        // Zobristハッシュを計算
        let keys = self.compute_keys();
        let (board_key, hand_key) = (keys.board_key, keys.hand_key);
        self.board_key = board_key;
        self.hand_key = hand_key;
        self.zobrist = board_key ^ hand_key;

        // StateStackをリセット
        let this = self as *const Self;
        let board_key = self.board_key;
        let hand_key = self.hand_key;
        let turn = self.turn();
        let hand = self.hand(turn);
        let st_index = {
            let stack = self.state_stack_mut();
            stack.reset_sfen();
            let st_index = stack.current_index();
            let state = stack.current_mut();
            state.board_key = board_key;
            state.hand_key = hand_key;
            // SAFETY: `compute_caches_for_state` は `state_stack` に依存しない。
            unsafe {
                (&*this).compute_caches_for_state(state);
            }
            state.hand = hand;
            st_index
        };
        self.st_index = st_index;
    }

    /// SFEN文字列に変換。
    #[must_use]
    /// `None` の場合は現在の手数を使用する。
    /// `Some(x)` で負数を指定した場合は手数を出力しない。
    pub fn to_sfen(&self, game_ply: Option<i32>) -> String {
        let ply = game_ply.unwrap_or_else(|| i32::from(self.ply));
        let ply = if ply < 0 { None } else { Some(ply) };
        generate_sfen_with_ply(self, ply)
    }

    /// 先後反転（盤面を180度回転し、駒色と手番を反転）したSFENを取得
    #[must_use]
    /// `None` の場合は現在の手数を使用する。
    /// `Some(x)` で負数を指定した場合は手数を出力しない。
    pub fn to_sfen_flipped(&self, game_ply: Option<i32>) -> String {
        let mut flipped = Self::empty();

        for (sq, piece) in self.board.iter() {
            if piece.is_empty() {
                continue;
            }
            let flipped_piece = Piece::from_parts(piece.color().flip(), piece.piece_type());
            flipped.board.set(sq.flip(), flipped_piece);
        }

        flipped.hands[Color::BLACK.to_index()] = self.hands[Color::WHITE.to_index()];
        flipped.hands[Color::WHITE.to_index()] = self.hands[Color::BLACK.to_index()];
        flipped.set_side_to_move(self.turn().flip());
        flipped.ply = self.ply;

        let ply = game_ply.unwrap_or_else(|| i32::from(self.ply));
        let ply = if ply < 0 { None } else { Some(ply) };
        generate_sfen_with_ply(&flipped, ply)
    }

    /// SFEN文字列を先後反転したSFENに変換する
    pub fn sfen_to_sfen_flipped(sfen: &str) -> Result<String, SfenError> {
        let mut pos = Self::empty();
        pos.set_sfen(sfen)?;
        Ok(pos.to_sfen_flipped(None))
    }
}
