package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

type MoveDir rune

const (
	MoveDirUp    MoveDir = 'U'
	MoveDirDown          = 'D'
	MoveDirLeft          = 'L'
	MoveDirRight         = 'R'
)

type Path struct {
	Sx, Sy int
	Dx, Dy int
	Ox, Oy int
}

func ToPaths(moves []string) []Path {
	var cx, cy int
	paths := make([]Path, len(moves))
	for i := range moves {
		var path Path
		path.Sx = cx
		path.Sy = cy
		path.Dx = cx
		path.Dy = cy
		path.Ox = cx
		path.Oy = cy
		dist, _ := strconv.Atoi(string(moves[i][1:]))
		switch MoveDir(moves[i][0]) {
		case MoveDirUp:
			cy += dist
			path.Dy = cy
		case MoveDirDown:
			cy -= dist
			path.Sy = cy
		case MoveDirLeft:
			cx -= dist
			path.Sx = cx
		case MoveDirRight:
			cx += dist
			path.Dx = cx
		}
		paths[i] = path
	}

	return paths
}

func abs(x int) int {
	if x > 0 {
		return x
	}
	return -x
}

type Crossing struct {
	X, Y int
}

func findCrossings(pathA, pathB []Path, debug bool) []Crossing {
	crossings := make([]Crossing, 0, 10)
	for _, a := range pathA {
		for _, b := range pathB {
			var builder strings.Builder
			fmt.Fprintf(&builder, "checking %+v vs %+v... ", a, b)
			if a.Sy == a.Dy && b.Sx == b.Dx {
				fmt.Fprintf(&builder, "potential")
				if a.Sy >= b.Sy && a.Sy <= b.Dy {
					fmt.Fprintf(&builder, ", in y")
					if b.Sx >= a.Sx && b.Sx <= a.Dx {
						fmt.Fprintf(&builder, ", collision! %d %d", b.Sx, a.Sy)
						crossings = append(crossings, Crossing{
							X: b.Sx,
							Y: a.Sy,
						})
					}
				}
			} else if a.Sx == a.Dx && b.Sy == b.Dy {
				fmt.Fprintf(&builder, "potential")
				if a.Sx >= b.Sx && a.Sx <= b.Dx {
					fmt.Fprintf(&builder, ", in x")
					if b.Sy >= a.Sy && b.Sy <= a.Dy {
						fmt.Fprintf(&builder, ", collision! %d %d", a.Sx, b.Sy)
						crossings = append(crossings, Crossing{
							X: a.Sx,
							Y: b.Sy,
						})
					}
				}
			}
			if debug {
				log.Println(builder.String())
			}
		}
	}
	return crossings
}

func FindClosestCrossing(pathA, pathB []Path, debug bool) int {
	dist := 999999

	crossings := findCrossings(pathA, pathB, debug)
	for _, c := range crossings {
		if c.X != 0 && c.Y != 0 && abs(c.X)+abs(c.Y) < dist {
			dist = abs(c.X) + abs(c.Y)
		}
	}

	return dist
}

func countSteps(path []Path, crossings []Crossing) map[Crossing]int {
	steps := make(map[Crossing]int)
	for _, c := range crossings {
		for _, p := range path {
			if p.Sx == p.Dx && p.Sx == c.X && c.Y >= p.Sy && c.Y <= p.Dy {
				steps[c] += abs(c.Y - p.Oy)
				break
			} else if p.Sy == p.Dy && p.Sy == c.Y && c.X >= p.Sx && c.X <= p.Dx {
				steps[c] += abs(c.X - p.Ox)
				break
			} else {
				steps[c] += (p.Dx - p.Sx) + (p.Dy - p.Sy)
			}
		}
	}
	return steps
}

func FindShortestCrossing(pathA, pathB []Path, debug bool) int {
	crossings := findCrossings(pathA, pathB, debug)
	cA := countSteps(pathA, crossings)
	cB := countSteps(pathB, crossings)

	dist := 999999
	for _, c := range crossings {
		if c.X != 0 && c.Y != 0 && cA[c]+cB[c] < dist {
			dist = cA[c] + cB[c]
		}
	}
	return dist
}

func main() {
	input, err := os.Open("input.txt")
	if err != nil {
		log.Fatal(fmt.Errorf("could not open input file: %w", err))
	}
	defer input.Close()

	scanner := bufio.NewScanner(input)
	scanner.Split(bufio.ScanLines)
	var wires [][]Path
	for scanner.Scan() {
		m := strings.Split(strings.TrimSpace(scanner.Text()), ",")
		wires = append(wires, ToPaths(m))
	}
	fmt.Printf("%+v\n", wires)

	dist := FindClosestCrossing(wires[0], wires[1], false)
	fmt.Printf("Closest: %d\n", dist)

	dist = FindShortestCrossing(wires[0], wires[1], false)
	fmt.Printf("Shortest: %d\n", dist)
}