package main

import (
	"fmt"

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

const (
	MAZE_UNKNOWN = -1
	MAZE_WALL    = 0
	MAZE_EMPTY   = 1
	MAZE_O2SYS   = 2
)

const (
	DIR_N = 1
	DIR_S = 2
	DIR_W = 3
	DIR_E = 4
)

type Maze struct {
	maze       map[string]int
	maxX, minX int
	maxY, minY int
	bot        *helpers.Coordinate
	path       []*helpers.Coordinate
	dirHistory []int
	dnt        bool

	startCoord *helpers.Coordinate
	o2Coord    *helpers.Coordinate
}

func NewMaze() *Maze {
	m := &Maze{
		maze:       make(map[string]int),
		maxX:       helpers.MIN_INT,
		minX:       helpers.MAX_INT,
		maxY:       helpers.MIN_INT,
		minY:       helpers.MAX_INT,
		bot:        helpers.NewCoordinate(0, 0),
		startCoord: helpers.NewCoordinate(0, 0),
	}
	m.path = append(m.path, helpers.NewCoordinate(0, 0))
	return m
}

func (m *Maze) SetCoord(c *helpers.Coordinate, val int) {
	if val == 2 {
		m.o2Coord = c
	}
	m.maze[c.String()] = val
	if m.maxX < c.X {
		m.maxX = c.X
	}
	if m.minX > c.X {
		m.minX = c.X
	}
	if m.maxY < c.Y {
		m.maxY = c.Y
	}
	if m.minY > c.Y {
		m.minY = c.Y
	}
}

func (m *Maze) GetCoord(c *helpers.Coordinate) int {
	v, ok := m.maze[c.String()]
	if !ok {
		return MAZE_UNKNOWN
	}
	return v
}

func (m *Maze) GetDirFromBot(dir int) *helpers.Coordinate {
	switch dir {
	case DIR_N:
		return m.bot.GetNorthCoord()
	case DIR_E:
		return m.bot.GetEastCoord()
	case DIR_S:
		return m.bot.GetSouthCoord()
	case DIR_W:
		return m.bot.GetWestCoord()
	}
	return nil
}

func (m *Maze) MoveBot(dir int) bool {
	dest := m.GetDirFromBot(dir)
	if m.GetCoord(dest) == MAZE_UNKNOWN || m.GetCoord(dest) == MAZE_WALL {
		return false
	}
	m.bot.X, m.bot.Y = dest.X, dest.Y
	if !m.dnt {
		m.path = append(m.path, helpers.NewCoordinate(m.bot.X, m.bot.Y))
		m.dirHistory = append(m.dirHistory, dir)
	}
	m.dnt = false
	return true
}

func (m *Maze) GetDirectionToLast() int {
	if len(m.dirHistory) == 0 {
		return -1
	}
	m.dnt = true
	var dir int
	dir, m.dirHistory = m.dirHistory[len(m.dirHistory)-1], m.dirHistory[:len(m.dirHistory)-1]
	switch dir {
	case DIR_N:
		return DIR_S
	case DIR_S:
		return DIR_N
	case DIR_W:
		return DIR_E
	case DIR_E:
		return DIR_W
	}
	return -1
}

func (m *Maze) Print() {
	for y := m.minY; y <= m.maxY; y++ {
		for x := m.minX; x <= m.maxX; x++ {
			c := helpers.NewCoordinate(x, y)
			if m.bot.Equals(*c) {
				fmt.Print("%")
			} else {
				switch m.GetCoord(c) {
				case MAZE_UNKNOWN:
					fmt.Print("~")
				case MAZE_WALL:
					fmt.Print("#")
				case MAZE_EMPTY:
					fmt.Print(" ")
				case MAZE_O2SYS:
					fmt.Print("O")
				}
			}
		}
		fmt.Println()
	}
}
