use std::env;
use std::fs;
use std::path::Path;

fn main() {
    println!("cargo:rerun-if-changed=build.rs");

    // OUT_DIR環境変数を取得
    let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
    let dest_path = Path::new(&out_dir);

    // 座標変換テーブル生成（タスク5.2で実装）
    generate_square_tables(dest_path);

    // 攻撃テーブル生成（P4フェーズ）
    generate_attack_tables(dest_path);
}

/// 座標変換テーブルを生成
fn generate_square_tables(out_dir: &Path) {
    let mut output = String::new();

    // ヘッダー部分
    output.push_str(
        "// Auto-generated by build.rs - DO NOT EDIT

",
    );
    // use文は不要（include!するファイルで既にインポートされている）

    // SQUARE_TO_FILEテーブル生成
    // Squareは 0..81 (SQ_11..SQ_99) + 81 (SQ_NONE)
    output.push_str(
        "/// マスから筋への変換テーブル (82要素: 0..81 + `SQ_NONE`)
",
    );
    output.push_str(
        "pub const SQUARE_TO_FILE: [File; 82] = [
",
    );

    // 0..81: 通常のマス (file = sq / 9, rank = sq % 9)
    // 将棋の座標系: file(筋)は1-9、rank(段)も1-9
    // Square = file * 9 + rank なので、file = sq / 9
    for sq in 0..81 {
        let file = sq / 9;
        if sq % 9 == 0 {
            output.push_str("    ");
        }
        output.push_str(&format!("File::new({file}), "));
        if sq % 9 == 8 {
            output.push_str(&format!(
                "// SQ_{}1 - SQ_{}9
",
                9 - file,
                9 - file
            ));
        }
    }

    // 81: SQ_NONE用
    output.push_str(
        "    File::new(-1), // SQ_NONE
",
    );
    output.push_str(
        "];

",
    );

    // SQUARE_TO_RANKテーブル生成
    output.push_str(
        "/// マスから段への変換テーブル (82要素: 0..81 + `SQ_NONE`)
",
    );
    output.push_str(
        "pub const SQUARE_TO_RANK: [Rank; 82] = [
",
    );

    // 0..81: 通常のマス
    for sq in 0..81 {
        let rank = sq % 9;
        if sq % 9 == 0 {
            output.push_str("    ");
        }
        output.push_str(&format!("Rank::new({rank}), "));
        if sq % 9 == 8 {
            output.push_str(&format!(
                "// SQ_{}1 - SQ_{}9
",
                9 - sq / 9,
                9 - sq / 9
            ));
        }
    }

    // 81: SQ_NONE用
    output.push_str(
        "    Rank::new(-1), // SQ_NONE
",
    );
    output.push_str(
        "];

",
    );

    // ファイルに書き込み
    let path = out_dir.join("square_tables.rs");
    fs::write(path, output).expect("Failed to write square_tables.rs");
}

/// 攻撃テーブルを生成
fn generate_attack_tables(out_dir: &Path) {
    let mut output = String::new();

    // ヘッダー
    output.push_str(
        "// Auto-generated attack tables - DO NOT EDIT\n\
\n\
use crate::board::{Bitboard, Bitboard256};\n\
\n",
    );

    // 非滑り駒の攻撃テーブル
    output.push_str(&generate_non_slider_attacks());

    // 滑り駒の攻撃テーブル
    output.push_str(&generate_slider_beams());
    output.push_str(&generate_qugiy_step_effects());
    output.push_str(&generate_qugiy_rook_masks());
    output.push_str(&generate_qugiy_bishop_masks());
    output.push_str(&generate_check_candidate_table());

    // ファイルに書き込み
    let path = out_dir.join("attack_tables.rs");
    fs::write(path, output).expect("Failed to write attack_tables.rs");
}

/// 王手候補テーブルを生成
fn generate_check_candidate_table() -> String {
    let mut output = String::new();

    output.push_str(
        "/// 王手候補テーブル \\[king_sq\\]\\[piece_index\\]\\[color\\]\n\
/// piece_index: 0=PAWN,1=LANCE,2=KNIGHT,3=SILVER,4=GOLD,5=BISHOP,6=ROOK(HORSE)\n\
pub const CHECK_CANDIDATE_BB: [[[Bitboard; 2]; 7]; 81] = [\n",
    );

    for ksq in 0..81 {
        output.push_str("    [\n");
        for pt_idx in 0..7 {
            output.push_str("        [\n");
            for us in 0..2 {
                let bb = check_candidate_bb(ksq, pt_idx, us);
                output.push_str(&format!("            Bitboard::from_packed_bits({bb}u128),\n"));
            }
            output.push_str("        ],\n");
        }
        output.push_str("    ],\n");
    }

    output.push_str("];\n\n");
    output
}

