package main

import (
	"fmt"
	"sort"
	"strings"

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

func main() {
	inp := h.StdinToStringSlice()
	part1(inp)
	fmt.Println("")
	part2(inp)
}

func part1(inp []string) {
	fmt.Println("# Part 1")
	stacks, bottom := buildStacks(inp)

	// Run instructions
	// Instructions start at bottom+2
	for i := bottom + 2; i < len(inp); i++ {
		inst := strings.Fields(inp[i])
		count := h.Atoi(inst[1])
		from := h.Atoi(inst[3])
		to := h.Atoi(inst[5])
		for c := 0; c < count; c++ {
			moveCrates(stacks, from, to, 1)
		}
	}

	for i := range stacks {
		fmt.Print(string(stacks[i][0]))
	}
	fmt.Println("")
}

func part2(inp []string) {
	fmt.Println("# Part 2")
	stacks, bottom := buildStacks(inp)

	// Run instructions
	// Instructions start at bottom+2
	for i := bottom + 2; i < len(inp); i++ {
		inst := strings.Fields(inp[i])
		count := h.Atoi(inst[1])
		from := h.Atoi(inst[3])
		to := h.Atoi(inst[5])

		moveCrates(stacks, from, to, count)
	}

	for i := range stacks {
		fmt.Print(string(stacks[i][0]))
	}
	fmt.Println("")
}

func getCrateHash(stacks [][]byte) string {
	var ret []byte
	for i := range stacks {
		for j := range stacks[i] {
			ret = append(ret, stacks[i][j])
		}
	}
	sort.Slice(ret, func(p, q int) bool {
		return ret[p] > ret[q]
	})
	return string(ret)
}
func validateCrates(valid string, stacks [][]byte) bool {
	return valid == getCrateHash(stacks)
}

func buildStacks(inp []string) ([][]byte, int) {
	var stacks [][]byte
	// Find the bottom
	var bottom int
	for i, row := range inp {
		if len(row) > 1 && row[1] == '1' {
			bottom = i
			break
		}
	}
	// Allocate stacks
	stCnt := strings.Fields(inp[bottom])
	for i := 0; i < len(stCnt); i++ {
		stacks = append(stacks, []byte{})
	}

	// Build stacks
	for i := bottom - 1; i >= 0; i-- {
		for stI := 0; stI < len(stCnt); stI++ {
			pos := stI*4 + 1
			if len(inp[i]) >= pos && (inp[i][pos] >= 'A' && inp[i][pos] <= 'Z') {
				stacks[stI] = append([]byte{inp[i][pos]}, stacks[stI]...)
			}
		}
	}
	return stacks, bottom
}

func moveCrates(stacks [][]byte, from, to, count int) {
	var x []byte
	if count > 1 {
		x, stacks[from-1] = stacks[from-1][0:count], stacks[from-1][count:]
	} else {
		x, stacks[from-1] = []byte{stacks[from-1][0]}, stacks[from-1][count:]
	}
	for i := len(x) - 1; i >= 0; i-- {
		stacks[to-1] = append([]byte{x[i]}, stacks[to-1]...)
	}
}

func printStacks(stacks [][]byte) {
	for i := range stacks {
		fmt.Println(i+1, string(stacks[i]))
	}
}
