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

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

fn pawn_drop_mask(us: Color, 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 us == Color::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))
    }
}

fn emit_drops_unrolled(
    targets: Bitboard,
    drops: [u16; 6],
    start_idx: usize,
    count: usize,
    list: &mut impl MoveSink,
) {
    if count == 0 {
        return;
    }
    // `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));
            }
        }
        _ => {}
    }
}

#[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)]
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;
    let us = C::COLOR;
    let them = C::THEM;
    let bb = pos.bitboards();
    let hand = pos.hand(us);
    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::hand_exists(hand, HandPiece::HAND_PAWN) {
        let pawns = bb.pieces_for(PieceType::PAWN, us);
        let mut target2 = empty_target & pawn_drop_mask(us, 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;

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

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

    if Hand::hand_exists(hand, HandPiece::HAND_SILVER) {
        drops[drops_len] = Move16::make_drop_fast(PieceType::SILVER, Square::ZERO).raw();
        drops_len += 1;
    }
    if Hand::hand_exists(hand, HandPiece::HAND_GOLD) {
        drops[drops_len] = Move16::make_drop_fast(PieceType::GOLD, Square::ZERO).raw();
        drops_len += 1;
    }
    if Hand::hand_exists(hand, HandPiece::HAND_BISHOP) {
        drops[drops_len] = Move16::make_drop_fast(PieceType::BISHOP, Square::ZERO).raw();
        drops_len += 1;
    }
    if Hand::hand_exists(hand, HandPiece::HAND_ROOK) {
        drops[drops_len] = Move16::make_drop_fast(PieceType::ROOK, Square::ZERO).raw();
        drops_len += 1;
    }

    if drops_len == 0 {
        return;
    }

    if next_to_lance == 0 {
        emit_drops_unrolled(empty_target, drops, 0, drops_len, list);
        return;
    }

    let rank1 = if us == Color::BLACK { Rank::RANK_1 } else { Rank::RANK_9 };
    let rank2 = if us == Color::BLACK { Rank::RANK_2 } else { Rank::RANK_8 };
    let rank3_to_9 = if us == Color::BLACK {
        Bitboard::rank_mask(Rank::RANK_3)
            | Bitboard::rank_mask(Rank::RANK_4)
            | Bitboard::rank_mask(Rank::RANK_5)
            | Bitboard::rank_mask(Rank::RANK_6)
            | Bitboard::rank_mask(Rank::RANK_7)
            | Bitboard::rank_mask(Rank::RANK_8)
            | Bitboard::rank_mask(Rank::RANK_9)
    } else {
        Bitboard::rank_mask(Rank::RANK_1)
            | Bitboard::rank_mask(Rank::RANK_2)
            | Bitboard::rank_mask(Rank::RANK_3)
            | Bitboard::rank_mask(Rank::RANK_4)
            | Bitboard::rank_mask(Rank::RANK_5)
            | Bitboard::rank_mask(Rank::RANK_6)
            | Bitboard::rank_mask(Rank::RANK_7)
    };

    let target1 = empty_target & Bitboard::rank_mask(rank1);
    let target2 = empty_target & Bitboard::rank_mask(rank2);
    let target3 = empty_target & rank3_to_9;

    // 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, drops, 0, drops_len, list);
}
