package day06

import (
	"strings"
)

type Point struct {
	x         int
	y         int
	direction int
}

const (
	Up    = 0
	Right = 1
	Down  = 2
	Left  = 3
)

var rows, cols int

var moves = []Point{
	{-1, 0, Up},
	{0, 1, Up},
	{1, 0, Up},
	{0, -1, Up},
}

func Part1(input string) int {
	grid, start, direction := ParseInput(input)
	total := PredictPath(grid, start, direction)
	return total
}

func Part2(input string) int {
	grid, start, direction := ParseInput(input)
	total := FindLoopingPositions(grid, start, direction)
	return total
}

func ParseInput(input string) ([][]rune, Point, int) {
	lines := strings.Split(strings.TrimSpace(input), "\n")
	rows = len(lines)
	cols = len(lines[0])
	grid := make([][]rune, rows)

	var start Point
	var direction int

	for i := 0; i < rows; i++ {
		grid[i] = []rune(lines[i])
		for j, char := range lines[i] {
			if char == '^' {
				start = Point{i, j, Up}
				direction = Up
			} else if char == '>' {
				start = Point{i, j, Right}
				direction = Right
			} else if char == 'v' {
				start = Point{i, j, Down}
				direction = Down
			} else if char == '<' {
				start = Point{i, j, Left}
				direction = Left
			}
		}
	}

	return grid, start, direction
}

func PredictPath(grid [][]rune, start Point, direction int) int {
	visited := make(map[Point]bool)
	current := start

	visited[current] = true

	// do while
	for {
		next := Point{current.x + moves[direction].x, current.y + moves[direction].y, Up}

		if next.x < 0 || next.x >= rows || next.y < 0 || next.y >= cols {
			break
		}

		if grid[next.x][next.y] == '#' {
			direction = (direction + 1) % 4
		} else {
			current = next
			visited[current] = true
		}
	}

	return len(visited)
}

func FindLoopingPositions(grid [][]rune, start Point, direction int) int {
	possiblePositions := 0

	for i := 0; i < rows; i++ {
		for j := 0; j < cols; j++ {
			if grid[i][j] != '.' || (i == start.x && j == start.y) {
				continue
			}

			grid[i][j] = '#'
			if IsLooping(grid, start, direction) {
				possiblePositions++
			}
			grid[i][j] = '.'
		}
	}

	return possiblePositions
}

func IsLooping(grid [][]rune, start Point, direction int) bool {
	visited := make(map[Point]int)
	current := start

	step := 0
	for {
		step++
		next := Point{current.x + moves[direction].x, current.y + moves[direction].y, direction}

		if next.x < 0 || next.x >= rows || next.y < 0 || next.y >= cols {
			return false
		}

		if grid[next.x][next.y] == '#' {
			direction = (direction + 1) % 4
		} else {
			current = next

			if visitStep, ok := visited[current]; ok && step-visitStep > 4 {
				return true
			}

			visited[current] = step
		}
	}
}
