use crate::game::component::*;
use crate::hand_isomorphism::{hand_index_last, hand_indexer_t, uint_fast32_t};
use rocksdb::{ColumnFamily, IteratorMode, Options, DB};
use std::marker::PhantomData;
use super::abstract_base::*;



#[derive(Debug, Clone, Copy)]
pub enum ResolutionConfig {
    WinningTrace(usize),   // recall from(not sentinel)
    PotentialTrace(usize), //recall from(not sentinel)
}

pub struct FullResolutionAbstr<T> {
    // streets_indexers_1d_index: Vec<usize>,
    recall_froms: Vec<usize>,
    #[cfg(not(feature = "db_inloop"))]
    streets_iso_to_dist_id: Vec<Vec<u32>>,
    #[cfg(feature = "db_inloop")]
    db_api: (DB, Vec<String>),
    _marker: PhantomData<T>,
}

fn get_table_name<T: Singleton + Deal>(street: usize, config: ResolutionConfig) -> String {
    let func = |iso_str, dist_str| -> String {
        format!(
            "{}_{}_to_{}_distribution_id",
            T::GAME_NAME,
            iso_str,
            dist_str
        )
    };

    match config {
        ResolutionConfig::WinningTrace(recall_from) => {
            assert!(T::NUM_STREET as usize > recall_from);
            if street == recall_from {
                func(format!("nriso_{}", street + 1), "winning")
            } else {
                func(
                    format!("priso_{}_from_{}", street + 1, recall_from + 1),
                    "winning_trace",
                )
            }
        }
        ResolutionConfig::PotentialTrace(recall_from) => {
            assert!(T::NUM_STREET as usize - 1 > recall_from);
            if street == recall_from {
                func(format!("nriso_{}", street + 1), "winning")
            } else {
                func(
                    format!("priso_{}_from_{}", street + 1, recall_from + 1),
                    "potential_trace",
                )
            }
        }
    }
}

impl<T> FullResolutionAbstr<T>
where
    T: Singleton + Deal,
{
    pub fn new(configs: Vec<ResolutionConfig>) -> Self {
        assert!(configs.len() == T::NUM_STREET as usize);
        // streets_indexers_1d_index
        let recall_froms = configs
            .iter()
            .cloned()
            // .enumerate()
            .map(|config| {
                let recall_from = match config {
                    ResolutionConfig::PotentialTrace(recall_from) => recall_from,
                    ResolutionConfig::WinningTrace(recall_from) => recall_from,
                };
                recall_from
            })
            .collect::<Vec<usize>>();

        // streets_iso_to_dist_id
        let db_path = format!("data/{}", T::GAME_NAME);
        let options = Options::default();
        let kvcfs = configs
            .iter()
            .cloned()
            .enumerate()
            .map(|(street, config)| get_table_name::<T>(street, config))
            .collect::<Vec<String>>();
        let db = DB::open_cf_for_read_only(
            &options,
            &db_path,
            kvcfs.clone(),
            false, /* enable_statistics */
        )
        .unwrap_or_else(|err| {
            eprintln!(
                "{}//没有这个数据库:{}，或者下列cf不存在:{:?}",
                err,
                db_path,
                kvcfs.clone()
            );
            std::process::exit(1);
        });
        let db_cfs: Vec<ColumnFamily> = Vec::with_capacity(T::NUM_STREET as usize);

        #[cfg(not(feature = "db_inloop"))]
        let mut iso_maps = vec![None; T::NUM_STREET as usize];

        for (street, kvcf) in kvcfs.iter().enumerate() {
            let kvcf = db.cf_handle(kvcf).unwrap_or_else(|| {
                eprintln!("数据库{}中没有列族{}", db_path, kvcf);
                std::process::exit(1);
            });

            #[cfg(not(feature = "db_inloop"))]
            {
                let mut iso_map_universal = Vec::new();
                let iter = db.iterator_cf(kvcf, IteratorMode::Start);
                for  item in iter {
                    let (key, value) = item.unwrap();
                    assert!(key.len() == 4 && value.len() == 4);
                    let iso = u32::from_be_bytes((*key).try_into().unwrap());
                    let distribution_id = u32::from_be_bytes((*value).try_into().unwrap());
                    iso_map_universal.push((iso, distribution_id));
                } //先把列族中的数据以元组方式存起来

                let mut iso_map: Vec<Option<u32>> = vec![None; iso_map_universal.len()];
                for (iso, distribution_id) in iso_map_universal.iter() {
                    iso_map[*iso as usize] = Some(*distribution_id);
                }
                let iso_map: Vec<u32> = iso_map.into_iter().flatten().collect();
                assert!(iso_map.len() == iso_map_universal.len());

                iso_maps[street] = Some(iso_map);
            }
        }

        Self {
            recall_froms,
            #[cfg(not(feature = "db_inloop"))]
            streets_iso_to_dist_id: iso_maps.into_iter().flatten().collect(),
            #[cfg(feature = "db_inloop")]
            db_api: (db, kvcfs),
            _marker: PhantomData,
        }
    }
}

impl<T: Singleton + WaughTrait + 'static> Abstract for FullResolutionAbstr<T> {
    fn abstract_hand(&self, street: usize, hand: &[u8]) -> u32 {
        let game: &'static T = T::instance().as_ref();
        // let street_x_recall_indexer = &game.get_game_indexer().street_x_recall_indexers_1d
            // [self.streets_indexers_1d_index[street]];

        let iso = game.hand_indexify(hand, street, self.recall_froms[street]);

        #[cfg(not(feature = "db_inloop"))]
        {
            self.streets_iso_to_dist_id[street][iso as usize]
        }

        #[cfg(feature = "db_inloop")]
        {
            1
        }
    }
}
