package main

import (
	"crypto/md5"
	"fmt"
	"os"

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

	"../../"
)

var gridWidth, gridHeight int
var path string

func main() {
	// TODO: Random seed
	passcode := "pvhmgsws"
	gridWidth, gridHeight = 4, 4
	if len(os.Args) >= 3 {
		gridWidth = aoc.Atoi(os.Args[1])
		gridHeight = aoc.Atoi(os.Args[2])
	}
	run := true
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	var e termbox.Event
	for run {
		PrintState("", passcode)
		e = termbox.PollEvent()
		if e.Type == termbox.EventKey {
			if e.Key == termbox.KeyCtrlC {
				run = false
			}
		}
	}
}

// We cache the last hash calculated
// It should speed things up a little
var lastHashInput string
var lastHash string

func doorIsOpen(path, passcode string, dir rune) bool {
	if lastHashInput != passcode+path {
		lastHashInput = passcode + path
		lastHash = fmt.Sprintf("%x", md5.Sum([]byte(lastHashInput)))
	}
	switch dir {
	case 'N':
		return lastHash[0] >= 'b' && lastHash[0] <= 'f'
	case 'E':
		return lastHash[3] >= 'b' && lastHash[3] <= 'f'
	case 'S':
		return lastHash[1] >= 'b' && lastHash[1] <= 'f'
	case 'W':
		return lastHash[2] >= 'b' && lastHash[2] <= 'f'
	}
	return false
}

func currPos(path string) (int, int) {
	var cX, cY int
	for i := range path {
		switch path[i] {
		case 'U':
			cY--
		case 'D':
			cY++
		case 'L':
			cX--
		case 'R':
			cX++
		}
	}
	return cX, cY
}

func PrintState(path, passcode string) {
	posX, posY := currPos(path)
	for y := 0; y < gridHeight; y++ {
		for x := 0; x < gridWidth; x++ {
			fmt.Print(aoc.BorderNW)
			if posX == x && posY == y && doorIsOpen(path, passcode, 'N') {
				// Current Pos w/ open door
				fmt.Print(aoc.BorderSE)
				fmt.Print(aoc.BorderSW)
			} else if y == posY+1 && x == posX && doorIsOpen(path, passcode, 'S') {
				// Room below, w/ open door
				fmt.Print(aoc.BorderNE)
				fmt.Print(aoc.BorderNW)
			} else {
				// All other cases
				fmt.Print(aoc.BorderWE)
				fmt.Print(aoc.BorderWE)
			}
			fmt.Print(aoc.BorderNE)
		}
		fmt.Println()
		for x := 0; x < gridWidth; x++ {
			if posX == x && posY == y && doorIsOpen(path, passcode, 'W') {
				fmt.Print(aoc.BorderSE)
			} else {
				fmt.Print(aoc.BorderNS)
			}
			if posX == x && posY == y {
				fmt.Print(aoc.BorderNW)
				fmt.Print(aoc.BorderNE)
			} else {
				fmt.Print(" ")
				fmt.Print(" ")
			}
			if posX == x && posY == y && doorIsOpen(path, passcode, 'E') {
				fmt.Print(aoc.BorderSW)
			} else {
				fmt.Print(aoc.BorderNS)
			}
		}
		fmt.Println()
		for x := 0; x < gridWidth; x++ {
			if posX == x && posY == y && doorIsOpen(path, passcode, 'W') {
				fmt.Print(aoc.BorderNE)
			} else {
				fmt.Print(aoc.BorderNS)
			}
			if posX == x && posY == y {
				fmt.Print(aoc.BorderSW)
				fmt.Print(aoc.BorderSE)
			} else {
				fmt.Print(" ")
				fmt.Print(" ")
			}
			if posX == x && posY == y && doorIsOpen(path, passcode, 'E') {
				fmt.Print(aoc.BorderNW)
			} else {
				fmt.Print(aoc.BorderNS)
			}
		}
		fmt.Println()
		for x := 0; x < gridWidth; x++ {
			fmt.Print(aoc.BorderSW)
			if posX == x && posY == y && doorIsOpen(path, passcode, 'S') {
				fmt.Print(aoc.BorderNE)
				fmt.Print(aoc.BorderNW)
			} else {
				fmt.Print(aoc.BorderWE)
				fmt.Print(aoc.BorderWE)
			}
			fmt.Print(aoc.BorderSE)
		}
		fmt.Println()
	}
}

func ClearScreen() {
	fmt.Print(aoc.ClearScreen)
}

func printUsageAndExit() {
	fmt.Println("Usage: ./day17 <passcode> <gridWidth> <gridHeight>")
	os.Exit(0)
}
