package main

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

const (
	gridX    = 1000
	gridY    = 1000
	gridSize = gridX * gridY
)

type Puzzle struct {
	Collides map[int]bool
	Grid     [gridSize]int
}

func (p *Puzzle) Add(num, x, y, w, h int) {
	if x+w > gridX || y+h > gridY {
		return
	}

	if p.Collides == nil {
		p.Collides = make(map[int]bool)
	}
	if _, ok := p.Collides[num]; !ok {
		p.Collides[num] = false
	}

	for yy := y; yy < y+h; yy++ {
		for xx := x; xx < x+w; xx++ {
			i := (yy * gridX) + xx
			if p.Grid[i] == 0 {
				p.Grid[i] = num
			} else {
				p.Collides[num] = true
				p.Collides[p.Grid[i]] = true
				p.Grid[i] = -1
			}
		}
	}
}

func (p *Puzzle) NumCollisions() int {
	collisions := 0
	for y := 0; y < gridY; y++ {
		for x := 0; x < gridX; x++ {
			i := (y * gridX) + x
			if p.Grid[i] > 1 {
				collisions++
			}
		}
	}
	return collisions
}

func (p *Puzzle) Spared() int {
	for id, collides := range p.Collides {
		if !collides {
			return id
		}
	}
	return -1
}

func (p Puzzle) String() string {
	output := strings.Builder{}
	output.Grow(gridSize)
	for y := 0; y < gridY; y++ {
		for x := 0; x < gridX; x++ {
			i := (y * gridX) + x
			switch p.Grid[i] {
			case 0:
				output.WriteByte('.')
			case -1:
				output.WriteByte('x')
			default:
				output.WriteByte('+')
			}
		}
		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{}
	for scanner.Scan() {
		var num, x, y, w, h int
		fmt.Sscanf(scanner.Text(), "#%d @ %d,%d: %dx%d", &num, &x, &y, &w, &h)
		p.Add(num, x, y, w, h)
	}
	if err := scanner.Err(); err != nil {
		panic(err.Error())
	}

	fmt.Println("Collisions:", p.NumCollisions())
	fmt.Println("Spared:", p.Spared())
}