//! 合法手生成
//!
//! このモジュールはジェネリクスによる型安全な合法手生成 API を提供します。
//! YaneuraOu の手生成ロジックと互換性を保ちつつ、Rust らしい API を実現しています。
//!
//! # 基本的な使い方
//!
//! ```
//! use rshogi::board::{self, MoveList};
//! use rshogi::movegen::{Legal, Captures, generate_moves};
//!
//! board::init();
//! let pos = board::hirate_position();
//!
//! // 全合法手を生成
//! let mut moves = MoveList::new();
//! generate_moves::<Legal>(&pos, &mut moves);
//!
//! println!("合法手数: {}", moves.len());
//! for m in moves.iter() {
//!     println!("{}", m.to_usi());
//! }
//! ```
//!
//! # 手生成タイプ
//!
//! 型パラメータで生成する手の種類を指定します。
//!
//! | タイプ | 説明 | 王手時 |
//! |--------|------|--------|
//! | [`Legal`] | 全合法手 | ✅ |
//! | [`Captures`] | 駒を取る手 | ❌ |
//! | [`Quiets`] | 駒を取らない手 | ❌ |
//! | [`Evasions`] | 王手回避手 | ✅ |
//! | [`Checks`] | 王手をかける手 | ❌ |
//! | [`QuietChecks`] | 駒を取らない王手 | ❌ |
//! | [`Recaptures`] | 取り返し手 | ❌ |
//! | [`NonEvasions`] | 回避以外の手 | ❌ |
//! | [`CapturePlusPro`] | 駒取り + 成り | ❌ |
//!
//! 各タイプには `*All` バリアント（例: `LegalAll`）があり、
//! 通常省略される不成の手も含めて生成します。
//!
//! # 王手時の手生成
//!
//! 王手されている場合は [`Evasions`] を使用する必要があります。
//! [`Legal`] は内部で自動的に判定します。
//!
//! ```
//! use rshogi::board::{self, MoveList, position_from_sfen};
//! use rshogi::movegen::{Evasions, Legal, generate_moves};
//!
//! board::init();
//!
//! // 王手されている局面
//! let pos = position_from_sfen(
//!     "4k4/9/4G4/9/9/9/9/9/4K4 w - 1"  // 後手玉に金で王手
//! ).unwrap();
//!
//! // 方法1: Legal を使う（自動判定）
//! let mut moves = MoveList::new();
//! generate_moves::<Legal>(&pos, &mut moves);
//!
//! // 方法2: Evasions を明示的に使う
//! let mut evasions = MoveList::new();
//! generate_moves::<Evasions>(&pos, &mut evasions);
//! ```
//!
//! # 特定のマスへの手生成
//!
//! ```
//! use rshogi::board::{self, MoveList};
//! use rshogi::movegen::{Recaptures, generate_recaptures};
//! use rshogi::types::Square;
//!
//! board::init();
//! let pos = board::hirate_position();
//!
//! // 特定のマス（例: 5五）への取り返し手を生成
//! let target = Square::from_file_rank(
//!     rshogi::types::File::FILE_5,
//!     rshogi::types::Rank::RANK_5
//! );
//! let mut moves = MoveList::new();
//! generate_recaptures(&pos, target, &mut moves);
//! ```
//!
//! # 成りの扱い
//!
//! デフォルトでは探索効率のため、明らかに不利な不成は生成しません。
//! 例えば、敵陣での角の不成は通常生成されません。
//!
//! 全ての合法手（不成を含む）を生成するには `*All` バリアントを使用します：
//!
//! ```
//! use rshogi::board::{self, MoveList};
//! use rshogi::movegen::{Legal, LegalAll, generate_moves};
//!
//! board::init();
//! let pos = board::hirate_position();
//!
//! let mut normal = MoveList::new();
//! generate_moves::<Legal>(&pos, &mut normal);
//!
//! let mut all = MoveList::new();
//! generate_moves::<LegalAll>(&pos, &mut all);
//!
//! // all の方が多くなる可能性がある（不成を含むため）
//! assert!(all.len() >= normal.len());
//! ```
//!
//! # MoveListGen ラッパー
//!
//! YaneuraOu 互換の `MoveList<GenType>` スタイルで使用したい場合は
//! [`MoveListGen`] を使用できます。
//!
//! ```
//! use rshogi::board::{self, movegen::MoveListGen};
//! use rshogi::movegen::Legal;
//!
//! board::init();
//! let pos = board::hirate_position();
//!
//! let moves = MoveListGen::<Legal>::new(&pos);
//! println!("合法手数: {}", moves.len());
//! ```

