package main

import (
	"math"
	"sort"
)

type Unit struct {
	Kind      int
	Hitpoints int
	Power     int
	Tile      *Tile
}

const (
	defaultHitpoints = 200
	defaultPower     = 3
)

type SortableUnits []*Unit

func (s SortableUnits) Len() int {
	return len(s)
}
func (s SortableUnits) Less(i, j int) bool {
	if s[i].Tile.Y == s[j].Tile.Y {
		return s[i].Tile.X < s[j].Tile.X
	}
	return s[i].Tile.Y < s[j].Tile.Y
}

func (s SortableUnits) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func NewUnit(tile *Tile, kind, elfPower int) *Unit {
	unit := &Unit{
		Kind:      kind,
		Hitpoints: defaultHitpoints,
		Power:     defaultPower,
		Tile:      tile,
	}
	tile.Unit = unit
	if unit.Kind == KindElf {
		unit.Power = elfPower
	}
	return unit
}

func (u Unit) Targets(c *Cave) bool {
	for _, unit := range c.Units {
		if unit.Kind != u.Kind && unit.Hitpoints > 0 {
			return true
		}
	}
	return false
}

// NextTile returns the next tile to move to and the target tile (or nil if no target found)
func (u *Unit) NextTile(c *Cave) (*Tile, *Tile) {
	var targets SortableTiles
	closestTargetDistance := math.MaxInt32
	distances, path := c.Map.FindWalkableTiles(u.Tile)
	enemies := u.Enemies(c)
	for _, enemy := range enemies {
		for _, target := range enemy.Tile.WalkableNeighbors() {
			if distance, ok := distances[target]; ok && distance <= closestTargetDistance {
				if distance < closestTargetDistance {
					closestTargetDistance = distance
					targets = SortableTiles{}
				}
				targets = append(targets, target)
			}
		}
	}
	sort.Sort(targets)
	if len(targets) > 0 {
		target := targets[0]
		current := target
		for {
			if path[current] == u.Tile {
				return current, target
			}
			current = path[current]
		}
	}
	return nil, nil
}

// Enemies returns enemy units sorted by map position in reading order
func (u *Unit) Enemies(c *Cave) SortableUnits {
	var enemies SortableUnits
	for _, unit := range c.Units {
		if unit.Kind != u.Kind && unit.Hitpoints > 0 {
			enemies = append(enemies, unit)
		}
	}
	sort.Sort(enemies)
	return enemies
}

func (u *Unit) EnemyNeighbor(c *Cave) *Unit {
	var target *Unit
	for _, offset := range offsets {
		if t := c.Map.Tile(u.Tile.X+offset.X, u.Tile.Y+offset.Y); t != nil && t.Unit != nil && t.Unit.Kind != u.Kind && t.Unit.Hitpoints > 0 {
			if target == nil || t.Unit.Hitpoints < target.Hitpoints {
				target = t.Unit
			}
		}
	}
	return target
}

func (u *Unit) Move(c *Cave) {
	if u.EnemyNeighbor(c) != nil {
		return
	}
	next, _ := u.NextTile(c)
	if next != nil {
		next.Unit = u
		next.Kind = u.Kind
		u.Tile.Kind = KindSpace
		u.Tile.Unit = nil
		u.Tile = next
	}
}

func (u *Unit) Attack(c *Cave) bool {
	enemy := u.EnemyNeighbor(c)
	if enemy != nil {
		killed := enemy.Damage(c, u.Power)
		return killed && enemy.Kind == KindElf
	}
	return false
}

func (u *Unit) Damage(c *Cave, damage int) bool {
	u.Hitpoints = u.Hitpoints - damage
	if u.Hitpoints <= 0 {
		c.RemoveUnit(u)
		return true
	}
	return false
}
