package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
)

// Food ...
type Food struct {
	Ingredients []string
	Allergens   []string
}

func inSlice(s []string, a string) bool {
	for i := range s {
		if s[i] == a {
			return true
		}
	}
	return false
}

func delFromSlice(s []string, a string) []string {
	result := make([]string, 0, 10)
	for i := range s {
		if s[i] != a {
			result = append(result, s[i])
		}
	}
	return result
}

func intersectSlice(a, b []string) []string {
	result := make([]string, 0, 10)
	for i := range a {
		if inSlice(b, a[i]) && !inSlice(result, a[i]) {
			result = append(result, a[i])
		}
	}
	for i := range b {
		if inSlice(a, b[i]) && !inSlice(result, b[i]) {
			result = append(result, b[i])
		}
	}
	return result
}

func compileIngredients(input []Food) map[string]int {
	result := make(map[string]int)
	for _, in := range input {
		for _, i := range in.Ingredients {
			result[i]++
		}
	}
	return result
}

func findAllergens(input []Food) map[string][]string {
	result := make(map[string][]string)
	for {
		stop := true
		for _, in := range input {
			for _, a := range in.Allergens {
				if _, ok := result[a]; !ok {
					result[a] = append(result[a], in.Ingredients...)
				} else {
					result[a] = intersectSlice(result[a], in.Ingredients)
				}
			}
		}

		for a, all := range result {
			if len(all) == 1 {
				for i := range result {
					if i != a {
						result[i] = delFromSlice(result[i], all[0])
					}
				}
			} else {
				stop = false
			}
		}

		if stop {
			break
		}
	}
	return result
}

// Task1 ...
func Task1(input []Food, debug bool) int {
	ingredients := compileIngredients(input)
	allergens := findAllergens(input)

	var result int
	for i, c := range ingredients {
		var found bool
		for _, a := range allergens {
			if i == a[0] {
				found = true
				break
			}
		}
		if !found {
			result += c
		}
	}

	if debug {
		fmt.Printf("%+v\n", ingredients)
		fmt.Printf("%+v\n", allergens)
	}

	return result
}

// Task2 ...
func Task2(input []Food, debug bool) string {
	allergens := findAllergens(input)
	sorted := make([]string, 0, len(allergens))
	for a := range allergens {
		sorted = append(sorted, a)
	}
	sort.Strings(sorted)
	ingredients := make([]string, 0, len(sorted))
	for _, a := range sorted {
		ingredients = append(ingredients, allergens[a][0])
	}
	return strings.Join(ingredients, ",")
}

func readInput(reader io.Reader) []Food {
	input := make([]Food, 0, 10)

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		parts := strings.Split(scanner.Text(), "(contains ")
		ingredients := strings.Split(strings.TrimSpace(parts[0]), " ")
		allergens := strings.Split(parts[1][:len(parts[1])-1], ", ")
		input = append(input, Food{
			Ingredients: ingredients,
			Allergens:   allergens,
		})
	}

	return input
}

func main() {
	var debug bool
	flag.BoolVar(&debug, "debug", false, "debug")
	flag.Parse()

	var input []Food

	file, _ := os.Open("input.txt")
	input = readInput(file)
	file.Close()

	result1 := Task1(input, debug)
	fmt.Printf("Task 1: %d\n", result1)

	result2 := Task2(input, debug)
	fmt.Printf("Task 2: %s\n", result2)
}