#[allow(clippy::too_many_lines)]
fn check_candidate_bb(ksq: i8, pt_idx: usize, us: usize) -> u128 {
    let them = us ^ 1;
    let enemy_field = enemy_field_mask(us);
    let enemy_gold = gold_effect(them, ksq) & enemy_field;
    let mut target = 0u128;

    match pt_idx {
        0 => {
            let mut bb = pawn_effect(them, ksq);
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= pawn_effect(them, sq);
            }
            let mut bb = enemy_gold;
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= pawn_effect(them, sq);
            }
            target &= !bit_of(ksq);
        }
        1 => {
            target = lance_step_effect(them, ksq);
            if (enemy_field & bit_of(ksq)) != 0 {
                let file = ksq / 9;
                let rank = ksq % 9;
                if file != 0 {
                    let sq = (file - 1) * 9 + rank;
                    target |= lance_step_effect(them, sq);
                }
                if file != 8 {
                    let sq = (file + 1) * 9 + rank;
                    target |= lance_step_effect(them, sq);
                }
            }
        }
        2 => {
            let mut bb = knight_effect(them, ksq) | enemy_gold;
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= knight_effect(them, sq);
            }
            target &= !bit_of(ksq);
        }
        3 => {
            let mut bb = silver_effect(them, ksq);
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= silver_effect(them, sq);
            }
            let mut bb = enemy_gold;
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= silver_effect(them, sq);
            }
            let mut bb = gold_effect(them, ksq);
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= silver_effect(them, sq) & enemy_field;
            }
            target &= !bit_of(ksq);
        }
        4 => {
            let mut bb = gold_effect(them, ksq);
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= gold_effect(them, sq);
            }
            target &= !bit_of(ksq);
        }
        5 => {
            let mut bb = bishop_effect(ksq);
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= bishop_effect(sq);
            }
            let mut bb = king_effect(ksq) & enemy_field;
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= bishop_effect(sq);
            }
            let mut bb = king_effect(ksq);
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= bishop_effect(sq) & enemy_field;
            }
            target &= !bit_of(ksq);
        }
        6 => {
            let mut bb = horse_effect(ksq);
            while let Some(sq) = pop_lsb(&mut bb) {
                target |= horse_effect(sq);
            }
            target &= !bit_of(ksq);
        }
        _ => {}
    }

    target
}

fn bit_of(sq: i8) -> u128 {
    let shift = u32::try_from(sq).expect("square index is non-negative");
    1u128 << shift
}

fn pop_lsb(bb: &mut u128) -> Option<i8> {
    if *bb == 0 {
        return None;
    }
    let idx = bb.trailing_zeros();
    *bb &= *bb - 1;
    Some(i8::try_from(idx).expect("square index fits in i8"))
}

fn enemy_field_mask(us: usize) -> u128 {
    let ranks: [i8; 3] = if us == 0 { [0, 1, 2] } else { [6, 7, 8] };
    let mut mask = 0u128;
    for rank in ranks {
        for file in 0..9 {
            let sq = file * 9 + rank;
            mask |= bit_of(sq);
        }
    }
    mask
}

fn pawn_effect(color: usize, sq: i8) -> u128 {
    generate_pawn_attack(sq, color)
}

fn knight_effect(color: usize, sq: i8) -> u128 {
    generate_knight_attack(sq, color)
}

fn silver_effect(color: usize, sq: i8) -> u128 {
    generate_silver_attack(sq, color)
}

fn gold_effect(color: usize, sq: i8) -> u128 {
    generate_gold_attack(sq, color)
}

fn king_effect(sq: i8) -> u128 {
    generate_king_attack(sq)
}

fn lance_step_effect(color: usize, sq: i8) -> u128 {
    let (black, white) = generate_lance_beams(sq);
    if color == 0 {
        black
    } else {
        white
    }
}

fn bishop_effect(sq: i8) -> u128 {
    let (ne, se, sw, nw) = generate_bishop_beams(sq);
    ne | se | sw | nw
}

fn horse_effect(sq: i8) -> u128 {
    bishop_effect(sq) | king_effect(sq)
}

