use std::collections::VecDeque;
use std::io::Read;

use common::Solution;

fn winning_marbles(elves: usize, marbles: usize) -> usize {
    let mut scores = vec![0usize; elves];
    let mut state = VecDeque::with_capacity(marbles);
    state.push_front(0);

    for marble in 1..=marbles {
        if marble % 23 == 0 {
            let player = marble % elves;

            for _ in 0..7 {
                let c = state.pop_back().unwrap();
                state.push_front(c);
            }

            let removed = state.pop_back().unwrap();
            scores[player] += removed + marble;

            let c = state.pop_front().unwrap();
            state.push_back(c);
        } else {
            let c = state.pop_front().unwrap();
            state.push_back(c);
            state.push_back(marble);
        }
    }

    *scores.iter().max().unwrap()
}

#[derive(Default)]
pub struct Day09 {}

impl Day09 {
    pub fn new() -> Self {
        Default::default()
    }

    fn read_input(input: &mut dyn Read) -> (usize, usize) {
        let mut data = String::new();
        input.read_to_string(&mut data).unwrap();
        let mut parts = data.split(' ');
        let elves = parts.next().unwrap().parse().unwrap();
        let marbles = parts.nth(5).unwrap().parse().unwrap();

        (elves, marbles)
    }
}

impl Solution for Day09 {
    fn part1(&mut self, input: &mut dyn Read) -> String {
        let (elves, marbles) = Day09::read_input(input);

        winning_marbles(elves, marbles).to_string()
    }

    fn part2(&mut self, input: &mut dyn Read) -> String {
        let (elves, marbles) = Day09::read_input(input);

        winning_marbles(elves, marbles * 100).to_string()
    }
}

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

    #[test]
    fn test_winning_marbles() {
        assert_eq!(32, winning_marbles(9, 25));
        assert_eq!(8317, winning_marbles(10, 1618));
        assert_eq!(146373, winning_marbles(13, 7999));
        assert_eq!(2764, winning_marbles(17, 1104));
        assert_eq!(54718, winning_marbles(21, 6111));
        assert_eq!(37305, winning_marbles(30, 5807));
    }
}
