package main

import (
	"fmt"

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

const (
	White = iota
	Black
)

var tiles map[h.Coordinate]bool

func init() {
	tiles = make(map[h.Coordinate]bool)
}

func main() {
	fmt.Println("# Day 24")
	inp := h.StdinToStringSlice()

	solve(inp, h.Atoi(h.OptArgNumber(1, "2")))
}

func solve(inp []string, part int) {
	buildTiles(inp)
	if part == 1 {
		fmt.Println("## Part 1\nAnswer:", len(tiles))
	} else {
		for day := 1; day <= 100; day++ {
			next := make(map[h.Coordinate]bool)
			lx, ly, hx, hy := getBordering()
			for y := ly; y <= hy; y++ {
				for x := lx; x <= hx; x++ {
					tst := h.Coordinate{X: x, Y: y}
					_, ok := tiles[tst]
					bl := CountTiles(GetAllNeighborCoords(tst))
					if (ok && !(bl == 0 || bl > 2)) || (!ok && bl == 2) {
						next[tst] = true
					}
				}
			}
			tiles = next
			fmt.Printf("Day %d: %d\n", day, len(next))
		}
	}
}

func getBordering() (int, int, int, int) {
	lx, ly, hx, hy := h.MAX_INT, h.MAX_INT, h.MIN_INT, h.MIN_INT
	for k := range tiles {
		lx = h.Min(lx, k.X)
		ly = h.Min(ly, k.Y)
		hx = h.Max(hx, k.X)
		hy = h.Max(hy, k.Y)
	}
	return lx - 1, ly - 1, hx + 1, hy + 1
}

/*
   o o
  o o o
   o o
*/
func buildTiles(inp []string) {
	for k := range inp {
		curr := h.Coordinate{}
		var last rune
		for _, r := range inp[k] {
			if r == 'n' || r == 's' {
				last = r
				continue
			}
			curr = GetNeighborCoord(curr, last+r)
			last = 0
		}
		if v := tiles[curr]; v {
			delete(tiles, curr)
		} else {
			tiles[curr] = true
		}
	}
}

func CountTiles(c []h.Coordinate) int {
	var ret int
	for _, v := range c {
		if _, ok := tiles[v]; ok {
			ret++
		}
	}
	return ret
}

func GetAllNeighborCoords(c h.Coordinate) []h.Coordinate {
	var ret []h.Coordinate
	for _, v := range []rune{
		'e', 'n' + 'e', 's' + 'e', 'w', 'n' + 'w', 's' + 'w',
	} {
		ret = append(ret, GetNeighborCoord(c, v))
	}
	return ret
}

// Translate hex neighbors into a 2d plane
func GetNeighborCoord(c h.Coordinate, dir rune) h.Coordinate {
	switch dir {
	case 'e':
		return c.East()
	case 'n' + 'e':
		return c.North()
	case 's' + 'e':
		return c.South().East()
	case 'w':
		return c.West()
	case 'n' + 'w':
		return c.North().West()
	case 's' + 'w':
		return c.South()
	}
	return c
}
