use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

fn get_input() -> i32 {
    let mut file = File::open("input.txt").unwrap();
    let mut content = String::new();

    file.read_to_string(&mut content).unwrap();
    content.parse().unwrap()
}

#[derive(Copy, Clone, Debug)]
enum Direction {
    XPos,
    XNeg,
    YPos,
    YNeg
}

#[derive(Copy, Clone, Debug)]
struct 
 {
    stride: i32,
    left: i32,
    x: i32,
    y: i32,
    dir: Direction
}

impl State {
    fn move_next(self: &mut State) {
        if self.left > 0 {
            match self.dir {
                Direction::XPos => self.x += 1,
                Direction::XNeg => self.x -= 1,
                Direction::YPos => self.y += 1,
                Direction::YNeg => self.y -= 1
            }
            self.left -= 1;
        } else {
            match self.dir {
                Direction::XPos => {
                    self.dir = Direction::YPos;
                    self.y += 1;
                },
                Direction::YPos => {
                    self.stride += 1;
                    self.dir = Direction::XNeg;
                    self.x -= 1;
                },
                Direction::XNeg => {
                    self.dir = Direction::YNeg;
                    self.y -= 1;
                },
                Direction::YNeg => {
                    self.stride += 1;
                    self.dir = Direction::XPos;
                    self.x += 1;
                }
            }
            self.left = self.stride - 1;
        }
    }

    fn move_next_value(self: &mut State, grid: &mut HashMap<(i32, i32), i32>) -> i32 {
        self.move_next();
        let mut v = 0;

        v += grid.get(&(self.x + 1, self.y)).unwrap_or(&0);
        v += grid.get(&(self.x - 1, self.y)).unwrap_or(&0);
        v += grid.get(&(self.x, self.y + 1)).unwrap_or(&0);
        v += grid.get(&(self.x, self.y - 1)).unwrap_or(&0);
        v += grid.get(&(self.x + 1, self.y + 1)).unwrap_or(&0);
        v += grid.get(&(self.x + 1, self.y - 1)).unwrap_or(&0);
        v += grid.get(&(self.x - 1, self.y + 1)).unwrap_or(&0);
        v += grid.get(&(self.x - 1, self.y - 1)).unwrap_or(&0);

        grid.insert((self.x, self.y), v);

        v
    }
}

fn part_1(input: i32) -> i32 {
    let mut state = State { stride: 1, left: 1, x: 0, y: 0, dir: Direction::XPos };

    for _ in 1 .. input {
        state.move_next();
    }

    state.x.abs() + state.y.abs()
}

fn part_2(input: i32) -> i32 {
    let mut state = State { stride: 1, left: 1, x: 0, y: 0, dir: Direction::XPos };
    let mut grid = HashMap::new();
    grid.insert((0, 0), 1);

    let mut v = 1;

    while v <= input {
        v = state.move_next_value(&mut grid);
    }

    v
}

fn main() {
    let input = get_input();

    println!("Part 1 distance: {}", part_1(input));
    println!("Part 2 value: {}", part_2(input));
}

State