use std::collections::VecDeque;

pub fn parse_snafus(input: &str) -> Vec<i64> {
    input.lines().map(snafu_to_num).collect()
}

pub fn snafu_to_num(snafu: &str) -> i64 {
    let mut pow = 1;
    let mut result = 0;
    for c in snafu.chars().rev() {
        let digit = match c {
            '=' => -2,
            '-' => -1,
            '0' => 0,
            '1' => 1,
            '2' => 2,
            _ => panic!("Unknown digit {}", c),
        };
        result += pow * digit;
        pow *= 5;
    }
    result
}

pub fn num_to_snafu(num: i64) -> String {
    assert!(num > 0);

    let exp = (num as f64).log(5.0).floor() as u32 + 1;
    let mut result = VecDeque::with_capacity((exp as usize) + 1);
    let mut pow = 5_i64.pow(exp);
    let mut rem = num;

    while pow >= 1 {
        let digit = rem / pow;
        rem %= pow;
        result.push_back(digit);
        pow /= 5;
    }

    for i in (0..result.len()).rev() {
        if result[i] > 2 {
            result[i] -= 5;
            result[i - 1] += 1;
        }
    }

    result
        .iter()
        .skip_while(|d| **d == 0)
        .map(|d| match d {
            -2 => '=',
            -1 => '-',
            0 => '0',
            1 => '1',
            2 => '2',
            _ => panic!("Unknown digit {}", d),
        })
        .collect()
}

#[cfg(test)]
mod tests {
    #[test]
    fn num_to_snafu_examples() {
        use crate::day25::num_to_snafu;
        assert_eq!(num_to_snafu(1), "1");
        assert_eq!(num_to_snafu(2), "2");
        assert_eq!(num_to_snafu(3), "1=");
        assert_eq!(num_to_snafu(4), "1-");
        assert_eq!(num_to_snafu(5), "10");
        assert_eq!(num_to_snafu(6), "11");
        assert_eq!(num_to_snafu(7), "12");
        assert_eq!(num_to_snafu(8), "2=");
        assert_eq!(num_to_snafu(9), "2-");
        assert_eq!(num_to_snafu(10), "20");
        assert_eq!(num_to_snafu(15), "1=0");
        assert_eq!(num_to_snafu(20), "1-0");
        assert_eq!(num_to_snafu(2022), "1=11-2");
        assert_eq!(num_to_snafu(12345), "1-0---0");
        assert_eq!(num_to_snafu(314159265), "1121-1110-1=0");
        assert_eq!(num_to_snafu(4890), "2=-1=0");
    }
}