package main

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

type point struct {
	row, col int
}

type pixels map[point]bool

type image struct {
	IEA    string
	m      pixels
	size   int
	flippy bool
}

var directions = [][2]int{
	{-1, -1},
	{-1, 0},
	{-1, 1},
	{0, -1},
	{0, 0},
	{0, 1},
	{1, -1},
	{1, 0},
	{1, 1},
}

func main() {
	if err := myMain(); err != nil {
		log.Println(err)
	}
}

func myMain() error {
	in, err := parseInput("input.txt")
	if err != nil {
		return err
	}

	log.Println(solveFirst(in, 2))
	log.Println(solveFirst(in, 50))

	return nil
}

func solveFirst(in image, maxRounds int) int {
	for round := 1; round <= maxRounds; round++ {
		next := pixels{}
		for row := -round; row < in.size+round; row++ {
			for col := -round; col < in.size+round; col++ {
				p := point{row, col}
				next.getValue(in, p, round)
			}
		}
		in.m = next
		if in.IEA[0] == '#' && in.IEA[len(in.IEA)-1] == '.' {
			in.flippy = !in.flippy
		}

	}

	return len(in.m)
}

func (next pixels) getValue(in image, p point, round int) {
	binary := strings.Builder{}
	for _, d := range directions {
		p := point{p.row + d[0], p.col + d[1]}
		if in.flippy &&
			(p.row < -round+1 || p.row >= in.size+round-1 ||
				p.col < -round+1 || p.col >= in.size+round-1) {
			binary.WriteByte('1')
			continue
		}
		if in.m[p] {
			binary.WriteByte('1')
		} else {
			binary.WriteByte('0')
		}
	}

	bin, err := strconv.ParseUint(binary.String(), 2, 9)
	if err != nil {
		log.Panicln(err)
	}

	if in.IEA[bin] == '#' {
		next[p] = true
	}
}

func parseInput(fileName string) (image, error) {
	ret := image{}
	ret.m = map[point]bool{}
	fd, err := os.Open(fileName)
	if err != nil {
		return ret, err
	}
	defer fd.Close()

	buf := bufio.NewScanner(fd)
	for line := 0; buf.Scan(); line++ {
		if line == 0 {
			ret.IEA = buf.Text()
			continue
		}
		for i, ch := range buf.Text() {
			if ch == '#' {
				ret.m[point{line - 2, i}] = true
			}
		}
		ret.size = line - 1
	}

	return ret, nil
}