package main

import (
	"bufio"
	"fmt"
	"os"
	"time"

	termbox "github.com/nsf/termbox-go"
)

const (
	N = iota
	E
	S
	W
	ERR
)

var network [][]byte
var message string
var ticks int
var nodes []byte
var width, height int
var printMap bool

func main() {
	// Grab the window size
	if len(os.Args) > 1 {
		if os.Args[1] == "-print" {
			fmt.Println("Not working right now... Sorry.")
			os.Exit(1)
			printMap = true
		}
	}
	if err := termbox.Init(); err != nil {
		panic(err)
	}
	go func() {
		for {
			event := termbox.PollEvent()
			if event.Key == termbox.KeyCtrlC {
				termbox.Close()
				os.Exit(0)
			}
		}
	}()
	width, height = termbox.Size()

	network = StdinTo2DBytes()
	if printMap {
		ClearScreen()
		PrintMap(0, 0)
		PrintStatus()
	}

	posX, posY, dir := FindEntry(), 0, S

	for dir != ERR {
		posX, posY, dir = Move(posX, posY, dir)

		ticks++
		switch network[posY][posX] {
		case '|', '-', '+':
		case ' ':
			dir = ERR
			break
		default:
			if len(nodes) == 0 || nodes[len(nodes)-1] != network[posY][posX] {
				message = fmt.Sprint("Found node ", string(network[posY][posX]), "!")
				nodes = append(nodes, network[posY][posX])
			}
		}
		ClearScreen()
		if printMap {
			PrintMap(posX, posY)
			time.Sleep(time.Second / 30)
		}
		PrintStatus()
	}
	fmt.Println("Ctrl+C to Quit")
}

func GetNodesAsString(nodes []byte) string {
	ret := "[ "
	for i := range nodes {
		ret += string(nodes[i]) + " "
	}
	return ret + "]"
}

func Move(posX, posY, dir int) (int, int, int) {
	chkX, chkY := posX, posY
	switch dir {
	case N:
		chkY--
	case E:
		chkX++
	case S:
		chkY++
	case W:
		chkX--
	}

	if chkX < 0 || chkY < 0 || chkX > len(network[0]) || chkY > len(network) {
		// Went off the map
		return posX, posY, ERR
	}

	if network[chkY][chkX] == '+' {
		// Which direction do we need to turn?
		switch dir {
		case N, S:
			if CanMoveTo(chkX+1, chkY) {
				dir = E
			} else if CanMoveTo(chkX-1, chkY) {
				dir = W
			}
		case E, W:
			fmt.Println(len(network), chkY+1)
			if CanMoveTo(chkX, chkY+1) {
				dir = S
			} else if CanMoveTo(chkX, chkY-1) {
				dir = N
			}
		}
	}

	return chkX, chkY, dir
}

func CanMoveTo(chkX, chkY int) bool {
	if chkY < 0 && chkX < 0 && chkY > len(network) && chkX > len(network[chkY]) {
		return false
	}
	fmt.Println(chkX, chkY)
	return network[chkY][chkX] != ' '
}

func FindEntry() int {
	for i := range network[0] {
		if network[0][i] == '|' {
			return i
		}
	}
	return -1
}

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

func PrintMap(posX, posY int) {
	stX, stY := 0, 0
	useHeight, useWidth := height-2, width
	if posY > useHeight/2 {
		stY = posY - (useHeight / 2)
	}
	if posX > width/2 {
		stX = posX - (width / 2)
	}
	totHeight := useHeight/2 + posY
	if totHeight < useHeight {
		useHeight = useHeight + (useHeight - totHeight) + 4
	}
	totWidth := useWidth/2 + posX
	if totWidth < useWidth {
		useWidth = useWidth + (useWidth - totWidth) + 4
	}
	for y := stY; y < posY+(useHeight/2); y++ {
		for x := stX; x < posX+(useWidth/2); x++ {
			if len(network) > y {
				if len(network[y]) > x {
					if y == posY && x == posX {
						fmt.Print("*")
					} else {
						fmt.Print(string(network[y][x]))
					}
				}
			}
		}
		fmt.Println("")
	}
}

func PrintStatus() {
	fmt.Print(ticks, " Ticks ", GetNodesAsString(nodes), "\n")
	fmt.Println(message)
}

func StdinTo2DBytes() [][]byte {
	var ret [][]byte
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		bts := scanner.Bytes()
		t := make([]byte, len(bts))
		copy(t, bts)
		ret = append(ret, t)
	}
	return ret
}
