package day_11

import "core:log"
import "core:os"
import "core:strings"

Input :: struct {
	rack_wires: map[string][]string,
}

Result1 :: int
Result2 :: int

// --- Input --- //
print_input :: proc(input: Input) {
	log.infof("%v", input)
}

parse_input_file :: proc(filepath: string) -> Input {
	input: Input

	raw_data, ok := os.read_entire_file_from_filename(filepath)
	if !ok {
		panic("oh no, could not read file")
	}
	defer delete(raw_data)

	lines, err := strings.split_lines(strings.trim_space(string(raw_data)))
	if err != .None {
		panic("oh no, failed splitting into lines")
	}
	defer delete(lines)

	input.rack_wires = make(map[string][]string)
	for line in lines {
		rack_wires := strings.split(line, ":")
		defer delete(rack_wires)
		wires := strings.split(strings.trim_space(rack_wires[1]), " ")
		defer delete(wires)

		rack := strings.clone(rack_wires[0])
		input.rack_wires[rack] = make([]string, len(wires))
		for w, i in wires {
			input.rack_wires[rack][i] = strings.clone(strings.trim_space(w))
		}
	}

	return input
}

free_input :: proc(input: ^Input) {
	for i in input.rack_wires {
		for w in input.rack_wires[i] {
			delete(w)
		}
		delete(i)
		delete(input.rack_wires[i])
	}
	delete(input.rack_wires)
}
// --- Input --- //


// --- Helpers --- //
State :: struct {
	node:     string,
	seen_fft: bool,
	seen_dac: bool,
}

paths :: proc(input: Input, paths_cache: ^map[string]int, name: string) -> int {
	if name == "out" {
		return 1
	}

	if cached, ok := paths_cache[name]; ok {
		return cached
	}

	total: int
	for child in input.rack_wires[name] {
		total += paths(input, paths_cache, child)
	}

	paths_cache[name] = total

	return total
}

paths_fft_dac :: proc(
	input: Input,
	paths_cache: ^map[State]int,
	name: string,
	seen_fft := false,
	seen_dac := false,
) -> int {
	if name == "out" {
		return 1 if seen_fft && seen_dac else 0
	}

	state := State {
		node     = name,
		seen_fft = seen_fft,
		seen_dac = seen_dac,
	}

	if cached, ok := paths_cache[state]; ok {
		return cached
	}

	state.seen_fft = seen_fft
	state.seen_dac = seen_dac

	total: int
	for child in input.rack_wires[name] {
		total += paths_fft_dac(
			input,
			paths_cache,
			child,
			(state.seen_fft || child == "fft"),
			(state.seen_dac || child == "dac"),
		)
	}

	paths_cache[state] = total

	return total
}
// --- Helpers --- //


// --- Task 1 --- //
run_task1 :: proc(input: Input) -> Result1 {
	result: Result1

	paths_cache := make(map[string]int)
	defer delete(paths_cache)

	result = paths(input, &paths_cache, "you")

	return result
}

print_result1 :: proc(result: Result1) {
	log.infof("Task 1: %d", result)
}
// --- Task 1 --- //


// --- Task 2 --- //
run_task2 :: proc(input: Input) -> Result2 {
	result: Result2

	paths_cache := make(map[State]int)
	defer delete(paths_cache)

	result = paths_fft_dac(input, &paths_cache, "svr")

	return result
}

print_result2 :: proc(result: Result2) {
	log.infof("Task 2: %d", result)
}
// --- Task 2 --- //

run :: proc() {
	input := parse_input_file("input/day_11.txt")
	defer free_input(&input)

	result1 := run_task1(input)
	print_result1(result1)

	result2 := run_task2(input)
	print_result2(result2)
}