package main

import (
	"fmt"
	"regexp"
	"strings"

	h "git.bullercodeworks.com/brian/adventofcode/helpers"
)

var rgx = regexp.MustCompile(`\([0-9\*\+ ]*\)`)

const (
	LtoR = iota
	AthenM
)

func main() {
	fmt.Println("# Day 18")
	fmt.Println()
	inp := h.StdinToStringSlice()
	fmt.Println("## Part1\nAnswer:", sum(inp, LtoR))
	fmt.Println("## Part2\nAnswer:", sum(inp, AthenM))
}

func sum(inp []string, algo int) int {
	var ret int
	for k := range inp {
		ret = ret + solve(inp[k], algo)
	}
	return ret
}

func solve(inp string, algo int) int {
	var total int
	rs := rgx.FindString(inp)
	if rs == "" {
		// no parentheticals
		total = evaluateLine(inp, algo)
	} else {
		for rs != "" {
			repl := evaluateLine(rs[1:len(rs)-1], algo)
			inp = strings.Replace(inp, rs, h.Itoa(repl), -1)
			rs = rgx.FindString(inp)
		}
		total = evaluateLine(inp, algo)
	}
	return total
}

func evaluateLine(inp string, algo int) int {
	switch algo {
	case LtoR:
		return evaluateLineLtoR(inp)
	case AthenM:
		return evaluateLineAthenM(inp)
	}
	return 0
}

func evaluateLineLtoR(inp string) int {
	var cnt, total int
	var op string
	// No parentheticals
	pts := strings.Split(inp, " ")
	for k := range pts {
		switch pts[k] {
		case "*", "+":
			op = pts[k]
		default:
			if cnt == 0 {
				total = h.Atoi(pts[k])
				cnt++
			} else {
				if op == "+" {
					total = total + h.Atoi(pts[k])
				} else {
					total = total * h.Atoi(pts[k])
				}
			}
		}
	}
	return total
}

func evaluateLineAthenM(inp string) int {
	// First solve all addition
	inp = evaluateLineAddition(inp)
	inp = evaluateLineMultiplication(inp)
	return h.Atoi(inp)
}

func evaluateLineAddition(inp string) string {
	var newPts []string
	for strings.Contains(inp, "+") {
		pts := strings.Split(inp, " ")
		for k := 0; k < len(pts); k++ {
			if pts[k] == "+" {
				newPts = pts[:k-1]
				newPts = append(newPts, h.Itoa(h.Atoi(pts[k-1])+h.Atoi(pts[k+1])))
				newPts = append(newPts, pts[k+2:]...)
				inp = strings.Join(newPts, " ")
				break
			}
		}
	}
	return inp
}

func evaluateLineMultiplication(inp string) string {
	var newPts []string
	for strings.Contains(inp, "*") {
		pts := strings.Split(inp, " ")
		for k := 0; k < len(pts); k++ {
			if pts[k] == "*" {
				newPts = pts[:k-1]
				newPts = append(newPts, h.Itoa(h.Atoi(pts[k-1])*h.Atoi(pts[k+1])))
				newPts = append(newPts, pts[k+2:]...)
				inp = strings.Join(newPts, " ")
				break
			}
		}
	}
	return inp
}

type Token struct {
	SubTokens []Token
}