/// 非滑り駒（歩・桂・銀・金・玉）の攻撃テーブルを生成
fn generate_non_slider_attacks() -> String {
    let mut output = String::new();

    // 歩の攻撃テーブル
    output.push_str("/// 歩の攻撃パターン \\[sq\\]\\[color\\]\n");
    output.push_str("pub const PAWN_ATTACKS: [[Bitboard; 2]; 81] = [\n");
    for sq in 0..81 {
        output.push_str("    [\n");
        for color in 0..2 {
            let bb = generate_pawn_attack(sq, color);
            output.push_str(&format!("        Bitboard::from_packed_bits({bb}u128),\n"));
        }
        output.push_str("    ],\n");
    }
    output.push_str("];\n\n");

    // 桂馬の攻撃テーブル
    output.push_str("/// 桂馬の攻撃パターン \\[sq\\]\\[color\\]\n");
    output.push_str("pub const KNIGHT_ATTACKS: [[Bitboard; 2]; 81] = [\n");
    for sq in 0..81 {
        output.push_str("    [\n");
        for color in 0..2 {
            let bb = generate_knight_attack(sq, color);
            output.push_str(&format!("        Bitboard::from_packed_bits({bb}u128),\n"));
        }
        output.push_str("    ],\n");
    }
    output.push_str("];\n\n");

    // 銀の攻撃テーブル
    output.push_str("/// 銀の攻撃パターン \\[sq\\]\\[color\\]\n");
    output.push_str("pub const SILVER_ATTACKS: [[Bitboard; 2]; 81] = [\n");
    for sq in 0..81 {
        output.push_str("    [\n");
        for color in 0..2 {
            let bb = generate_silver_attack(sq, color);
            output.push_str(&format!("        Bitboard::from_packed_bits({bb}u128),\n"));
        }
        output.push_str("    ],\n");
    }
    output.push_str("];\n\n");

    // 金の攻撃テーブル
    output.push_str("/// 金の攻撃パターン \\[sq\\]\\[color\\]\n");
    output.push_str("pub const GOLD_ATTACKS: [[Bitboard; 2]; 81] = [\n");
    for sq in 0..81 {
        output.push_str("    [\n");
        for color in 0..2 {
            let bb = generate_gold_attack(sq, color);
            output.push_str(&format!("        Bitboard::from_packed_bits({bb}u128),\n"));
        }
        output.push_str("    ],\n");
    }
    output.push_str("];\n\n");

    // 玉の攻撃テーブル
    output.push_str("/// 玉の攻撃パターン \\[sq\\]\n");
    output.push_str("pub const KING_ATTACKS: [Bitboard; 81] = [\n");
    for sq in 0..81 {
        let bb = generate_king_attack(sq);
        output.push_str(&format!("    Bitboard::from_packed_bits({bb}u128),\n"));
    }
    output.push_str("];\n\n");

    output
}

/// 歩の攻撃パターンを生成
fn generate_pawn_attack(sq: i8, color: usize) -> u128 {
    let file = sq / 9;
    let rank = sq % 9;

    // 先手(BLACK=0): 前方(-1)、後手(WHITE=1): 前方(+1)
    let forward_rank = if color == 0 { rank - 1 } else { rank + 1 };

    if !(0..9).contains(&forward_rank) {
        return 0; // 盤外
    }

    let target_sq = file * 9 + forward_rank;
    1u128 << target_sq
}

/// 桂馬の攻撃パターンを生成
fn generate_knight_attack(sq: i8, color: usize) -> u128 {
    let file = sq / 9;
    let rank = sq % 9;

    let mut attacks = 0u128;

    // 先手(BLACK=0): 2つ前、左右1マス
    // 後手(WHITE=1): 2つ後、左右1マス
    let forward_offset = if color == 0 { -2 } else { 2 };
    let target_rank = rank + forward_offset;

    if !(0..9).contains(&target_rank) {
        return 0;
    }

    // 左1マス
    if (0..9).contains(&(file + 1)) {
        let target_sq = (file + 1) * 9 + target_rank;
        attacks |= 1u128 << target_sq;
    }

    // 右1マス
    if (0..9).contains(&(file - 1)) {
        let target_sq = (file - 1) * 9 + target_rank;
        attacks |= 1u128 << target_sq;
    }

    attacks
}