mod checks;
mod drops;
mod evasions;
mod generate;
mod pieces;
mod promotions;
mod recaptures;
mod types;

#[cfg(test)]
mod tests;

use std::marker::PhantomData;
#[cfg(feature = "generate-all-legal-moves")]
use std::sync::atomic::{AtomicBool, Ordering};

use crate::board::move_list::{ExtMove, ExtMoveList, MoveList};
use crate::board::Position;
use crate::types::{Bitboard, Color, Move16, PieceType, Square};

pub use checks::{generate_checks, generate_checks_drops_part, generate_quiet_checks};
pub use evasions::generate_evasions;
pub use generate::generate_legal_all_yaneuraou;
pub use generate::{generate_moves, generate_moves_to};
pub use recaptures::{generate_recaptures, generate_recaptures_all};
pub use types::{
    CapturePlusPro, CapturePlusProAll, Captures, CapturesAll, Checks, ChecksAll, Evasions,
    EvasionsAll, Legal, LegalAll, MoveGenType, NonEvasions, NonEvasionsAll, QuietChecks,
    QuietChecksAll, Quiets, QuietsAll, QuietsProMinus, QuietsProMinusAll, Recaptures,
    RecapturesAll,
};

pub(crate) trait ColorMarker {
    const COLOR: Color;
    const THEM: Color;
    const IS_BLACK: bool;
}

pub(crate) struct Black;
pub(crate) struct White;

impl ColorMarker for Black {
    const COLOR: Color = Color::BLACK;
    const THEM: Color = Color::WHITE;
    const IS_BLACK: bool = true;
}

impl ColorMarker for White {
    const COLOR: Color = Color::WHITE;
    const THEM: Color = Color::BLACK;
    const IS_BLACK: bool = false;
}

pub trait MoveSink {
    fn push_move(&mut self, mv: Move16);
    fn retain_unordered<F>(&mut self, f: F)
    where
        F: FnMut(Move16) -> bool;

    #[inline]
    fn push_moves_from(&mut self, from: Square, mut destinations: Bitboard) {
        let base = (from.raw() as u16) << 7;
        while let Some(to) = destinations.pop_lsb() {
            let mv = Move16::from_raw(base | to.raw() as u16);
            self.push_move(mv);
        }
    }

    #[inline]
    fn push_promotions_from(&mut self, from: Square, mut destinations: Bitboard) {
        let base = Move16::MOVE_PROMOTE | ((from.raw() as u16) << 7);
        while let Some(to) = destinations.pop_lsb() {
            let mv = Move16::from_raw(base | to.raw() as u16);
            self.push_move(mv);
        }
    }

    #[inline]
    fn push_drops_to(&mut self, piece_type: PieceType, mut destinations: Bitboard) {
        let base = Move16::MOVE_DROP | ((piece_type.to_index() as u16) << 7);
        while let Some(to) = destinations.pop_lsb() {
            let mv = Move16::from_raw(base | to.raw() as u16);
            self.push_move(mv);
        }
    }
}

impl MoveSink for MoveList {
    #[inline]
    fn push_move(&mut self, mv: Move16) {
        // SAFETY: MoveList は MAX_MOVES 超過が起きない前提のホットパス。
        unsafe { self.push_unchecked(mv) };
    }

    #[inline]
    fn retain_unordered<F>(&mut self, f: F)
    where
        F: FnMut(Move16) -> bool,
    {
        self.retain_unordered(f);
    }
}

