package main

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

var lineSize = 2000
var offset = 10

type Rule struct {
	Match []byte
	Out   byte
}

type Puzzle struct {
	Plants []byte
	Rules  []Rule
}

func sliceEqual(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for t := 0; t < len(a); t++ {
		if a[t] != b[t] {
			return false
		}
	}
	return true
}

func (p *Puzzle) findMatch(pattern []byte) byte {
	for _, r := range p.Rules {
		if sliceEqual(r.Match, pattern) {
			return r.Out
		}
	}
	return '.'
}

func (p *Puzzle) Solution1() int {
	fmt.Printf("%2d %s\n", 0, string(p.Plants))
	for i := 0; i < 20; i++ {
		out := make([]byte, lineSize)
		copy(out, p.Plants)
		for t := 0; t < lineSize-offset; t++ {
			pattern := p.Plants[t : t+5]
			out[t+2] = p.findMatch(pattern)
		}
		p.Plants = out

		fmt.Printf("%2d %s\n", i+1, string(p.Plants))
	}
	points := 0
	for t, c := range p.Plants {
		if c == '#' {
			points += (t - offset)
		}
	}
	return points
}

func (p *Puzzle) Solution2() int {
	points := 0
	lastPoints := 0
	for i := 0; i < 200; i++ {
		fmt.Println(i, points, points-lastPoints)
		out := make([]byte, lineSize)
		for t := range p.Plants {
			out[t] = '.'
		}
		for t := 0; t < lineSize-offset; t++ {
			pattern := p.Plants[t : t+5]
			out[t+2] = p.findMatch(pattern)
		}
		p.Plants = out
		fmt.Printf("%2d %s\n", i+1, string(p.Plants))

		lastPoints = points
		points = 0
		for t, c := range p.Plants {
			if c == '#' {
				points += (t - offset)

				if t > lineSize-20 {
					fmt.Println("oh fuck")
					return 0
				}
			}
		}
	}
	return points
}

func main() {
	input, err := os.Open("input.txt")
	if err != nil {
		panic(err.Error())
	}
	defer input.Close()
	scanner := bufio.NewScanner(input)

	p := Puzzle{
		Plants: make([]byte, lineSize),
		Rules:  make([]Rule, 0, 10),
	}
	scanner.Scan()
	initial := []byte(strings.Split(scanner.Text(), ": ")[1])
	for t := range p.Plants {
		p.Plants[t] = '.'
	}
	for t := range initial {
		p.Plants[t+offset] = initial[t]
	}
	scanner.Scan()
	for scanner.Scan() {
		ruleParts := strings.Split(scanner.Text(), " => ")
		p.Rules = append(p.Rules, Rule{
			Match: []byte(ruleParts[0]),
			Out:   []byte(ruleParts[1])[0],
		})
	}
	if err := scanner.Err(); err != nil {
		panic(err.Error())
	}

	/*
		palette := []color.Color{
			color.RGBA{0, 0, 0, 0xff},
			color.RGBA{0xff, 0xff, 0xff, 0xff},
		}
		size := image.Rect(0, 0, 128, 128)
		images := make([]*image.Paletted, 10)
		delays := make([]int, 10)
		for t := 0; t < 10; t++ {
			images[t] = image.NewPaletted(size, palette)

			for y := 0; y < 128; y++ {
				for x := 0; x < 128; x++ {
					c := byte((x + t) ^ (y + t))
					images[t].Set(x, y, color.RGBA{
						c, c, c, 0xff,
					})
				}
			}
		}

		f, err := os.OpenFile("out.gif", os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			panic(err)
		}
		defer f.Close()
		gif.EncodeAll(f, &gif.GIF{
			Image: images,
			Delay: delays,
		})
	*/

	// fmt.Println(p.Solution1())
	fmt.Println(p.Solution2())
}