package main

import (
	"bufio"
	"log"
	"math"
	"os"
	"regexp"
	"strconv"
	"strings"
)

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

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

	log.Println(solveFirst(in))

	log.Println(solveSecond(in))

	return nil
}

func solveSecond(in []string) int {
	var max int
	for _, x := range in {
		for _, y := range in {
			if x == y {
				continue
			}
			xy := reduce("[" + x + "," + y + "]")
			m, _ := magnitude(xy, 0)
			if m > max {
				max = m
			}
		}
	}
	return max
}

func solveFirst(in []string) int {
	var ret string
	for _, st := range in {
		if ret == "" {
			ret = st
		} else {
			ret = reduce("[" + ret + "," + st + "]")
		}
	}

	res, _ := magnitude(ret, 0)

	return res
}

func magnitude(in string, pos int) (int, int) {
	left, right := -1, -1
	var rght bool

	var skip int
	for i := pos; i < len(in); i++ {
		if in[i] == ']' {
			skip = i - pos + 1
			break
		}

		if in[i] == '[' {
			var p int
			if rght {
				right, p = magnitude(in, i+1)
			} else {
				left, p = magnitude(in, i+1)
			}
			i += p
			continue
		}

		switch in[i] {
		case ',':
			rght = true
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			tmp, err := strconv.Atoi(string(in[i]))
			if err != nil {
				log.Panicln(err)
			}
			if rght {
				right = tmp
			} else {
				left = tmp
			}
		}
	}

	// log.Println("RETURN:", left, right)

	if left == -1 || right == -1 {
		return left + right + 1, skip
	}

	return 3*left + 2*right, skip
}

func reduce(in string) string {
	if next := explode(in); next != in {
		in = reduce(next)
	} else if next := split(in); next != in {
		in = reduce(next)
	}

	return in
}

func explode(in string) string {
	var count int
	for i, ch := range in {
		if ch == '[' {
			count++
			if count == 5 {
				return doExplode(in, i)
			}
		}
		if ch == ']' {
			count--
		}
	}
	return in
}

var number = regexp.MustCompile(`\d+`)

func doExplode(in string, pivot int) string {
	// log.Println(in[:pivot], in[pivot:])
	lefts := number.FindAllStringSubmatchIndex(in[:pivot+1], -1)
	rights := number.FindAllStringSubmatchIndex(in[pivot:], 3)
	// log.Println("L:", lefts, "R:", rights)

	// Exploding left item: 1st from rights
	expLeft := []int{pivot + rights[0][0], pivot + rights[0][1]}
	// Exploding right item: 2nd from rights
	expRight := []int{pivot + rights[1][0], pivot + rights[1][1]}

	var l, r []int

	if len(lefts) > 0 {
		l = []int{lefts[len(lefts)-1][0], lefts[len(lefts)-1][1]}
		// log.Println("left value:", in[l[0]:l[1]])
	}

	if len(rights) > 2 { // rights has to be at least 3 long
		r = []int{pivot + rights[2][0], pivot + rights[2][1]}
		// log.Println("right value:", in[r[0]:r[1]])
	}

	ret := strings.Builder{}
	if len(l) != 0 {
		ret.WriteString(in[:l[0]])

		leftNum, err := strconv.Atoi(in[l[0]:l[1]])
		if err != nil {
			log.Panicln(err)
		}
		explodeLeftNum, err := strconv.Atoi(in[expLeft[0]:expLeft[1]])
		if err != nil {
			log.Panicln(err)
		}

		ret.WriteString(strconv.Itoa(leftNum + explodeLeftNum))

		ret.WriteString(in[l[1] : expLeft[0]-1])
	} else {
		ret.WriteString(in[:expLeft[0]-1])
	}

	ret.WriteByte('0')

	if len(r) != 0 {
		ret.WriteString(in[expRight[1]+1 : r[0]])

		rightNum, err := strconv.Atoi(in[r[0]:r[1]])
		if err != nil {
			log.Panicln(err)
		}
		explodeRightNum, err := strconv.Atoi(in[expRight[0]:expRight[1]])
		if err != nil {
			log.Panicln(err)
		}
		ret.WriteString(strconv.Itoa(rightNum + explodeRightNum))

		ret.WriteString(in[r[1]:])
	} else {
		ret.WriteString(in[expRight[1]+1:])
	}

	// log.Println("after doExplode", ret.String())
	return ret.String()
}

var twodigit = regexp.MustCompile(`\d\d+`)

func split(in string) string {
	if split := twodigit.FindStringIndex(in); len(split) != 0 {
		in = doSplit(in, split)
	}
	return in
}

func doSplit(in string, split []int) string {
	ret := strings.Builder{}
	ret.WriteString(in[:split[0]])

	sno, err := strconv.Atoi(in[split[0]:split[1]])
	if err != nil {
		log.Panicln(err)
	}
	ret.WriteByte('[')
	ret.WriteString(strconv.Itoa(sno / 2))
	ret.WriteByte(',')
	ret.WriteString(strconv.Itoa(int(math.Ceil(float64(sno) / 2))))
	ret.WriteByte(']')

	ret.WriteString(in[split[1]:])

	// log.Println("after doSplit", ret.String())
	return ret.String()
}

func parseInput(fileName string) ([]string, error) {
	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
}