package main

import (
	"fmt"
	"math"
	"os"
	"strings"

	h "git.bullercodeworks.com/brian/adventofcode/helpers"
)

var part = -1
var testRow = 2000000
var watch bool
var watchRows = 1

func main() {
	inp := h.StdinToStringSlice()
	if len(os.Args) > 1 {
		for _, arg := range os.Args[1:] {
			if strings.HasPrefix(arg, "--part") || strings.HasPrefix(arg, "-p") {
				if strings.Contains(arg, "=") {
					pts := strings.Split(arg, "=")
					part = h.Atoi(pts[1])
				}
			} else if strings.HasPrefix(arg, "--row") || strings.HasPrefix(arg, "-r") {
				if strings.Contains(arg, "=") {
					pts := strings.Split(arg, "=")
					testRow = h.Atoi(pts[1])
				}
			} else if strings.HasPrefix(arg, "--watch") || strings.HasPrefix(arg, "-w") {
				watch = true
				if strings.Contains(arg, "=") {
					pts := strings.Split(arg, "=")
					if pts[1] == "full" {
						watchRows = -1
					} else {
						watchRows = h.Atoi(pts[1])
					}
				}
			}
		}
	}

	if part == 1 || part == -1 {
		part1(inp)
	}
	if part == 2 || part == -1 {
		part2(inp)
	}
}

func part1(inp []string) {
	min_x, max_x := math.MaxInt, math.MinInt
	var spots [][]int
	for i := range inp {
		s, b := strToCoords(inp[i])
		xs, ys, xb, yb := s.X, s.Y, b.X, b.Y
		dist := s.Distance(b)
		l, r := xs-dist, xs+dist
		min_x = h.Min(min_x, l)
		max_x = h.Max(max_x, r)
		spots = append(spots, []int{xs, ys, xb, yb, dist})
	}
	var nopeCount int
	for x := min_x; x <= max_x; x++ {
		for _, c := range spots {
			if x == c[2] && testRow == c[3] {
				break
			}
			if h.ManhattanDistance(x, testRow, c[0], c[1]) <= c[4] {
				nopeCount++
				break
			}
		}
	}
	fmt.Println("# Part 1")
	fmt.Println(nopeCount)
}

func part2(inp []string) {
	var spots [][]int
	for i := range inp {
		s, b := strToCoords(inp[i])
		xs, ys, xb, yb := s.X, s.Y, b.X, b.Y
		dist := s.Distance(b)
		spots = append(spots, []int{xs, ys, xb, yb, dist})
	}
	min, max := 0, testRow*2
	// Brute-force it
	for y := min; y <= max; y++ {
	SEARCH:
		for x := min; x <= max; x++ {
			for _, c := range spots {
				if dx, dy := c[0]-x, c[1]-y; h.Abs(dx)+h.Abs(dy) <= c[4] {
					// Jump across the sensor's scan area
					x += c[4] - h.Abs(dy) + dx
					continue SEARCH
				}
			}
			// If we get here, we found it.
			fmt.Println("# Part 2")
			fmt.Println(x*4000000 + y)
			return
		}
	}
}

func strToCoords(s string) (h.Coordinate, h.Coordinate) {
	var sensor, beacon h.Coordinate
	r := strings.NewReader(s)
	fmt.Fscanf(r, "Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", &sensor.X, &sensor.Y, &beacon.X, &beacon.Y)
	return sensor, beacon
}