package overthink

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"sort"
	"time"
)

const (
	UseEmoji    = false
	MaxInt      = int(^uint(0) >> 1)
	ClearScreen = "\033[H\033[2J"

	DIR_N = -1i
	DIR_E = 1
	DIR_S = 1i
	DIR_W = -1
)

var width int
var input []byte

var elves []*complex64
var goblins []*complex64
var allChars []*complex64
var charMap map[complex64]*Character

func main() {
	stdinToByteSlice()
	setupBattle()
	part1()
}

func part1() {
	//for {
	sortCharacters()
	printBattleField()
	for _, c := range allChars {
		charMap[*c].tick()
	}
	time.Sleep(time.Millisecond * 250)
	printBattleField()
	//}
	fmt.Println("")
}

func sortCharacters() {
	sort.Sort(ByPos(allChars))
	sort.Sort(ByPos(elves))
	sort.Sort(ByPos(goblins))
}

type Character struct {
	tp     byte
	pos    complex64
	power  int
	health int
}

func (c *Character) tick() {
	// If we're already in range of a target, don't look for a new one
	var alreadyAtTarget bool
	var chosenTarget *complex64
	lowestTargetHp := MaxInt
	for _, v := range elves {
		if c.isAdjacentTo(*v) {
			if charMap[*v].health < lowestTargetHp {
				chosenTarget = v
			}
			alreadyAtTarget = true
		}
	}
	if alreadyAtTarget {
		// Attack the target
		charMap[*chosenTarget].health -= c.power
	} else {
		// Looking for a target
		if c.tp == 'G' {
			// First identify all possible targets (elves that have an open adjacent space)
			for _, v := range elves {

			}
		} else {
			// First identify all possible targets (goblins that have an open adjacent space)
			for _, v := range goblins {
			}
		}
	}
}

func (c *Character) isAdjacentTo(p complex64) bool {
	return c.pos+DIR_N == p || c.pos+DIR_E == p ||
		c.pos+DIR_S == p || c.pos+DIR_W == p
}

func (c *Character) hasOpenFlank() bool {

}

// Not sure if we'll use this...
func manhattanDistance(p1, p2 complex64) int {
	x1, y1, x2, y2 := real(p1), imag(p1), real(p2), imag(p2)
	return int(math.Abs(float64(x1)-float64(x2)) + math.Abs(float64(y1)-float64(y2)))
}

// We have to sort the characters on each tick by y,x position
// y is the imaginary part, x is the real part
type ByPos []*complex64

func (c ByPos) Len() int      { return len(c) }
func (c ByPos) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ByPos) Less(i, j int) bool {
	return imag(*c[i]) < imag(*c[j]) ||
		(imag(*c[i]) == imag(*c[j]) && real(*c[i]) < real(*c[j]))
}

// getByte pulls a byte from the given position in the input
func getByte(pos complex64) byte {
	return input[int(real(pos))+int(imag(pos))*width]
}

func setByte(pos complex64, b byte) {
	input[int(real(pos))+int(imag(pos))*width] = b
}

func getPosFromInt(i int) complex64 {
  return complex(float32(i%width), float32(i/width))
}

func printBattleField() {
	fmt.Print(ClearScreen)
	for i := 0; i < len(input); i++ {
		pos := getPosFromInt(i)
		var bt byte
		if c, ok := charMap[pos]; ok {
			if UseEmoji {
				switch c.tp {
				case 'G':
					if UseEmoji {
						fmt.Print("👺")
					}
				case 'E':
					fmt.Print("😃")
				}
			} else {
				fmt.Print(string(c.tp))
			}
			bt = c.tp
		} else {
			bt = getByte(pos)
			if UseEmoji {
				if bt == '#' {
					fmt.Print("🏿")
				} else {
					fmt.Print("  ")
				}
			} else {
				fmt.Print(string(bt))
			}
		}
		if i%width == width-1 {
			fmt.Println("")
		}
	}
}

func setupBattle() {
	charMap = make(map[complex64]*Character)
	for i := 0; i < len(input); i++ {
		pos := complex(float32(i%width), float32(i/width))
		bt := getByte(pos)
		if bt == 'G' || bt == 'E' {
			charMap[pos] = &Character{
				tp:     bt,
				pos:    pos,
				power:  3,
				health: 200,
			}
			setByte(pos, '.')
			allChars = append(allChars, &charMap[pos].pos)
			switch bt {
			case 'G':
				goblins = append(goblins, &charMap[pos].pos)
			case 'E':
				elves = append(elves, &charMap[pos].pos)
			}
		}
	}
}

func stdinToByteSlice() {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		data := scanner.Bytes()
		if width == 0 {
			width = len(data)
		}
		input = append(input, data...)
	}
}
