package main

import (
	"fmt"
	"os"
	"strconv"
)

var enemyHp int
var enemyDmg int
var enemyDef int

var weaponsAvailable []weapon
var armorAvailable []armor
var ringsAvailable []ring

var currWeapon, cheapWeapon, highWeapon weapon
var currArmor, cheapArmor, highArmor armor
var currRing1, cheapRing1, highRing1 ring
var currRing2, cheapRing2, highRing2 ring

// My input: ./day21 100 8 2
func main() {
	if len(os.Args) < 4 || os.Args[1] == "-help" {
		fmt.Println("Usage: day21 <enemy hp> <enemy dmg> <enemy def")
		os.Exit(1)
	}

	enemyHp = mustAtoi(os.Args[1])
	enemyDmg = mustAtoi(os.Args[2])
	enemyDef = mustAtoi(os.Args[3])

	cheapest := -1
	highest := -1
	initShop()

	for wi := range weaponsAvailable {
		currWeapon = weaponsAvailable[wi]
		for ai := range armorAvailable {
			currArmor = armorAvailable[ai]
			for r1i := range ringsAvailable {
				currRing1 = ringsAvailable[r1i]
				for r2i := range ringsAvailable {
					if r2i == r1i {
						continue
					}
					currRing2 = ringsAvailable[r2i]
					if toTheDeath(false) {
						if calcCost() < cheapest || cheapest == -1 {
							cheapest = calcCost()
							saveCheapest()
						}
					} else {
						if calcCost() > highest || highest == -1 {
							highest = calcCost()
							saveHighest()
						}
					}
				}
			}
		}
	}

	if cheapest >= 0 {
		fmt.Println("Cheapest Victory: " + itoa(cheapest))
		fmt.Println("Weapon: " + cheapWeapon.name)
		fmt.Println("Armor: " + cheapArmor.name)
		fmt.Println("Ring1: " + cheapRing1.name)
		fmt.Println("Ring2: " + cheapRing2.name)
		loadCheapest()
		toTheDeath(true)
	} else {
		fmt.Println("No victory to be had.")
	}
	fmt.Println()
	if highest >= 0 {
		fmt.Println("Most Expensive Loss: " + itoa(highest))
		fmt.Println("Weapon: " + highWeapon.name)
		fmt.Println("Armor: " + highArmor.name)
		fmt.Println("Ring1: " + highRing1.name)
		fmt.Println("Ring2: " + highRing2.name)
		loadHighest()
		toTheDeath(true)
	} else {
		fmt.Println("No victory to be had.")
	}
}

func loadCheapest() {
	currWeapon = cheapWeapon
	currArmor = cheapArmor
	currRing1 = cheapRing1
	currRing2 = cheapRing2
}

func saveCheapest() {
	cheapWeapon = currWeapon
	cheapArmor = currArmor
	cheapRing1 = currRing1
	cheapRing2 = currRing2
}

func loadHighest() {
	currWeapon = highWeapon
	currArmor = highArmor
	currRing1 = highRing1
	currRing2 = highRing2
}

func saveHighest() {
	highWeapon = currWeapon
	highArmor = currArmor
	highRing1 = currRing1
	highRing2 = currRing2
}

func toTheDeath(output bool) bool {
	var pHp, pDmg, pDef, eHp, eDmg, eDef int
	pHp = 100

	pDmg += currWeapon.damage
	pDmg += currRing1.damage
	pDmg += currRing2.damage
	pDef += currArmor.defense
	pDef += currRing1.defense
	pDef += currRing2.defense

	eHp = enemyHp
	eDmg = enemyDmg
	eDef = enemyDef

	for pHp > 0 && eHp > 0 {
		pAtk := pDmg - eDef
		if pAtk <= 1 {
			pAtk = 1
		}
		eHp -= pAtk
		eAtk := 1
		if eHp > 0 {
			eAtk = eDmg - pDef
			if eAtk <= 1 {
				eAtk = 1
			}
			pHp -= eAtk
		}
	}
	if output {
		fmt.Print(itoa(calcCost()) + ": ")
		fmt.Print(itoa(pHp) + " to " + itoa(eHp) + " ")
		fmt.Println("DMG(" + itoa(pDmg) + ") DEF(" + itoa(pDef) + ") ")
	}
	return pHp > 0
}

func calcCost() int {
	return currWeapon.cost + currArmor.cost + currRing1.cost + currRing2.cost
}

func initShop() {
	addWeapon("Dagger", 8, 4)
	addWeapon("Shortsword", 10, 5)
	addWeapon("Warhammer", 25, 6)
	addWeapon("Longsword", 40, 7)
	addWeapon("Greataxe", 74, 8)

	addArmor("Leather", 13, 1)
	addArmor("Chainmail", 31, 2)
	addArmor("Splintmail", 53, 3)
	addArmor("Bandedmail", 75, 4)
	addArmor("Platemail", 102, 5)
	addArmor("No Armor", 0, 0)

	addRing("Damage +1", 25, 1, 0)
	addRing("Damage +2", 50, 2, 0)
	addRing("Damage +3", 100, 3, 0)
	addRing("Defense +1", 20, 0, 1)
	addRing("Defense +2", 40, 0, 2)
	addRing("Defense +3", 80, 0, 3)
	addRing("No Ring 1", 0, 0, 0)
	addRing("No Ring 2", 0, 0, 0)
}
func addWeapon(n string, c, dmg int) {
	weaponsAvailable = append(weaponsAvailable,
		weapon{name: n, cost: c, damage: dmg})
}
func addArmor(n string, c, def int) {
	armorAvailable = append(armorAvailable,
		armor{name: n, cost: c, defense: def})
}
func addRing(n string, c, dmg, def int) {
	ringsAvailable = append(ringsAvailable,
		ring{name: n, cost: c, damage: dmg, defense: def})
}

type weapon struct {
	name         string
	cost, damage int
}

type armor struct {
	name          string
	cost, defense int
}

type ring struct {
	name                  string
	cost, damage, defense int
}

func itoa(i int) string {
	return strconv.Itoa(i)
}
func mustAtoi(s string) int {
	var i int
	var err error
	if i, err = strconv.Atoi(s); err != nil {
		fmt.Println("Tried to atoi " + s)
		os.Exit(1)
	}
	return i
}
