package main

import (
	"bufio"
	"log"
	"os"
	"regexp"
	"sort"
	"strings"
)

func main() {
	if err := myMain(); err != nil {
		log.Println(err)
	}
}

func myMain() error {
	// parse input.txt
	notes, err := parseInput("input.txt")
	if err != nil {
		return err
	}

	log.Println(solveFirst(notes))

	log.Println(solveSecond(notes))

	return nil
}

var numbers = map[string]int{
	"abcefg":  0,
	"cf":      1,
	"acdeg":   2,
	"acdfg":   3,
	"bcdf":    4,
	"abdfg":   5,
	"abdefg":  6,
	"acf":     7,
	"abcdefg": 8,
	"abcdfg":  9,
}

const chars = "abcdefg"

type decodeKey map[rune]rune

func solveSecond(notes Notes) int {
	var sum int
	for _, n := range notes {
		var key = decodeKey{}
		key.findKey(n, chars)
		sum += key.getValue(n)
	}

	return sum
}

func (key decodeKey) getValue(note Note) int {
	var ret [4]int
	for i, out := range note.output {
		decoded := strings.Map(func(in rune) rune {
			return key[in]
		}, out)
		decoded = sortString(decoded)
		ret[i] = numbers[decoded]
	}
	return ret[0]*1000 + ret[1]*100 + ret[2]*10 + ret[3]
}

func (key decodeKey) findKey(note Note, in string) bool {
	if len(in) == 0 {
		if key.checkKey(note) {
			return true
		}
		return false
	}

	for i := range in {
		key[rune(chars[len(key)])] = rune(in[i])
		if key.findKey(note, in[:i]+in[i+1:]) {
			return true
		} else {
			delete(key, rune(chars[len(key)-1]))
		}
	}

	return false
}

func (key decodeKey) checkKey(note Note) bool {
	for _, digit := range note.signalPatterns {
		decoded := strings.Map(func(in rune) rune {
			return key[in]
		}, digit)
		decoded = sortString(decoded)
		if _, ok := numbers[decoded]; !ok {
			return false
		}
	}

	return true
}

type sortRunes []rune

func (s sortRunes) Less(i, j int) bool {
	return s[i] < s[j]
}

func (s sortRunes) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortRunes) Len() int {
	return len(s)
}

func sortString(s string) string {
	r := []rune(s)
	sort.Sort(sortRunes(r))
	return string(r)
}

func solveFirst(notes Notes) int {
	var sum int
	for _, note := range notes {
		for _, item := range note.output {
			switch len(item) {
			case 2: //one
				sum++
			case 4: // four
				sum++
			case 7: // eight
				sum++
			case 3: // seven
				sum++
			}
		}
	}
	return sum
}

type Note struct {
	signalPatterns [10]string
	output         [4]string
}

type Notes []Note

func parseInput(fileName string) (Notes, error) {
	var ret = Notes{}
	fd, err := os.Open(fileName)
	if err != nil {
		return ret, err
	}
	defer fd.Close()

	var line = regexp.MustCompile(`\w+`)

	buf := bufio.NewScanner(fd)
	for buf.Scan() {
		note := Note{}
		tmp := line.FindAllString(buf.Text(), -1)

		for i := 0; i < 10; i++ {
			note.signalPatterns[i] = tmp[i]
		}

		for i := 0; i < 4; i++ {
			note.output[i] = tmp[10+i]
		}

		ret = append(ret, note)
	}

	return ret, nil
}