package main

import (
	"log"
	"math"
	"strings"

	"io/ioutil"

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

type Layout struct {
	Area        map[h.Coordinate]bool
	BottomRight h.Coordinate

	History map[string]float64
}

func NewLayout(filename string) *Layout {
	ret := Layout{
		Area:        make(map[h.Coordinate]bool),
		BottomRight: h.Coordinate{X: 0, Y: 0},
		History:     make(map[string]float64),
	}
	input, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err.Error())
	}
	lines := make([]string, 0)
	for _, line := range strings.Split(string(input), "\n") {
		lines = append(lines, line)
	}
	for y := 0; y < len(lines); y++ {
		if len(lines[y]) < ret.BottomRight.X {
			break
		}
		ret.BottomRight.Y = y
		for x := 0; x < len(lines[y]); x++ {
			ret.BottomRight.X = x
			ret.Area[h.Coordinate{X: x, Y: y}] = lines[y][x] == '#'
		}
	}
	ret.SaveToHistory()
	return &ret
}

func (l Layout) SaveToHistory() {
	l.History[l.String()] = l.Rating()
}

func (l Layout) Rating() float64 {
	var rating, pow float64
	for y := 0; y <= l.BottomRight.Y; y++ {
		for x := 0; x <= l.BottomRight.X; x++ {
			if l.Area[h.Coordinate{X: x, Y: y}] {
				rating = rating + math.Pow(2, pow)
			}
			pow++
		}
	}
	return rating
}

func (l Layout) Step() bool {
	a := make(map[h.Coordinate]bool)
	for k, v := range l.Area {
		a[k] = v
	}
	for y := 0; y <= l.BottomRight.Y; y++ {
		for x := 0; x <= l.BottomRight.X; x++ {
			c := h.Coordinate{X: x, Y: y}
			neighbors := 0
			for _, v := range []h.Coordinate{c.North(), c.East(), c.South(), c.West()} {
				if n, ok := l.Area[v]; ok && n {
					neighbors++
				}
			}
			if l.Area[c] {
				if neighbors != 1 {
					a[c] = false
				}
			} else {
				if neighbors == 1 || neighbors == 2 {
					a[c] = true
				}
			}
		}
	}
	for k, v := range a {
		l.Area[k] = v
	}
	// Finally check/save to history
	_, ok := l.History[l.String()]
	l.History[l.String()] = l.Rating()
	return ok
}

func (l Layout) String() string {
	var ret string
	for y := 0; y <= l.BottomRight.Y; y++ {
		for x := 0; x <= l.BottomRight.X; x++ {
			if l.Area[h.Coordinate{X: x, Y: y}] {
				ret = ret + "#"
			} else {
				ret = ret + "."
			}
		}
		ret = ret + "\n"
	}
	return ret
}
