package main

import (
	"crypto/md5"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

var hashStore []string
var salt string

func main() {
	if len(os.Args) < 3 {
		fmt.Println("Usage: ./day14 <salt> <key-num>")
		fmt.Println("  My input: ./day14 cuanljph 64")
		os.Exit(1)
	}
	salt = os.Args[1]
	keyNum := atoi(os.Args[2])
	var foundKeys []string
	tryIdx := 0
	for len(foundKeys) < keyNum {
		tst := getHash(tryIdx)
		for i := range tst {
			if len(tst) > i+3 {
				if tst[i:i+3] == strings.Repeat(string(tst[i]), 3) {
					// Found a triple, search the next 1000 for a quintuple
					if searchForQuint(salt, tryIdx+1, string(tst[i])) {
						foundKeys = append(foundKeys, tst)
					}
					// Once we test the first triplet, we're done with this one
					break
				}
			}
		}
		tryIdx++
	}
	fmt.Println("Key:", getHash(tryIdx-1), "Idx:", tryIdx)
}

func getHash(idx int) string {
	for len(hashStore)-1 < idx {
		hashStore = append(
			hashStore,
			getStretchedHash(fmt.Sprintf("%x", md5.Sum([]byte(salt+itoa(len(hashStore)+1))))),
		)
	}
	return hashStore[idx]
}

func getStretchedHash(s string) string {
	for i := 0; i < 2016; i++ {
		s = fmt.Sprintf("%x", md5.Sum([]byte(s)))
	}
	return s
}

func searchForQuint(salt string, startIdx int, srch string) bool {
	for i := startIdx; i <= startIdx+1000; i++ {
		tst := getHash(i)
		if strings.Contains(tst, strings.Repeat(srch, 5)) {
			return true
		}
	}
	return false
}

func atoi(i string) int {
	var ret int
	var err error
	if ret, err = strconv.Atoi(i); err != nil {
		log.Fatal("Invalid Atoi")
	}
	return ret
}

func itoa(i int) string {
	return strconv.Itoa(i)
}