/// 銀の攻撃パターンを生成
fn generate_silver_attack(sq: i8, color: usize) -> u128 {
    let file = sq / 9;
    let rank = sq % 9;

    let mut attacks = 0u128;

    // 先手(BLACK=0): 前方5マス（前1、斜め前2、斜め後2）
    // 後手(WHITE=1): 前方5マス（後1、斜め後2、斜め前2）
    let forward = if color == 0 { -1 } else { 1 };
    let backward = -forward;

    // 前方1マス
    if let Some(target_sq) = add_direction(file, rank, 0, forward) {
        attacks |= 1u128 << target_sq;
    }

    // 斜め前左
    if let Some(target_sq) = add_direction(file, rank, 1, forward) {
        attacks |= 1u128 << target_sq;
    }

    // 斜め前右
    if let Some(target_sq) = add_direction(file, rank, -1, forward) {
        attacks |= 1u128 << target_sq;
    }

    // 斜め後左
    if let Some(target_sq) = add_direction(file, rank, 1, backward) {
        attacks |= 1u128 << target_sq;
    }

    // 斜め後右
    if let Some(target_sq) = add_direction(file, rank, -1, backward) {
        attacks |= 1u128 << target_sq;
    }

    attacks
}

/// 金の攻撃パターンを生成
fn generate_gold_attack(sq: i8, color: usize) -> u128 {
    let file = sq / 9;
    let rank = sq % 9;

    let mut attacks = 0u128;

    // 基本の利き（先手基準）: 前後左右 + 斜め前2方向
    #[allow(clippy::cast_possible_wrap)]
    let black_offsets: &[(i8, i8)] = &[
        (0, -1),  // 前方
        (1, -1),  // 左斜め前
        (-1, -1), // 右斜め前
        (1, 0),   // 左
        (-1, 0),  // 右
        (0, 1),   // 後方
    ];

    let white_offsets: &[(i8, i8)] = &[
        (0, 1),  // 前方（後手基準）
        (-1, 1), // 左斜め前（後手視点）
        (1, 1),  // 右斜め前（後手視点）
        (-1, 0), // 左
        (1, 0),  // 右
        (0, -1), // 後方
    ];

    let offsets = if color == 0 { black_offsets } else { white_offsets };

    for &(df, dr) in offsets {
        if let Some(target_sq) = add_direction(file, rank, df, dr) {
            attacks |= 1u128 << target_sq;
        }
    }

    attacks
}

/// 玉の攻撃パターンを生成
fn generate_king_attack(sq: i8) -> u128 {
    let file = sq / 9;
    let rank = sq % 9;

    let mut attacks = 0u128;

    // 8方向すべて
    for df in -1..=1 {
        for dr in -1..=1 {
            if df == 0 && dr == 0 {
                continue; // 自分自身はスキップ
            }

            if let Some(target_sq) = add_direction(file, rank, df, dr) {
                attacks |= 1u128 << target_sq;
            }
        }
    }

    attacks
}

/// 方向を加算してSquareを取得（盤内チェック付き）
fn add_direction(file: i8, rank: i8, df: i8, dr: i8) -> Option<i8> {
    let new_file = file + df;
    let new_rank = rank + dr;

    if (0..9).contains(&new_file) && (0..9).contains(&new_rank) {
        Some(new_file * 9 + new_rank)
    } else {
        None
    }
}

