package main

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

const MaxInt = int(^uint(0) >> 1)

func main() {
	inp := StdinToStringSlice()
	start(inp)
	part1()
	part2()
}

var points []RegionPos
var boundaries []RegionPos
var allPoints []RegionPos
var maxX, maxY, minX, minY int

func start(inp []string) {
	maxX, maxY = 0, 0
	minX, minY = MaxInt, MaxInt

	for _, v := range inp {
		pts := strings.Split(v, ", ")
		x, y := Atoi(pts[0]), Atoi(pts[1])
		p := RegionPos{x: x, y: y}
		points = append(points, p)
		if x < minX {
			minX = x
		}
		if x > maxX {
			maxX = x
		}
		if y < minY {
			minY = y
		}
		if y > maxY {
			maxY = y
		}
	}
	// Find the points that are the closest to the boundaries
	for x := minX; x <= maxX; x++ {
		boundaries = append(boundaries, *NewRegionPos(x, minY))
		boundaries = append(boundaries, *NewRegionPos(x, maxY))
	}
	for y := minY; y <= maxY; y++ {
		boundaries = append(boundaries, *NewRegionPos(minX, y))
		boundaries = append(boundaries, *NewRegionPos(maxX, y))
	}
	for i := range boundaries {
		closest := boundaries[i].findClosest(points)
		if len(closest) == 1 {
			for j := range points {
				if points[j].x == closest[0].x && points[j].y == closest[0].y {
					points[j].ignored = true
				}
			}
		} else {
			boundaries[i].ignored = true
		}
	}
}

func part1() {
	for x := minX; x <= maxX; x++ {
		for y := minY; y <= maxY; y++ {
			p := NewRegionPos(x, y)
			closest := p.findClosest(points)
			if len(closest) == 1 {
				c := closest[0]
				for i := range points {
					if points[i].ignored {
						continue
					}
					if points[i].x == c.x && points[i].y == c.y {
						points[i].regionSize++
						break
					}
				}
			}
		}
	}
	var largestSize int
	for _, v := range points {
		if !v.ignored {
			if v.regionSize > largestSize {
				largestSize = v.regionSize
			}
		}
	}
	fmt.Println("= Part 1 =")
	fmt.Println(largestSize)
}

func part2() {
	minDistance := 10000

	var regionCount int
	for y := minY - minDistance; y <= maxY+minDistance; y++ {
		for x := minX - minDistance; x <= maxX+minDistance; x++ {
			p := NewRegionPos(x, y)
			if p.sumOfDistances(minDistance, points) < minDistance {
				regionCount++
			}
		}
	}
	fmt.Println("= Part 2 =")
	fmt.Println(regionCount)
}

func manhattanDistance(x1, y1, x2, y2 int) int {
	return int(math.Abs(float64(x1)-float64(x2)) + math.Abs(float64(y1)-float64(y2)))
}

type RegionPos struct {
	x, y       int
	regionSize int
	closestPos *RegionPos
	ignored    bool
}

func NewRegionPos(x, y int) *RegionPos {
	b := &RegionPos{}
	b.x = x
	b.y = y
	return b
}

func (p *RegionPos) distanceToPos(o *RegionPos) int {
	return manhattanDistance(p.x, p.y, o.x, o.y)
}

func (b *RegionPos) sumOfDistances(d int, list []RegionPos) int {
	var ret int
	for _, p := range list {
		ret += b.distanceToPos(&p)
	}
	return ret
}

func (b *RegionPos) findClosest(list []RegionPos) []RegionPos {
	var ret []RegionPos
	minD := MaxInt
	for _, p := range list {
		test := b.distanceToPos(&p)
		if test < minD {
			minD = test
			ret = ret[:0]
			ret = append(ret, p)
		} else if test == minD {
			ret = append(ret, p)
		}
	}
	return ret
}

func (b *RegionPos) string() string {
	return fmt.Sprintf("%d;%d", b.x, b.y)
}

func StdinToStringSlice() []string {
	var input []string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = append(input, scanner.Text())
	}
	return input
}

func Atoi(i string) int {
	var ret int
	var err error
	if ret, err = strconv.Atoi(i); err != nil {
		log.Fatal("Invalid Atoi")
	}
	return ret
}
