package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"strings"
)

func abs(t int) int {
	if t < 0 {
		return -t
	}
	return t
}

type Point struct {
	ID  byte
	X   int
	Y   int
	Inf bool
}

func (p *Point) Equal(x, y int) bool {
	return (p.X == x && p.Y == y)
}

func (p *Point) Distance(x, y int) int {
	return abs(p.X-x) + abs(p.Y-y)
}

func (p Point) String() string {
	var inf string
	if p.Inf {
		inf = " INF"
	}
	return fmt.Sprintf("%c %dx%d%s", p.ID, p.X, p.Y, inf)
}

type Puzzle struct {
	Points     []Point
	MaxX, MaxY int
}

func (p *Puzzle) Add(x, y int) {
	p.Points = append(p.Points, Point{
		ID: byte('"' + len(p.Points)),
		X:  x,
		Y:  y,
	})
	if x > p.MaxX {
		p.MaxX = x
	}
	if y > p.MaxY {
		p.MaxY = y
	}
}

func (p *Puzzle) Solution1() int {
	size := (p.MaxY + 2) * (p.MaxX + 2)
	grid := make([]byte, size)
	for y := 0; y < p.MaxY+2; y++ {
		for x := 0; x < p.MaxX+2; x++ {
			i := (y * (p.MaxX + 2)) + x
			c := byte('.')

			if x == 0 || x == p.MaxX+1 || y == 0 || y == p.MaxY+1 {
				c = 219
			}

			for _, point := range p.Points {
				if point.Equal(x, y) {
					c = point.ID
					break
				}
			}
			grid[i] = c
		}
	}

	dist := make([]int, len(p.Points))
	for y := 0; y < p.MaxY+2; y++ {
		for x := 0; x < p.MaxX+2; x++ {
			skip := false
			for t, point := range p.Points {
				if point.Equal(x, y) {
					skip = true
					break
				}
				dist[t] = point.Distance(x, y)
			}
			if skip {
				continue
			}

			var id byte
			shortest := math.MaxInt32
			for t, d := range dist {
				if d < shortest {
					shortest = d
					id = p.Points[t].ID
				}
			}
			matches := 0
			for _, d := range dist {
				if d == shortest {
					matches++
				}
			}

			i := (y * (p.MaxX + 2)) + x
			if grid[i] == 219 {
				for t := range p.Points {
					if p.Points[t].ID == id {
						p.Points[t].Inf = true
						break
					}
				}
			} else {
				if matches > 1 {
					id = '!'
				}
				grid[i] = id
			}
		}
	}

	/*
		for y := 0; y < p.MaxY+2; y++ {
			for x := 0; x < p.MaxX+2; x++ {
				i := (y * (p.MaxX + 2)) + x
				fmt.Printf("%c", grid[i])
			}
			fmt.Printf("\n")
		}
	*/

	largest := 0
	for _, point := range p.Points {
		if point.Inf {
			continue
		}

		count := 0
		for _, c := range grid {
			if c == point.ID {
				count++
			}
		}
		if count > largest {
			largest = count
		}
	}

	return largest
}

func (p *Puzzle) Solution2() int {
	size := (p.MaxY + 2) * (p.MaxX + 2)
	grid := make([]byte, size)
	for y := 0; y < p.MaxY+2; y++ {
		for x := 0; x < p.MaxX+2; x++ {
			i := (y * (p.MaxX + 2)) + x
			c := byte('.')

			if x == 0 || x == p.MaxX+1 || y == 0 || y == p.MaxY+1 {
				c = 219
			}

			for _, point := range p.Points {
				if point.Equal(x, y) {
					c = point.ID
					break
				}
			}

			grid[i] = c
		}
	}

	drawn := 0
	for y := 0; y < p.MaxY+2; y++ {
		for x := 0; x < p.MaxX+2; x++ {
			total := 0
			for _, point := range p.Points {
				total += point.Distance(x, y)
			}

			i := (y * (p.MaxX + 2)) + x
			if grid[i] == 219 {
				continue
			} else {
				if total < 10000 {
					grid[i] = '#'
					drawn++
				}
			}
		}
	}

	/*for y := 0; y < p.MaxY+2; y++ {
		for x := 0; x < p.MaxX+2; x++ {
			i := (y * (p.MaxX + 2)) + x
			fmt.Printf("%c", grid[i])
		}
		fmt.Printf("\n")
	}*/

	return drawn
}

func (p Puzzle) String() string {
	output := strings.Builder{}
	output.WriteString(fmt.Sprintf("%dx%d\n", p.MaxX, p.MaxY))
	for y := 0; y < p.MaxY+2; y++ {
		for x := 0; x < p.MaxX+2; x++ {
			c := byte('.')
			for _, point := range p.Points {
				if point.Equal(x, y) {
					c = point.ID
					break
				}
			}
			output.WriteByte(c)
		}
		output.WriteByte('\n')
	}
	return output.String()
}

func main() {
	input, err := os.Open("input.txt")
	if err != nil {
		panic(err.Error())
	}
	defer input.Close()
	scanner := bufio.NewScanner(input)

	p := Puzzle{
		Points: make([]Point, 0, 1024),
	}
	for scanner.Scan() {
		var x, y int
		fmt.Sscanf(scanner.Text(), "%d,%d", &x, &y)
		p.Add(x, y)
	}
	if err := scanner.Err(); err != nil {
		panic(err.Error())
	}

	fmt.Println(p.Solution1())
	fmt.Println(p.Solution2())
}