/// 滑り駒（香・角・飛）のビームテーブルを生成
fn generate_slider_beams() -> String {
    let mut output = String::new();

    // LanceBeams構造体定義
    output.push_str(
        r"/// 香車のビーム構造
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LanceBeams {
    pub black: Bitboard,
    pub white: Bitboard,
}

",
    );

    // BishopBeams構造体定義
    output.push_str(
        r"/// 角のビーム構造
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BishopBeams {
    pub ne: Bitboard,
    pub se: Bitboard,
    pub sw: Bitboard,
    pub nw: Bitboard,
}

",
    );

    // RookBeams構造体定義
    output.push_str(
        r"/// 飛車のビーム構造
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RookBeams {
    pub n: Bitboard,
    pub e: Bitboard,
    pub s: Bitboard,
    pub w: Bitboard,
}

",
    );

    // 香車のビームテーブル
    output.push_str("/// 香車のビームテーブル \\[sq\\]\n");
    output.push_str("pub const LANCE_BEAMS: [LanceBeams; 81] = [\n");
    for sq in 0..81 {
        let (black_beam, white_beam) = generate_lance_beams(sq);
        output.push_str(&format!(
            "    LanceBeams {{ black: Bitboard::from_packed_bits({black_beam}u128), white: Bitboard::from_packed_bits({white_beam}u128) }},\n"
        ));
    }
    output.push_str("];\n\n");

    // 角のビームテーブル
    output.push_str("/// 角のビームテーブル \\[sq\\]\n");
    output.push_str("pub const BISHOP_BEAMS: [BishopBeams; 81] = [\n");
    for sq in 0..81 {
        let (ne, se, sw, nw) = generate_bishop_beams(sq);
        output.push_str(&format!(
            "    BishopBeams {{ ne: Bitboard::from_packed_bits({ne}u128), se: Bitboard::from_packed_bits({se}u128), sw: Bitboard::from_packed_bits({sw}u128), nw: Bitboard::from_packed_bits({nw}u128) }},\n"
        ));
    }
    output.push_str("];\n\n");

    // 飛車のビームテーブル
    output.push_str("/// 飛車のビームテーブル \\[sq\\]\n");
    output.push_str("pub const ROOK_BEAMS: [RookBeams; 81] = [\n");
    for sq in 0..81 {
        let (n, e, s, w) = generate_rook_beams(sq);
        output.push_str(&format!(
            "    RookBeams {{ n: Bitboard::from_packed_bits({n}u128), e: Bitboard::from_packed_bits({e}u128), s: Bitboard::from_packed_bits({s}u128), w: Bitboard::from_packed_bits({w}u128) }},\n"
        ));
    }
    output.push_str("];\n\n");

    output
}

/// Qugiyの方向利きステップテーブルを生成
fn generate_qugiy_step_effects() -> String {
    let mut output = String::new();

    output.push_str(
        "/// Qugiyの方向利きステップテーブル \\[dir\\]\\[sq\\]\n\
/// dir index: 0=RU, 1=R, 2=RD, 3=LU, 4=L, 5=LD\n\
pub const QUGIY_STEP_EFFECT: [[Bitboard; 81]; 6] = [\n",
    );

    // YaneuraOuのrayEffectに合わせ、RU/R/RD方向はbyte_reverseしたマスクを持つ。
    let directions: [(i8, i8); 6] = [
        (-1, -1), // RU
        (-1, 0),  // R
        (-1, 1),  // RD
        (1, -1),  // LU
        (1, 0),   // L
        (1, 1),   // LD
    ];

    let reverse_dirs = [true, true, true, false, false, false];

    for (dir_idx, (df, dr)) in directions.iter().enumerate() {
        output.push_str("    [\n");
        for sq in 0..81 {
            let mut mask = generate_ray_mask(sq, *df, *dr);
            if reverse_dirs[dir_idx] {
                mask = byte_reverse_u128(mask);
            }
            output
                .push_str(&format!("        Bitboard::from_packed_bits_unchecked({mask}u128),\n"));
        }
        output.push_str("    ],\n");
    }

    output.push_str("];\n\n");
    output
}

/// Qugiyの飛車マスクテーブルを生成
fn generate_qugiy_rook_masks() -> String {
    let mut output = String::new();

    output.push_str("/// Qugiyの飛車マスクテーブル \\[sq\\]\\[lo/hi\\]\n");
    output.push_str("pub const QUGIY_ROOK_MASK: [[Bitboard; 2]; 81] = [\n");

    for sq in 0..81 {
        let left = generate_ray_parts(sq, 1, 0);
        let right = generate_ray_parts(sq, -1, 0);
        let right_rev = (right.1.swap_bytes(), right.0.swap_bytes());
        let (hi, lo) = unpack_parts(right_rev, left);
        let hi = parts_to_raw_u128(hi);
        let lo = parts_to_raw_u128(lo);
        output.push_str("    [\n");
        output.push_str(&format!("        Bitboard::from_packed_bits_unchecked({lo}u128),\n"));
        output.push_str(&format!("        Bitboard::from_packed_bits_unchecked({hi}u128),\n"));
        output.push_str("    ],\n");
    }

    output.push_str("];\n\n");
    output
}

