package main

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

const (
	N = iota
	E
	S
	W
)

const (
	ST_CLEAN = iota
	ST_WEAK
	ST_INFECTED
	ST_FLAGGED
	ST_ERROR
)

var nodes map[string]int
var cX, cY, cD int
var minX, minY, maxX, maxY int
var tickCount, infectCount int

func main() {
	inp := StdinToStrings()
	nodes = make(map[string]int)
	cX, cY, cD = len(inp)/2, len(inp[0])/2, N
	for y := 0; y < len(inp); y++ {
		for x := 0; x < len(inp[y]); x++ {
			nodes[cs(x, y)] = ST_CLEAN
			if inp[y][x] == '#' {
				nodes[cs(x, y)] = ST_INFECTED
			}
			updateMinMax(x, y)
		}
	}

	for i := 0; i < 10000000; i++ {
		//ClearScreen()
		if part1 {
			p1Tick()
		} else {
			p2Tick()
		}
		//PrettyPrint()
		//PrintStatus()
		//time.Sleep(time.Second / 5)
	}
}

func p1Tick() {
	tickCount++
	switch nodes[cs(cX, cY)] {
	case ST_INFECTED:
		TurnRight()
		nodes[cs(cX, cY)] = ST_CLEAN
		infectCount++
	default:
		TurnLeft()
		nodes[cs(cX, cY)] = ST_INFECTED
		infectCount++
	}
	cX, cY = getXYInDir()
	updateMinMax(cX, cY)
}

func p2Tick() {
	tickCount++
	switch nodes[cs(cX, cY)] {
	case ST_CLEAN:
		TurnLeft()
		nodes[cs(cX, cY)] = ST_WEAK
	case ST_WEAK:
		nodes[cs(cX, cY)] = ST_INFECTED
		infectCount++
	case ST_INFECTED:
		TurnRight()
		nodes[cs(cX, cY)] = ST_FLAGGED
	case ST_FLAGGED:
		TurnRight()
		TurnRight()
		nodes[cs(cX, cY)] = ST_CLEAN
	}
	cX, cY = getXYInDir()
	updateMinMax(cX, cY)
}

func updateMinMax(x, y int) {
	if x < minX {
		minX = x
	}
	if x > maxX {
		maxX = x
	}
	if y < minY {
		minY = y
	}
	if y > maxY {
		maxY = y
	}
}

func PrettyPrint() {
	for kY := minY; kY <= maxY; kY++ {
		for kX := minX; kX <= maxX; kX++ {
			if kX == cX && kY == cY {
				fmt.Print("[")
			} else {
				fmt.Print(" ")
			}
			switch nodes[cs(kX, kY)] {
			case ST_CLEAN:
				fmt.Print(".")
			case ST_WEAK:
				fmt.Print("W")
			case ST_INFECTED:
				fmt.Print("#")
			case ST_FLAGGED:
				fmt.Print("F")
			}
			if kX == cX && kY == cY {
				fmt.Print("]")
			} else {
				fmt.Print(" ")
			}
		}
		fmt.Println()
	}
}

func PrintStatus() {
	fmt.Println("Ticks:", tickCount)
	fmt.Println("Infections:", infectCount)
}

func TurnRight() {
	switch cD {
	case N:
		cD = E
	case E:
		cD = S
	case S:
		cD = W
	case W:
		cD = N
	}
}

func TurnLeft() {
	switch cD {
	case N:
		cD = W
	case E:
		cD = N
	case S:
		cD = E
	case W:
		cD = S
	}
}

func getXYInDir() (int, int) {
	switch cD {
	case N:
		return cX, cY - 1
	case E:
		return cX + 1, cY
	case S:
		return cX, cY + 1
	case W:
		return cX - 1, cY
	}
	return cX, cY
}

func cs(x, y int) string {
	return fmt.Sprintf("%d;%d", x, y)
}

func sc(key string) (int, int) {
	pts := strings.Split(key, ";")
	return Atoi(pts[0]), Atoi(pts[1])
}

func Atoi(i string) int {
	var ret int
	var err error
	if ret, err = strconv.Atoi(i); err != nil {
		log.Fatal("Invalid Atoi")
	}
	return ret
}

func ClearScreen() {
	fmt.Println("\033[H\033[2J")
}

func StdinToStrings() []string {
	var input []string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = append(input, scanner.Text())
	}
	return input
}
