package main

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

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

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

	log.Println(solveFirst(lines))

	log.Println(solveSecond(lines))

	return nil
}

func solveSecond(lines []string) int {
	var okLines []string
	for _, line := range lines {
		if _, ok := isCorrupted(line); !ok {
			okLines = append(okLines, line)
		}
	}

	scores := make([]int, len(okLines))
	for i, line := range okLines {
		scores[i] = compScore(mendLine(line))
	}
	sort.Ints(scores)
	return scores[len(scores)/2]
}

func solveFirst(lines []string) int {
	corrValue := map[rune]int{
		')': 3,
		']': 57,
		'}': 1197,
		'>': 25137,
	}
	var ret int
	for _, line := range lines {
		if end, ok := isCorrupted(line); ok {
			ret += corrValue[end]
		}
	}
	return ret
}

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

	buf := bufio.NewScanner(fd)
	for buf.Scan() {
		ret = append(ret, buf.Text())
	}

	return ret, nil
}

func isCorrupted(input string) (rune, bool) {
	var brackets string
	for _, b := range input {
		switch b {
		case '(', '[', '{', '<':
			brackets += string(b)
		case ')':
			if len(brackets) == 0 || brackets[len(brackets)-1] != '(' {
				return b, true
			}
			brackets = brackets[:len(brackets)-1]
		case ']':
			if len(brackets) == 0 || brackets[len(brackets)-1] != '[' {
				return b, true
			}
			brackets = brackets[:len(brackets)-1]
		case '}':
			if len(brackets) == 0 || brackets[len(brackets)-1] != '{' {
				return b, true
			}
			brackets = brackets[:len(brackets)-1]
		case '>':
			if len(brackets) == 0 || brackets[len(brackets)-1] != '<' {
				return b, true
			}
			brackets = brackets[:len(brackets)-1]
		}
	}
	return ' ', false
}

func mendLine(input string) string {
	var brackets string
	for _, b := range input {
		switch b {
		case '(', '[', '{', '<':
			brackets += string(b)
		case ')', ']', '}', '>':
			brackets = brackets[:len(brackets)-1]
		}
	}

	ret := strings.Builder{}
	for i := len(brackets) - 1; i >= 0; i-- {
		var pair byte
		switch brackets[i] {
		case '(':
			pair = ')'
		case '{':
			pair = '}'
		case '[':
			pair = ']'
		case '<':
			pair = '>'
		default:
			log.Panicln("invalid bracket")
		}
		ret.WriteByte(pair)
	}

	return ret.String()
}

func compScore(in string) int {
	var ret int
	for _, ch := range in {
		ret *= 5
		switch ch {
		case ')':
			ret += 1
		case ']':
			ret += 2
		case '}':
			ret += 3
		case '>':
			ret += 4
		}
	}
	return ret
}