use parking_lot::Mutex;
use std::sync::LazyLock;

static THREAD_POOL: LazyLock<Mutex<()>> = LazyLock::new(|| {
    unsafe { crate::SetMaxThreads(0) };
    Mutex::new(())
});

#[allow(clippy::large_types_passed_by_value)]
fn check(
    deal: crate::ddTableDeal,
    solution: crate::ddTableResults,
    pars: [crate::parResultsMaster; 2],
) {
    #[allow(clippy::cast_possible_wrap)]
    const SUCCESS: i32 = crate::RETURN_NO_FAULT as i32;
    let mut tricks = crate::ddTableResults::default();
    let status = unsafe {
        let _guard = THREAD_POOL.lock();
        crate::CalcDDtable(deal, &raw mut tricks)
    };
    assert_eq!(status, SUCCESS);
    assert_eq!(tricks, solution);

    let mut result = [crate::parResultsMaster::default(); 2];
    let status = unsafe { crate::SidesParBin(&raw mut tricks, &raw mut result[0], 0) };
    assert_eq!(status, SUCCESS);
    assert_eq!(result, pars);
}

const NO_CONTRACT: crate::contractType = crate::contractType {
    level: 0,
    denom: 0,
    seats: 0,
    underTricks: 0,
    overTricks: 0,
};

/// Everyone has a 13-card straight flush, and the par is 7SW=.
#[test]
fn solve_four_13_card_straight_flushes() {
    const MASK: core::ffi::c_uint = ((1 << 13) - 1) << 2;
    const DEAL: crate::ddTableDeal = crate::ddTableDeal {
        cards: [
            [0, 0, 0, MASK], // N
            [0, 0, MASK, 0], // E
            [0, MASK, 0, 0], // S
            [MASK, 0, 0, 0], // W
        ],
    };
    const SOLUTION: crate::ddTableResults = crate::ddTableResults {
        resTable: [
            [0, 13, 0, 13], // S
            [13, 0, 13, 0], // H
            [0, 13, 0, 13], // D
            [13, 0, 13, 0], // C
            [0, 0, 0, 0],   // NT
        ],
    };
    const CONTRACTS: [crate::contractType; 10] = [
        crate::contractType {
            level: 7,
            denom: 1, // spades
            seats: 5,
            underTricks: 0,
            overTricks: 0,
        },
        NO_CONTRACT,
        NO_CONTRACT,
        NO_CONTRACT,
        NO_CONTRACT,
        NO_CONTRACT,
        NO_CONTRACT,
        NO_CONTRACT,
        NO_CONTRACT,
        NO_CONTRACT,
    ];
    const NS: crate::parResultsMaster = crate::parResultsMaster {
        score: -1510,
        number: 1,
        contracts: CONTRACTS,
    };
    const EW: crate::parResultsMaster = crate::parResultsMaster {
        score: 1510,
        number: 1,
        contracts: CONTRACTS,
    };
    check(DEAL, SOLUTION, [NS, EW]);
}

/// Defenders can cash 8 tricks in every strain.
///
/// This example is taken from
/// <http://bridge.thomasoandrews.com/deals/parzero/>.
#[test]
fn solve_par_5_tricks() {
    const AKQJ: core::ffi::c_uint = 0xF << 11;
    const T987: core::ffi::c_uint = 0xF << 7;
    const XXXX: core::ffi::c_uint = 0xF << 3;
    const X: core::ffi::c_uint = 1 << 2;

    const DEAL: crate::ddTableDeal = crate::ddTableDeal {
        cards: [
            [AKQJ, X, XXXX, T987], // N
            [XXXX, T987, AKQJ, X], // E
            [X, AKQJ, T987, XXXX], // S
            [T987, XXXX, X, AKQJ], // W
        ],
    };
    const SOLUTION: crate::ddTableResults = crate::ddTableResults {
        resTable: [[5; 4]; 5],
    };
    const PAR: crate::parResultsMaster = crate::parResultsMaster {
        score: 0,
        number: 1,
        contracts: [NO_CONTRACT; 10],
    };
    check(DEAL, SOLUTION, [PAR; 2]);
}

/// A symmetric deal where everyone makes 1NT but no suit contract
///
/// This example is taken from
/// <http://www.rpbridge.net/7a23.htm#2>.
#[test]
#[allow(clippy::unusual_byte_groupings)]
fn solve_everyone_makes_1nt() {
    const A54: core::ffi::c_uint = 0b10000_0000_1100_00;
    const QJ32: core::ffi::c_uint = 0b00110_0000_0011_00;
    const K976: core::ffi::c_uint = 0b01000_1011_0000_00;
    const T8: core::ffi::c_uint = 0b00001_0100_0000_00;

    const DEAL: crate::ddTableDeal = crate::ddTableDeal {
        cards: [
            [A54, QJ32, K976, T8], // N
            [T8, A54, QJ32, K976], // E
            [K976, T8, A54, QJ32], // S
            [QJ32, K976, T8, A54], // W
        ],
    };
    const SOLUTION: crate::ddTableResults = crate::ddTableResults {
        resTable: [[6; 4], [6; 4], [6; 4], [6; 4], [7; 4]],
    };
    const NS: crate::parResultsMaster = crate::parResultsMaster {
        score: 90,
        number: 1,
        contracts: [
            crate::contractType {
                level: 1,
                denom: 0, // notrump
                seats: 4,
                underTricks: 0,
                overTricks: 0,
            },
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
        ],
    };
    const EW: crate::parResultsMaster = crate::parResultsMaster {
        score: 90,
        number: 1,
        contracts: [
            crate::contractType {
                level: 1,
                denom: 0, // notrump
                seats: 5,
                underTricks: 0,
                overTricks: 0,
            },
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
            NO_CONTRACT,
        ],
    };
    check(DEAL, SOLUTION, [NS, EW]);
}
