package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

type packageStruct struct {
	version     int64
	typeID      int64
	operator    string
	value       int64
	subPackages []packageStruct
}

func main() {
	args := os.Args[1:]
	input := args[0]
	binaryNumberString := parseHexToBit(input)
	pack, _ := parsePackage(binaryNumberString)
	fmt.Println(pack)
	sum := int64(0)
	sumVers(pack, &sum)
	fmt.Println(sum)
	fmt.Println(calculatePart2(pack))
}

func calculatePart2(pack packageStruct) int64 {
	switch pack.typeID {
	case 0:
		sum := int64(0)
		for _, p := range pack.subPackages {
			sum += calculatePart2(p)
		}
		return sum
	case 1:
		sum := int64(1)
		for _, p := range pack.subPackages {
			sum *= calculatePart2(p)
		}
		return sum
	case 2:
		min := calculatePart2(pack.subPackages[0])
		for _, p := range pack.subPackages {
			valueOfSubPackage := calculatePart2(p)
			if min > valueOfSubPackage {
				min = valueOfSubPackage
			}
		}
		return min
	case 3:
		max := calculatePart2(pack.subPackages[0])
		for _, p := range pack.subPackages {
			valueOfSubPackage := calculatePart2(p)
			if max < valueOfSubPackage {
				max = valueOfSubPackage
			}
		}
		return max
	case 5:
		if calculatePart2(pack.subPackages[0]) > calculatePart2(pack.subPackages[1]) {
			return 1
		} else {
			return 0
		}
	case 6:
		if calculatePart2(pack.subPackages[0]) < calculatePart2(pack.subPackages[1]) {
			return 1
		} else {
			return 0
		}
	case 7:
		if calculatePart2(pack.subPackages[0]) == calculatePart2(pack.subPackages[1]) {
			return 1
		} else {
			return 0
		}
	default:
		return pack.value
	}
}

func sumVers(pack packageStruct, sum *int64) {
	*sum += pack.version
	for _, p := range pack.subPackages {
		sumVers(p, sum)
	}
}

func parseHexToBit(hex string) string {
	returnString := ""
	for _, char := range hex {
		number, _ := strconv.ParseInt(string(char), 16, 0)
		returnString += padBinaryNumber(strconv.FormatInt(number, 2))
	}
	return returnString
}

func padBinaryNumber(binaryNumberString string) string {
	if (len(binaryNumberString) % 4) > 0 {
		amountPaddingZeros := 4 - (len(binaryNumberString) % 4)
		binaryNumberString = strings.Repeat("0", amountPaddingZeros) + binaryNumberString
	}
	return binaryNumberString
}

func parsePackage(binaryNumberString string) (pack packageStruct, index int64) {
	index = 0
	pack.version, _ = strconv.ParseInt(binaryNumberString[:3], 2, 0)
	pack.typeID, _ = strconv.ParseInt(binaryNumberString[3:6], 2, 0)
	index += 6
	if pack.typeID == 4 {
		indexAdd := int64(0)
		pack.value, indexAdd = parseLiteralValue(binaryNumberString[6:])
		index += indexAdd
		return
	}
	subPackages, indexAdd := parseOperator(binaryNumberString[6:])
	pack.subPackages = subPackages
	index += indexAdd
	return
}

func parseLiteralValue(packageString string) (value int64, index int64) {
	index = 0
	number := ""
	for {
		number += packageString[index+1 : index+5]
		if packageString[index] == '0' {
			value, _ = strconv.ParseInt(number, 2, 0)
			index += 5
			return
		}
		index += 5
	}
}

func parseOperator(packageString string) (packs []packageStruct, index int64) {
	index = 1
	lengthTypeID := packageString[0]
	if lengthTypeID == '0' {
		lengthSubPackets, _ := strconv.ParseInt(packageString[index+1:index+15], 2, 0)
		index += 15
		subPackageBinary := packageString[index : index+lengthSubPackets]
		subPackageIndex := int64(0)
		for subPackageIndex < lengthSubPackets {
			pack, i := parsePackage(subPackageBinary[subPackageIndex:])
			packs = append(packs, pack)
			subPackageIndex += i
		}
		index += lengthSubPackets
		return
	}
	if lengthTypeID == '1' {
		neededSubPackets, _ := strconv.ParseInt(packageString[index+1:index+11], 2, 0)
		index += 11
		subPackageBinary := packageString[index:]
		subPackageIndex := int64(0)
		countSubPackets := int64(0)
		for countSubPackets < neededSubPackets {
			pack, i := parsePackage(subPackageBinary[subPackageIndex:])
			packs = append(packs, pack)
			subPackageIndex += i
			countSubPackets++
		}
		index += subPackageIndex
		return
	}
	return
}
