use crate::board::{Bitboard, Position};
use crate::types::{Hand, Move16, PieceType, Square};

use super::types::MoveGenType;
use super::{ColorMarker, MoveSink};

#[inline]
fn pawn_drop_mask_for<C: ColorMarker>(pawns: Bitboard) -> Bitboard {
    let left = Bitboard::from_parts([0x4020100804020100, 0x0000000000020100]).packed_bits();
    let board_mask = (1u128 << 81) - 1;
    let mut t = left.wrapping_sub(pawns.packed_bits());
    if C::IS_BLACK {
        t = (t & left) >> 7;
        Bitboard::from_packed_bits(left ^ left.wrapping_sub(t))
    } else {
        t = (t & left) >> 8;
        Bitboard::from_packed_bits((!left & board_mask) & left.wrapping_sub(t))
    }
}

#[allow(clippy::inline_always)]
#[inline(always)]
fn emit_drops_unrolled(
    targets: Bitboard,
    drops: [u16; 6],
    start_idx: usize,
    count: usize,
    list: &mut impl MoveSink,
) {
    if count == 0 {
        return;
    }
    debug_assert!(count <= 6, "drop piece count must be <= 6");
    // `count` は局面ごとに固定なので、match をループの外に出して分岐を削る。
    match count {
        1 => {
            let d0 = drops[start_idx];
            let mut squares = targets;
            while let Some(to) = squares.pop_lsb() {
                debug_assert!(to.is_on_board(), "drop target must be on board");
                let sq = to.raw() as u16;
                list.push_move(Move16::from_raw(d0 | sq));
            }
        }
        2 => {
            let d0 = drops[start_idx];
            let d1 = drops[start_idx + 1];
            let mut squares = targets;
            while let Some(to) = squares.pop_lsb() {
                debug_assert!(to.is_on_board(), "drop target must be on board");
                let sq = to.raw() as u16;
                list.push_move(Move16::from_raw(d0 | sq));
                list.push_move(Move16::from_raw(d1 | sq));
            }
        }
        3 => {
            let d0 = drops[start_idx];
            let d1 = drops[start_idx + 1];
            let d2 = drops[start_idx + 2];
            let mut squares = targets;
            while let Some(to) = squares.pop_lsb() {
                debug_assert!(to.is_on_board(), "drop target must be on board");
                let sq = to.raw() as u16;
                list.push_move(Move16::from_raw(d0 | sq));
                list.push_move(Move16::from_raw(d1 | sq));
                list.push_move(Move16::from_raw(d2 | sq));
            }
        }
        4 => {
            let d0 = drops[start_idx];
            let d1 = drops[start_idx + 1];
            let d2 = drops[start_idx + 2];
            let d3 = drops[start_idx + 3];
            let mut squares = targets;
            while let Some(to) = squares.pop_lsb() {
                debug_assert!(to.is_on_board(), "drop target must be on board");
                let sq = to.raw() as u16;
                list.push_move(Move16::from_raw(d0 | sq));
                list.push_move(Move16::from_raw(d1 | sq));
                list.push_move(Move16::from_raw(d2 | sq));
                list.push_move(Move16::from_raw(d3 | sq));
            }
        }
        5 => {
            let d0 = drops[start_idx];
            let d1 = drops[start_idx + 1];
            let d2 = drops[start_idx + 2];
            let d3 = drops[start_idx + 3];
            let d4 = drops[start_idx + 4];
            let mut squares = targets;
            while let Some(to) = squares.pop_lsb() {
                debug_assert!(to.is_on_board(), "drop target must be on board");
                let sq = to.raw() as u16;
                list.push_move(Move16::from_raw(d0 | sq));
                list.push_move(Move16::from_raw(d1 | sq));
                list.push_move(Move16::from_raw(d2 | sq));
                list.push_move(Move16::from_raw(d3 | sq));
                list.push_move(Move16::from_raw(d4 | sq));
            }
        }
        6 => {
            let d0 = drops[start_idx];
            let d1 = drops[start_idx + 1];
            let d2 = drops[start_idx + 2];
            let d3 = drops[start_idx + 3];
            let d4 = drops[start_idx + 4];
            let d5 = drops[start_idx + 5];
            let mut squares = targets;
            while let Some(to) = squares.pop_lsb() {
                debug_assert!(to.is_on_board(), "drop target must be on board");
                let sq = to.raw() as u16;
                list.push_move(Move16::from_raw(d0 | sq));
                list.push_move(Move16::from_raw(d1 | sq));
                list.push_move(Move16::from_raw(d2 | sq));
                list.push_move(Move16::from_raw(d3 | sq));
                list.push_move(Move16::from_raw(d4 | sq));
                list.push_move(Move16::from_raw(d5 | sq));
            }
        }
        _ => unsafe {
            // SAFETY: `count` は hand の種類数由来で `1..=6` に限定される。
            core::hint::unreachable_unchecked()
        },
    }
}

#[allow(clippy::extra_unused_type_parameters, clippy::too_many_lines)]
#[inline]
pub(super) fn generate_drops_color<T: MoveGenType, C: ColorMarker>(
    pos: &Position,
    list: &mut impl MoveSink,
) {
    let empty = !pos.bitboards().occupied();
    generate_drops_for_target_color::<T, C>(pos, list, empty);
}