/// Qugiyの角マスクテーブルを生成
#[allow(clippy::similar_names)]
fn generate_qugiy_bishop_masks() -> String {
    let mut output = String::new();

    output.push_str("/// Qugiyの角マスクテーブル \\[sq\\]\\[part\\]\n");
    output.push_str("pub const QUGIY_BISHOP_MASK: [[Bitboard256; 2]; 81] = [\n");

    for sq in 0..81 {
        let lu = generate_ray_mask(sq, 1, -1);
        let ld = generate_ray_mask(sq, 1, 1);
        let ru = byte_reverse_u128(generate_ray_mask(sq, -1, -1));
        let rd = byte_reverse_u128(generate_ray_mask(sq, -1, 1));
        output.push_str("    [\n");
        for part in 0..2 {
            let lu_part = extract64(lu, part);
            let ru_part = extract64(ru, part);
            let ld_part = extract64(ld, part);
            let rd_part = extract64(rd, part);
            output.push_str(&format!(
                "        Bitboard256::new({lu_part}, {ru_part}, {ld_part}, {rd_part}),\n"
            ));
        }
        output.push_str("    ],\n");
    }

    output.push_str("];\n\n");
    output
}

fn generate_ray_mask(sq: i8, df: i8, dr: i8) -> u128 {
    let file = sq / 9;
    let rank = sq % 9;

    let mut mask = 0u128;
    let mut f = file + df;
    let mut r = rank + dr;
    while (0..9).contains(&f) && (0..9).contains(&r) {
        let target_sq = f * 9 + r;
        mask |= 1u128 << raw_bit_index(target_sq);
        f += df;
        r += dr;
    }
    mask
}

#[allow(clippy::cast_possible_truncation)]
const fn parts_to_raw_u128(parts: (u64, u64)) -> u128 {
    (parts.0 as u128) | ((parts.1 as u128) << 64)
}

fn square_parts(sq: i8) -> (u64, u64) {
    let file = sq / 9;
    let rank = sq % 9;
    if file < 7 {
        let shift = u32::try_from(file * 9 + rank).expect("square index fits in u32");
        (1u64 << shift, 0)
    } else {
        let shift = u32::try_from((file - 7) * 9 + rank).expect("square index fits in u32");
        (0, 1u64 << shift)
    }
}

fn generate_ray_parts(sq: i8, df: i8, dr: i8) -> (u64, u64) {
    let file = sq / 9;
    let rank = sq % 9;

    let mut p0 = 0u64;
    let mut p1 = 0u64;
    let mut f = file + df;
    let mut r = rank + dr;
    while (0..9).contains(&f) && (0..9).contains(&r) {
        let target_sq = f * 9 + r;
        let (b0, b1) = square_parts(target_sq);
        p0 |= b0;
        p1 |= b1;
        f += df;
        r += dr;
    }
    (p0, p1)
}

#[allow(clippy::cast_possible_truncation)]
const fn unpack_parts(hi_in: (u64, u64), lo_in: (u64, u64)) -> ((u64, u64), (u64, u64)) {
    let hi_out = (lo_in.1, hi_in.1);
    let lo_out = (lo_in.0, hi_in.0);
    (hi_out, lo_out)
}

#[allow(clippy::cast_possible_truncation)]
const fn extract64(packed_bits: u128, part: usize) -> u64 {
    if part == 0 {
        packed_bits as u64
    } else {
        (packed_bits >> 64) as u64
    }
}

#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
const fn raw_bit_index(sq: i8) -> u32 {
    if sq >= 63 {
        (sq as u32) + 1
    } else {
        sq as u32
    }
}

#[allow(clippy::cast_possible_truncation)]
const fn byte_reverse_u128(packed_bits: u128) -> u128 {
    let low = packed_bits as u64;
    let high = (packed_bits >> 64) as u64;
    ((low.swap_bytes() as u128) << 64) | (high.swap_bytes() as u128)
}

/// 香車のビームを生成（先手：北、後手：南）
fn generate_lance_beams(sq: i8) -> (u128, u128) {
    let file = sq / 9;
    let rank = sq % 9;

    let mut black_beam = 0u128; // 先手：北方向（rank減少）
    let mut white_beam = 0u128; // 後手：南方向（rank増加）

    // 先手（BLACK）：北方向（rank: current-1, current-2, ..., 0）
    for r in (0..rank).rev() {
        let target_sq = file * 9 + r;
        black_beam |= 1u128 << target_sq;
    }

    // 後手（WHITE）：南方向（rank: current+1, current+2, ..., 8）
    for r in (rank + 1)..9 {
        let target_sq = file * 9 + r;
        white_beam |= 1u128 << target_sq;
    }

    (black_beam, white_beam)
}

