use std::collections::HashMap;
use std::{
    fs::File,
    io::{prelude::*, BufReader},
    path::Path,
};

fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> {
    let file = File::open(filename).expect("no such file");
    let buf = BufReader::new(file);
    buf.lines()
        .map(|l| l.expect("Could not parse line"))
        .collect()
}
// ---

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
enum Move {
    Rock,
    Paper,
    Scissors,
    INVALID,
    Win,
    Lose,
    Tie,
}

fn main() {
    let lines = lines_from_file("../full/day02.txt");
    part1(&lines);
    part2(&lines);
}

fn to_enum(s: &str, part: i32) -> Move {
    if s == "A" {
        return Move::Rock;
    } else if s == "B" {
        return Move::Paper;
    } else if s == "C" {
        return Move::Scissors;
    } else if s == "X" {
        if part == 1 {
            return Move::Rock;
        } else {
            return Move::Lose;
        }
    } else if s == "Y" {
        if part == 1 {
            return Move::Paper;
        } else {
            return Move::Tie;
        }
    } else if s == "Z" {
        if part == 1 {
            return Move::Scissors;
        } else {
            return Move::Win;
        }
    }
    return Move::INVALID;
}

fn parse(lines: &Vec<String>, part: i32) -> (Vec<Move>, Vec<Move>) {
    let mut oppt: Vec<Move> = Vec::new();
    let mut mine: Vec<Move> = Vec::new();
    for line in lines {
        let split = line.split_whitespace().collect::<Vec<_>>();
        oppt.push(to_enum(split[0], part));
        mine.push(to_enum(split[1], part));
    }
    return (oppt, mine);
}

fn score_1(o: &Move, m: Move) -> i32 {
    let mut score: i32 = 0;
    let scores: HashMap<Move, i32> = HashMap::from([
        (Move::Rock, 1),
        (Move::Paper, 2),
        (Move::Scissors, 3),
        (Move::Win, 6),
        (Move::Tie, 3),
        (Move::Lose, 0),
    ]);
    // For my choice
    score += scores[&m];

    // for which outcome
    if o == &m {
        //println!("Tie");
        score += scores[&Move::Tie];
    } else if (m == Move::Paper && o == &Move::Rock)
        || (m == Move::Rock && o == &Move::Scissors)
        || (m == Move::Scissors && o == &Move::Paper)
    {
        //println!("Win");
        score += scores[&Move::Win];
    } else {
        //println!("Lose");
        score += scores[&Move::Lose];
    }

    return score;
}

fn part1(lines: &Vec<String>) {
    let (oppt, mine) = parse(lines, 1);
    let mut score: i32 = 0;
    for (idx, el) in oppt.iter().enumerate() {
        score += score_1(el, mine[idx]);
    }
    println!("{}", score);
}

fn score_2(o: &Move, m: Move) -> i32 {
    let mut new_move: &Move = &Move::INVALID;
    let mut score: i32 = 0;
    let scores: HashMap<Move, i32> = HashMap::from([
        (Move::Rock, 1),
        (Move::Paper, 2),
        (Move::Scissors, 3),
        (Move::Win, 6),
        (Move::Tie, 3),
        (Move::Lose, 0),
    ]);
    // For my choice
    if m == Move::Tie {
        new_move = o;
    } else if m == Move::Lose {
        if o == &Move::Rock {
            new_move = &Move::Scissors;
        } else if o == &Move::Paper {
            new_move = &Move::Rock;
        } else {
            new_move = &Move::Paper;
        }
    } else if m == Move::Win {
        if o == &Move::Rock {
            new_move = &Move::Paper;
        } else if o == &Move::Paper {
            new_move = &Move::Scissors;
        } else {
            new_move = &Move::Rock;
        }
    }

    score += scores[new_move];
    score += scores[&m];

    return score;
}

fn part2(lines: &Vec<String>) {
    let (oppt, mine) = parse(lines, 2);
    let mut score: i32 = 0;
    for (idx, el) in oppt.iter().enumerate() {
        score += score_2(el, mine[idx]);
    }
    println!("{}", score);
}
