package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

type container struct {
	value int
	name  string
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: day17 <liters>")
		os.Exit(1)
	}
	total := mustAtoi(os.Args[1])

	var input []string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = append(input, scanner.Text())
	}

	var containers []container
	for i := range input {
		val := mustAtoi(input[i])
		// Generate a name for this container
		var u int
		for j := range containers {
			if containers[j].value == val {
				u++
			}
		}
		n := strconv.Itoa(val) + "-" + strconv.Itoa(u)
		containers = append(containers, container{value: val, name: n})
	}
	fmt.Println("All Containers: ")
	fmt.Println(containers)
	fmt.Println()

	fmt.Println("Finding Combinations...")
	res := findCombis(total, containers)
	var final [][]container
	fmt.Println()
	fmt.Println("Sorting Results...")
	for i := 0; i < len(res); i++ {
		tst := sortContainers(res[i])
		var dup bool
		for j := 0; j < len(final); j++ {
			if isEqual(tst, final[j]) {
				dup = true
			}
		}
		if !dup {
			final = append(final, tst)
		}
	}
	minNum := len(final[0])
	var minConfig [][]container
	for i := 0; i < len(final); i++ {
		if len(final[i]) < minNum {
			minNum = len(final[i])
		}
	}
	for i := 0; i < len(final); i++ {
		if len(final[i]) == minNum {
			minConfig = append(minConfig, final[i])
		}
	}

	fmt.Print("Total Combinations: ")
	fmt.Println(strconv.Itoa(len(final)))

	fmt.Println("Minimum Containers: " + strconv.Itoa(minNum))
	fmt.Println("Num Configurations: " + strconv.Itoa(len(minConfig)))
}

func findCombis(total int, containers []container) [][]container {
	var ret [][]container

	for i := range containers {
		if total == containers[i].value {
			fmt.Print("O")
			ret = append(ret, []container{containers[i]})
		}
		if containers[i].value < total {
			fmt.Print(".")
			bld := []container{containers[i]}

			var pass []container
			for j := range containers {
				if i != j {
					pass = append(pass, containers[j])
				}
			}

			tmp := findCombis((total - containers[i].value), pass)

			for idx := 0; idx < len(tmp); idx++ {
				tmp[idx] = append(bld, tmp[idx]...)
			}
			ret = append(ret, tmp...)
		}
	}
	return ret
}

func sortContainers(c []container) []container {
	n := len(c)
	swapped := true
	for swapped {
		swapped = false
		for i := 1; i <= n-1; i++ {
			if strings.Compare(c[i-1].name, c[i].name) > 0 {
				tmp := c[i-1]
				c[i-1] = c[i]
				c[i] = tmp
				swapped = true
			}
		}
		n = n - 1
	}
	return c
}

func isEqual(a, b []container) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i].name != b[i].name {
			return false
		}
	}
	return true
}

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
}