/// 角のビームを生成（4方向：北東、南東、南西、北西）
#[allow(clippy::similar_names)]
fn generate_bishop_beams(sq: i8) -> (u128, u128, u128, u128) {
    let file = sq / 9;
    let rank = sq % 9;

    let mut ne_beam = 0u128; // 北東：file+1, rank-1
    let mut se_beam = 0u128; // 南東：file+1, rank+1
    let mut sw_beam = 0u128; // 南西：file-1, rank+1
    let mut nw_beam = 0u128; // 北西：file-1, rank-1

    // 北東方向
    let mut f = file + 1;
    let mut r = rank - 1;
    while (0..9).contains(&f) && (0..9).contains(&r) {
        let target_sq = f * 9 + r;
        ne_beam |= 1u128 << target_sq;
        f += 1;
        r -= 1;
    }

    // 南東方向
    let mut f = file + 1;
    let mut r = rank + 1;
    while (0..9).contains(&f) && (0..9).contains(&r) {
        let target_sq = f * 9 + r;
        se_beam |= 1u128 << target_sq;
        f += 1;
        r += 1;
    }

    // 南西方向
    let mut f = file - 1;
    let mut r = rank + 1;
    while (0..9).contains(&f) && (0..9).contains(&r) {
        let target_sq = f * 9 + r;
        sw_beam |= 1u128 << target_sq;
        f -= 1;
        r += 1;
    }

    // 北西方向
    let mut f = file - 1;
    let mut r = rank - 1;
    while (0..9).contains(&f) && (0..9).contains(&r) {
        let target_sq = f * 9 + r;
        nw_beam |= 1u128 << target_sq;
        f -= 1;
        r -= 1;
    }

    (ne_beam, se_beam, sw_beam, nw_beam)
}

/// 飛車のビームを生成（4方向：北、東、南、西）
fn generate_rook_beams(sq: i8) -> (u128, u128, u128, u128) {
    let file = sq / 9;
    let rank = sq % 9;

    let mut n_beam = 0u128; // 北：rank-1, rank-2, ..., 0
    let mut e_beam = 0u128; // 東：file+1, file+2, ..., 8
    let mut s_beam = 0u128; // 南：rank+1, rank+2, ..., 8
    let mut w_beam = 0u128; // 西：file-1, file-2, ..., 0

    // 北方向
    for r in (0..rank).rev() {
        let target_sq = file * 9 + r;
        n_beam |= 1u128 << target_sq;
    }

    // 東方向
    for f in (file + 1)..9 {
        let target_sq = f * 9 + rank;
        e_beam |= 1u128 << target_sq;
    }

    // 南方向
    for r in (rank + 1)..9 {
        let target_sq = file * 9 + r;
        s_beam |= 1u128 << target_sq;
    }

    // 西方向
    for f in (0..file).rev() {
        let target_sq = f * 9 + rank;
        w_beam |= 1u128 << target_sq;
    }

    (n_beam, e_beam, s_beam, w_beam)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;

    #[test]
    fn test_generate_square_tables() {
        // テンポラリディレクトリにテーブルを生成
        let temp_dir = std::env::temp_dir().join("rshogi_build_test");
        fs::create_dir_all(&temp_dir).unwrap();

        // テーブル生成
        generate_square_tables(&temp_dir);

        // 生成されたファイルを読み込み
        let content = fs::read_to_string(temp_dir.join("square_tables.rs"))
            .expect("Failed to read generated file");

        // 基本的な検証
        assert!(content.contains("SQUARE_TO_FILE"));
        assert!(content.contains("SQUARE_TO_RANK"));
        assert!(content.contains("[File; 82]")); // 82要素 (0..81 + SQ_NONE)
        assert!(content.contains("[Rank; 82]")); // 82要素

        // SQ_11 (sq=0) は File::FILE_1 (file=0), Rank::RANK_1 (rank=0)
        assert!(content.contains("File(0),")); // 最初の要素
        assert!(content.contains("Rank(0),")); // 最初の要素

        // SQ_99 (sq=80) は File::FILE_9 (file=8), Rank::RANK_9 (rank=8)
        // sq=80 → file=80/9=8, rank=80%9=8

        // SQ_NONE用のエントリ
        assert!(content.contains("File(-1), // SQ_NONE"));
        assert!(content.contains("Rank(-1), // SQ_NONE"));

        // クリーンアップ
        let _ = fs::remove_dir_all(&temp_dir);
    }
}
