use super::Position;
use crate::board::parser::{generate_sfen_with_ply, parse_sfen, PositionState, 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 state = parse_sfen(sfen)?;
        self.apply_position_state(&state);
        Ok(())
    }

    /// [`PositionState`] から盤面を設定する
    ///
    /// [`set_sfen()`](Self::set_sfen) が SFEN 文字列を受け取るのに対し、
    /// こちらは構造化済みの `PositionState` を直接受け取る。
    /// エディタ等で個別フィールドを変更した `PositionState` を局面に反映する用途に使用する。
    ///
    /// 盤面キャッシュやハッシュは再構築するが、局面のルール妥当性は検証しない。
    /// import 後に妥当性も確認したい場合は
    /// [`validate()`](Self::validate) または [`validate_all()`](Self::validate_all) を使う。
    ///
    /// # Example
    ///
    /// ```
    /// use rshogi::board::{self, Position, parse_sfen};
    ///
    /// board::init();
    /// let mut state = parse_sfen(
    ///     "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1"
    /// ).unwrap();
    /// state.ply = 10; // 手数を変更
    ///
    /// let mut pos = Position::empty();
    /// pos.set_position_state(&state);
    /// assert_eq!(pos.game_ply(), 10);
    /// assert!(pos.validate().is_ok());
    /// ```
    pub fn set_position_state(&mut self, state: &PositionState) {
        self.apply_position_state(state);
    }

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

    fn apply_position_state(&mut self, state: &PositionState) {
        self.board = state.board;
        self.hands = state.hands;
        self.set_side_to_move(state.side_to_move);
        self.ply = state.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 = std::ptr::from_ref(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))
    }
}
