#![allow(unused)]
use std::{cell::RefCell, ops::DerefMut};

use itertools::Itertools;
use rand::seq::SliceRandom;
use rand_pcg::Pcg64Mcg;
use settheory::{CardSelection, Deck, Set};

const DIM: usize = 4;
const RUNS: usize = 10000;

thread_local! {
    static RNG: RefCell<Pcg64Mcg> = RefCell::new(Pcg64Mcg::new(0xcafef00dd15ea5e5));
}

fn main() {
    let mut avg = 0;
    let mut min = usize::MAX;
    let mut max = usize::MIN;
    let mut dist = [0; 7];
    let mut avg_sets_in_sel = 0;
    let mut selection_count = 0;
    let mut max_sets_in_sel = usize::MIN;
    let mut max_sets_sel = CardSelection::new();
    let mut sets_in_sel_dist = [0; 16];
    let mut avg_same_count = 0;
    let mut no_same_count = 0;
    let mut games_no_same_set = 0;
    'runs: for _ in 0..RUNS {
        let mut deck = Deck::new();
        let mut selection: CardSelection<DIM> = deck.draw(12).collect();
        let mut no_same_set = false;
        loop {
            let mut sets = selection.sets().collect_vec();
            if sets.is_empty() {
                deck.reshuffle(&selection);
                if !deck.has_sets() {
                    avg += deck.len();
                    min = usize::min(min, deck.len());
                    max = usize::max(max, deck.len());
                    dist[deck.len() / 3] += 1;
                    games_no_same_set += if no_same_set { 1 } else { 0 };
                    continue 'runs;
                }
                selection = deck.draw(12).collect();
                continue;
            }
            avg_sets_in_sel += sets.len();
            selection_count += 1;
            if max_sets_in_sel < sets.len() {
                max_sets_in_sel = sets.len();
                max_sets_sel = selection.clone();
            }
            sets_in_sel_dist[sets.len()] += 1;
            // let set_found = RNG
            //     .with(|rng| sets.choose(rng.borrow_mut().deref_mut()))
            //     .unwrap()
            //     .clone();
            sets.sort_unstable_by_key(Set::same_count);
            let set_found = sets.pop().unwrap();
            // let set_found = sets[0].clone();
            avg_same_count += set_found.same_count() as u64;
            if set_found.same_count() == 0 {
                no_same_count += 1;
                no_same_set = true;
            }

            selection -= set_found;
        }
    }

    println!(
        "The average number of cards left at the end was {:.3}",
        avg as f32 / RUNS as f32
    );
    println!("The minimum number was {min}");
    println!("The maximum number was {max}");
    println!(
        "Distribution:\n{}\n{}\n{}",
        (0..7)
            .map(|i| format!("{:6}", i))
            .reduce(|a, b| a + "  |" + &b)
            .unwrap(),
        dist.into_iter()
            .map(|i| format!("{:6}", i))
            .reduce(|a, b| a + "  |" + &b)
            .unwrap(),
        dist.into_iter()
            .map(|i| format!("{:6.1}%", i as f32 * 100.0 / RUNS as f32))
            .reduce(|a, b| a + " |" + &b)
            .unwrap(),
    );
    println!(
        "The average number of sets in a selection with sets was {:.3}",
        avg_sets_in_sel as f32 / selection_count as f32
    );
    println!("The maximum number of sets in one selection was {max_sets_in_sel}");
    println!("Selection: {max_sets_sel}");
    println!(
        "Distribution:\n{}\n{}\n{}",
        (0..16)
            .map(|i| format!("{:6}", i))
            .reduce(|a, b| a + "  |" + &b)
            .unwrap(),
        sets_in_sel_dist
            .into_iter()
            .map(|i| format!("{:6}", i))
            .reduce(|a, b| a + "  |" + &b)
            .unwrap(),
        sets_in_sel_dist
            .into_iter()
            .map(|i| format!(
                "{:6.1}%",
                i as f32 * 100.0 / sets_in_sel_dist.iter().sum::<usize>() as f32
            ))
            .reduce(|a, b| a + " |" + &b)
            .unwrap(),
    );
    println!(
        "The average same count was {:.2}",
        avg_same_count as f32 / selection_count as f32
    );
    println!(
        "The average amount of times per game with only sets without same attributes was {}",
        no_same_count as f32 / RUNS as f32
    );
    println!(
        "The percentage of games with sets with no shared attributes was {:.2}%",
        games_no_same_set as f32 * 100.0 / RUNS as f32
    );
}