impl MoveSink for ExtMoveList {
    #[inline]
    fn push_move(&mut self, mv: Move16) {
        // SAFETY: ExtMoveList は MAX_MOVES 超過が起きない前提のホットパス。
        unsafe { self.push_unchecked(ExtMove::from(mv)) };
    }

    #[inline]
    fn retain_unordered<F>(&mut self, f: F)
    where
        F: FnMut(Move16) -> bool,
    {
        self.retain_unordered(f);
    }
}

#[cfg(feature = "generate-all-legal-moves")]
static GENERATE_ALL_LEGAL_MOVES: AtomicBool = AtomicBool::new(false);

#[cfg(feature = "generate-all-legal-moves")]
pub fn set_generate_all_legal_moves(enabled: bool) {
    GENERATE_ALL_LEGAL_MOVES.store(enabled, Ordering::Relaxed);
}

#[cfg(feature = "generate-all-legal-moves")]
#[must_use]
pub fn generate_all_legal_moves_enabled() -> bool {
    GENERATE_ALL_LEGAL_MOVES.load(Ordering::Relaxed)
}

#[cfg(not(feature = "generate-all-legal-moves"))]
pub const fn set_generate_all_legal_moves(_enabled: bool) {}

#[cfg(not(feature = "generate-all-legal-moves"))]
#[must_use]
pub const fn generate_all_legal_moves_enabled() -> bool {
    false
}

/// YaneuraOuの`MoveList<GenType>`相当のラッパー。
pub struct MoveListGen<T: MoveGenType> {
    moves: MoveList,
    _marker: PhantomData<T>,
}

impl<T: MoveGenType + 'static> MoveListGen<T> {
    #[must_use]
    pub fn new(pos: &Position) -> Self {
        let mut moves = MoveList::new();
        debug_assert!(!T::IS_RECAPTURES, "MoveListGen::new is not for Recaptures");
        generate_moves::<T>(pos, &mut moves);
        Self { moves, _marker: PhantomData }
    }

    #[must_use]
    pub fn new_with_target(pos: &Position, target_sq: Square) -> Self {
        let mut moves = MoveList::new();
        if T::IS_RECAPTURES {
            generate_moves_to::<T>(pos, target_sq, &mut moves);
        } else {
            generate_moves::<T>(pos, &mut moves);
        }
        Self { moves, _marker: PhantomData }
    }

    pub fn iter(&self) -> impl Iterator<Item = &Move16> {
        self.moves.iter()
    }

    #[must_use]
    pub const fn len(&self) -> usize {
        self.moves.len()
    }

    #[must_use]
    pub const fn is_empty(&self) -> bool {
        self.moves.is_empty()
    }

    #[must_use]
    pub fn contains(&self, mv: Move16) -> bool {
        self.moves.iter().any(|&m| m == mv)
    }

    #[must_use]
    pub fn at(&self, idx: usize) -> Move16 {
        debug_assert!(idx < self.moves.len(), "index out of bounds");
        self.moves.as_slice()[idx]
    }
}

impl<'a, T: MoveGenType> IntoIterator for &'a MoveListGen<T> {
    type Item = &'a Move16;
    type IntoIter = std::slice::Iter<'a, Move16>;

    fn into_iter(self) -> Self::IntoIter {
        self.moves.as_slice().iter()
    }
}

/// YaneuraOu互換のExtMove生成ヘルパー。
pub fn generate_moves_ext<T: MoveGenType + 'static>(pos: &Position, list: &mut ExtMoveList) {
    debug_assert!(!T::IS_RECAPTURES, "generate_moves_ext is not for Recaptures");
    generate_moves::<T>(pos, list);
}

/// YaneuraOu互換のExtMove生成ヘルパー（指定マスへの移動手）。
pub fn generate_moves_ext_to<T: MoveGenType + 'static>(
    pos: &Position,
    target_sq: Square,
    list: &mut ExtMoveList,
) {
    if T::IS_RECAPTURES {
        generate_moves_to::<T>(pos, target_sq, list);
    } else {
        generate_moves::<T>(pos, list);
    }
}
