package day_07

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

Input :: struct {
	starts_at:     [2]int,
	width, height: int,
	splitters:     map[[2]int]bool,
}

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.width = len(lines[0])
	input.height = len(lines)
	input.splitters = make(map[[2]int]bool)
	for line, y in lines {
		for c, x in line {
			if c == 'S' {
				input.starts_at = {x, y}
			} else if c == '^' {
				input.splitters[{x, y}] = true
			}
		}
	}

	return input
}

free_input :: proc(input: ^Input) {
	delete(input.splitters)
}
// --- Input --- //


// --- Helpers --- //
print_tree :: proc(input: Input) {
	b: strings.Builder
	strings.builder_init(&b)
	defer strings.builder_destroy(&b)

	fmt.sbprint(&b, "\n")
	for y in 0 ..< input.height {
		for x in 0 ..< input.width {
			if input.starts_at == {x, y} {
				fmt.sbprint(&b, "S")
			} else if ({x, y}) in input.splitters {
				fmt.sbprint(&b, "^")
			} else {
				fmt.sbprint(&b, ".")
			}
		}
		fmt.sbprint(&b, "\n")
	}

	log.infof(strings.to_string(b))
}

process_beam := proc(input: Input) -> (hit_splitters, num_timelines: int) {
	grid := make([]int, input.width * input.height)
	defer delete(grid)

	grid[(input.starts_at.y * input.width) + input.starts_at.x] = 1
	for y in 1 ..< input.height {
		i := y * input.width
		for x in 0 ..< input.width {
			pos := [2]int{x, y}
			timeline := grid[i + x - input.width]
			if timeline > 0 {
				if pos in input.splitters {
					hit_splitters += 1
					grid[i + x - 1] += timeline
					grid[i + x + 1] += timeline
				} else {
					grid[i + x] += timeline
				}
			}
		}
	}

	for i in len(grid) - input.width ..< len(grid) {
		num_timelines += grid[i]
	}
	print_grid(grid, input.width, input.height)

	return hit_splitters, num_timelines
}


print_grid :: proc(grid: []int, width, height: int) {
	b: strings.Builder
	strings.builder_init(&b)
	defer strings.builder_destroy(&b)

	fmt.sbprint(&b, "\n")
	for y in 0 ..< height {
		i := y * width
		for x in 0 ..< width {
			c := grid[i + x]
			if c > 0 {
				fmt.sbprintf(&b, " %d ", c)
			} else {
				fmt.sbprint(&b, " . ")
			}
		}
		fmt.sbprint(&b, "\n")
	}

	log.debugf(strings.to_string(b))
}
// --- Helpers --- //


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

	hit_splitters, _ := process_beam(input)
	result += hit_splitters

	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

	_, num_timelines := process_beam(input)
	result += num_timelines

	return result
}

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

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

	result1 := run_task1(input)
	print_result1(result1)

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