use super::*;

pub struct Numeral211 {
    game_indexer: WaughStruct,
}

impl Deck for Numeral211 {
    const DECK: &'static [u8] = &NUMERAL_DECK;
}

impl Deal for Numeral211 {
    const NUM_STREET: i32 = DEAL211.deal_hole_street.len() as i32; //有点丑
    const DEAL_HOLE_STREET: &'static [u8] = &DEAL211.deal_hole_street;
    const DEAL_BOARD_STREET: &'static [u8] = &DEAL211.deal_board_street;
    //////////////auto
    const HAND_LEN_STREET: &'static [usize] = &get_hand_len_street::<{ Self::NUM_STREET as usize }>(
        &Self::DEAL_HOLE_STREET,
        &Self::DEAL_BOARD_STREET,
    );
    const HAND_LEN: usize = Self::HAND_LEN_STREET[Self::NUM_STREET as usize - 1];
    const HOLE_LEN_STREET: &'static [usize] =
        &get_card_len_street::<{ Self::NUM_STREET as usize }>(&Self::DEAL_HOLE_STREET);
    const BOARD_LEN_STREET: &'static [usize] =
        &get_card_len_street::<{ Self::NUM_STREET as usize }>(&Self::DEAL_BOARD_STREET);
}

impl Hand for Numeral211 {
    const DEAL_HAND_COMBINATION_STREET: &'static [i32] =
        &deal_hand_combination_street::<{ Self::NUM_STREET as usize }>(
            Self::DECK.len(),
            &Self::DEAL_HOLE_STREET,
            &Self::DEAL_BOARD_STREET,
        );
}

pub mod game_specific {
    use std::sync::Once;
    use once_cell::sync::OnceCell;
    use std::path::Path;
    use std::fs::File;
    use byteorder::LittleEndian;
    use byteorder::ReadBytesExt;
    use std::io::{Read, BufReader};  

    static ONCE: Once = Once::new();
    
    pub const RHODE_ISLAND_2PLUS2_SIZE: usize = 15593606;

    pub static RHODE_ISLAND_2PLUS2_TABLE: OnceCell<Vec<i32>> = OnceCell::new();

    pub fn setup() {
        ONCE.call_once(|| {
            initial_2plus2();
        });
    }

    fn initial_2plus2() {
        RHODE_ISLAND_2PLUS2_TABLE.get_or_init(|| {
            let path = Path::new("data/2plus2/RhodeIslandHandRanks.dat");
            let display = path.display();

            let mut buffer: Vec<i32> = Vec::new();
            let file = File::open(&path).expect("couldn't create file");
            let mut reader = BufReader::new(file);
            loop {
                match reader.read_i32::<LittleEndian>() {
                    Ok(value) => buffer.push(value),
                    Err(_) => break, // 读取到文件末尾时退出循环
                }
            }
            assert_eq!(buffer.len(), RHODE_ISLAND_2PLUS2_SIZE);
            buffer.shrink_to_fit();
            assert_eq!(buffer.capacity(), RHODE_ISLAND_2PLUS2_SIZE);
            buffer
        });
    }
}

impl ShowdownRanker for Numeral211 {
    fn rank_showdown(hand: &[u8]) -> i32 {
        let mut rank: i32 = 53;
        for i in 0..Self::HAND_LEN {
            rank = game_specific::RHODE_ISLAND_2PLUS2_TABLE.get().unwrap()
                [(rank + hand[i] as i32 + 1 + (52 - Self::DECK.len() as i32)) as usize]
        }
        rank = game_specific::RHODE_ISLAND_2PLUS2_TABLE.get().unwrap()[rank as usize];
        rank
    }
}

//////////////////////////////////////////////
#[cfg(not(feature = "rust_waugh"))]
impl WaughTrait for Numeral211 {
    //
    const INDEXER_CONFIGS_1D: &'static [[u8; Self::WAUGH_INDEXER_CONFIG_LEN]] =
        &get_street_indexer_configs_1d::<{ Self::NUM_STREET as usize }>(
            &Self::DEAL_HOLE_STREET,
            &Self::DEAL_BOARD_STREET,
        );
    const INDEXER_STAGES_1D: &'static [i32] = &get_street_indexer_stages_1d::<
        { Self::NUM_STREET as usize },
    >(
        &Self::DEAL_HOLE_STREET, &Self::DEAL_BOARD_STREET
    );

    fn get_game_indexer<'a>(&'a self) -> &'a WaughStruct {
        &self.game_indexer
    }

    fn get_mut_game_indexer<'a>(&'a mut self) -> &'a mut WaughStruct {
        &mut self.game_indexer
    }
}

#[cfg(feature = "rust_waugh")]
impl WaughTrait for Numeral211 {
    //
    const SUITS: usize = 4;
    const RANKS: usize = 10;

    fn get_game_indexer<'a>(&'a self) -> &'a WaughStruct {
        &self.game_indexer
    }

    fn get_mut_game_indexer<'a>(&'a mut self) -> &'a mut WaughStruct {
        &mut self.game_indexer
    }
}

//////////////////////////////////////////////
type GameType = Numeral211;

use std::sync::{Arc, Once};

// 定义静态变量存储单例实例
static mut INSTANCE: *const GameType = std::ptr::null();

// 定义静态变量存储Once类型
static ONCE: Once = Once::new();

impl Singleton for GameType {
    const GAME_NAME: &'static str = "Numeral211";

    fn new() -> Self {
        Self {
            game_indexer: Self::new_game_indexer(),
        }
    }

    fn instance() -> Arc<&'static GameType> {
        ONCE.call_once(|| unsafe { INSTANCE = Box::into_raw(Box::new(GameType::new())) });
        unsafe { Arc::new(&*INSTANCE) } // 访问通过GameType::instance().as_ref();
    }
}

impl Drop for GameType {
    fn drop(&mut self) {
        self.drop_game_indexer();
    }
}