#[allow(clippy::extra_unused_type_parameters, clippy::too_many_lines)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub(super) fn generate_drops_for_target_color<T: MoveGenType, C: ColorMarker>(
    pos: &Position,
    list: &mut impl MoveSink,
    target: Bitboard,
) {
    use crate::board::attack_tables::PAWN_ATTACKS;
    const HAND_PAWN_MASK: u32 = 0x1f;
    const HAND_LANCE_MASK: u32 = 0x7 << 8;
    const HAND_KNIGHT_MASK: u32 = 0x7 << 12;
    const HAND_SILVER_MASK: u32 = 0x7 << 16;
    const HAND_BISHOP_MASK: u32 = 0x3 << 20;
    const HAND_ROOK_MASK: u32 = 0x3 << 24;
    const HAND_GOLD_MASK: u32 = 0x7 << 28;

    let us = C::COLOR;
    let them = C::THEM;
    let bb = pos.bitboards();
    let hand = pos.hand(us);
    let hand_bits = hand.bits();
    if hand == Hand::HAND_ZERO {
        return;
    }

    // 空きマスを取得
    let empty = !bb.occupied();
    let empty_target = empty.and(target);
    if empty_target.is_empty() {
        return;
    }

    // --- 歩の打ち駒（YaneuraOu互換: 先に生成）
    if (hand_bits & HAND_PAWN_MASK) != 0 {
        let pawns = bb.pieces_for(PieceType::PAWN, us);
        let mut target2 = empty_target & pawn_drop_mask_for::<C>(pawns);
        let pawn_drop_check = bb
            .pieces_for(PieceType::KING, them)
            .lsb()
            .map_or(Bitboard::EMPTY, |king_sq| PAWN_ATTACKS[king_sq.to_index()][them.to_index()]);
        if !pawn_drop_check.is_empty() {
            let to = pawn_drop_check.lsb().expect("pawn drop check square exists");
            if target2.test(to) && !pos.legal_drop(to) {
                target2 = target2.and_not(pawn_drop_check);
            }
        }

        let drop_base = Move16::make_drop_fast(PieceType::PAWN, Square::ZERO).raw();
        let mut drop_targets = target2;
        while let Some(to) = drop_targets.pop_lsb() {
            debug_assert!(to.is_on_board(), "drop target must be on board");
            let sq = to.raw() as u16;
            list.push_move(Move16::from_raw(drop_base | sq));
        }
    }

    // --- 歩以外の打ち駒（YaneuraOu互換: マス優先、駒種は KNIGHT → LANCE → SILVER → GOLD → BISHOP → ROOK）
    let mut drops: [u16; 6] = [0; 6];
    let mut drops_len = 0;

    let has_knight = (hand_bits & HAND_KNIGHT_MASK) != 0;
    let has_lance = (hand_bits & HAND_LANCE_MASK) != 0;

    if has_knight {
        drops[drops_len] = Move16::make_drop_fast(PieceType::KNIGHT, Square::ZERO).raw();
        drops_len += 1;
    }
    let next_to_knight = drops_len;

    if has_lance {
        drops[drops_len] = Move16::make_drop_fast(PieceType::LANCE, Square::ZERO).raw();
        drops_len += 1;
    }
    let next_to_lance = drops_len;

    if (hand_bits & HAND_SILVER_MASK) != 0 {
        drops[drops_len] = Move16::make_drop_fast(PieceType::SILVER, Square::ZERO).raw();
        drops_len += 1;
    }
    if (hand_bits & HAND_GOLD_MASK) != 0 {
        drops[drops_len] = Move16::make_drop_fast(PieceType::GOLD, Square::ZERO).raw();
        drops_len += 1;
    }
    if (hand_bits & HAND_BISHOP_MASK) != 0 {
        drops[drops_len] = Move16::make_drop_fast(PieceType::BISHOP, Square::ZERO).raw();
        drops_len += 1;
    }
    if (hand_bits & HAND_ROOK_MASK) != 0 {
        drops[drops_len] = Move16::make_drop_fast(PieceType::ROOK, Square::ZERO).raw();
        drops_len += 1;
    }

    if drops_len == 0 {
        return;
    }

    if !has_knight && !has_lance {
        emit_drops_unrolled(empty_target, drops, 0, drops_len, list);
        return;
    }

    let rank1 = if C::IS_BLACK {
        Bitboard::rank_mask(crate::types::Rank::RANK_1)
    } else {
        Bitboard::rank_mask(crate::types::Rank::RANK_9)
    };
    let rank2 = if C::IS_BLACK {
        Bitboard::rank_mask(crate::types::Rank::RANK_2)
    } else {
        Bitboard::rank_mask(crate::types::Rank::RANK_8)
    };
    if has_knight && !has_lance {
        let rank12 = rank1 | rank2;
        let target12 = empty_target & rank12;
        let target3_to_9 = empty_target.and_not(rank12);
        // 1〜2段目: 桂を除外
        emit_drops_unrolled(target12, drops, next_to_knight, drops_len - next_to_knight, list);
        // 3〜9段目: 全駒
        emit_drops_unrolled(target3_to_9, drops, 0, drops_len, list);
        return;
    }

    if !has_knight && has_lance {
        let target1 = empty_target & rank1;
        let target2_to_9 = empty_target.and_not(rank1);
        // 1段目: 香を除外
        emit_drops_unrolled(target1, drops, next_to_lance, drops_len - next_to_lance, list);
        // 2〜9段目: 全駒
        emit_drops_unrolled(target2_to_9, drops, 0, drops_len, list);
        return;
    }

    let rank12 = rank1 | rank2;
    let target1 = empty_target & rank1;
    let target2 = empty_target & rank2;
    let target3_to_9 = empty_target.and_not(rank12);

    // 1段目: 香・桂を除外
    emit_drops_unrolled(target1, drops, next_to_lance, drops_len - next_to_lance, list);
    // 2段目: 桂を除外
    emit_drops_unrolled(target2, drops, next_to_knight, drops_len - next_to_knight, list);
    // 3〜9段目: 全駒
    emit_drops_unrolled(target3_to_9, drops, 0, drops_len, list);
}
