package main

import (
	"bufio"
	"crypto/md5"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {
	input := stdinToString()
	partOne(input)
	partTwo(input)
}

func partOne(input string) {
	complete := false
	idx := 1
	password := ""
	for !complete {
		if idx%1000000 == 0 {
			fmt.Print("O")
		} else if idx%100000 == 0 {
			fmt.Print(".")
		}
		sum := fmt.Sprintf("%x", md5.Sum([]byte(input+itoa(idx))))
		if strings.HasPrefix(sum, "00000") {
			fmt.Println("d")
			fmt.Println(itoa(idx))
			password += string(sum[5])
		}
		idx++
		if len(password) == 8 {
			complete = true
		}
	}
	fmt.Println("")
	fmt.Println(password)
}

func partTwo(input string) {
	fmt.Println("Cracking the Password, hold on to your butts: ")
	rand.Seed(time.Now().UTC().UnixNano())
	complete := false
	idx := 1469580
	password := []byte{0, 0, 0, 0, 0, 0, 0, 0}
	printPassword(password)
	fnd := 0
	for !complete {
		sum := fmt.Sprintf("%x", md5.Sum([]byte(input+itoa(idx))))
		if strings.HasPrefix(sum, "00000") {
			pos, err := strconv.Atoi(string(sum[5]))
			if err == nil && pos < 8 && password[pos] == 0 {
				password[pos] = sum[6]
				fnd++
				if fnd == 8 {
					complete = true
				}
			}
		}
		if idx%100000 == 0 {
			clearPassword()
			printPassword(password)
		}
		idx++
	}
	clearPassword()
	printPassword(password)
	fmt.Println("")
	fmt.Println("!~CRACK'D~!")
}

func clearPassword() {
	fmt.Print("")
}

func printPassword(pw []byte) {
	for i := 0; i < len(pw); i++ {
		if pw[i] == 0 {
			// Print random char
			rndBt := byte(97 + rand.Intn(122-97))
			if rndBt%3 == 0 {
				fmt.Print(itoa(int(rndBt % 10)))
			} else {
				fmt.Print(string(byte(97 + rand.Intn(122-97))))
			}
		} else {
			fmt.Print(string(pw[i]))
		}
	}
}

func stdinToString() string {
	var input string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = input + scanner.Text()
	}
	return input
}

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

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
}
