package main

import "core:bytes"
import "core:fmt"
import "core:io"
import "core:os"

Input :: struct {
	width: int,
	grid:  []rune,
}

Result1 :: distinct int
Result2 :: distinct int


// --- 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")
	}

	buf: bytes.Buffer
	bytes.buffer_init(&buf, raw_data)
	defer bytes.buffer_destroy(&buf)

	current: int
	line: string
	err: io.Error
	for ; err != .EOF; line, err = bytes.buffer_read_string(&buf, '\n') {
		if line == "" || line == "\n" {
			continue
		}

		line = line[:len(line) - 1]

		if input.width == 0 {
			input.width = len(line)
			input.grid = make([]rune, input.width * input.width)
		}

		for c in line {
			input.grid[current] = c
			current += 1
		}
	}

	return input
}

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


// --- Task 1 --- //
read_number :: proc(line: []rune) -> (num, steps: int) {
	for i := 0; i < len(line); i += 1 {
		r := line[i]
		if r < '0' || r > '9' {
			break
		}
		num *= 10
		num += int(r - '0')
		steps += 1
	}
	return
}

print_input :: proc(input: ^Input) {
	for r, i in input.grid {
		if i % input.width == 0 {
			fmt.printf("\n")
		}
		fmt.printf("%c", r)
	}
	fmt.printf("\n")
}

run_task1 :: proc(input: ^Input, debug: bool) -> Result1 {
	result: Result1

	if debug {
		print_input(input)
	}

	for i := 0; i < len(input.grid); i += 1 {
		r := input.grid[i]
		if r < '0' || r > '9' {
			continue
		}

		start_i := i
		num, steps := read_number(input.grid[i:])
		i += steps

		x := start_i % input.width
		y := start_i / input.width
		start_x := x - 1 if x > 1 else 0
		start_y := y - 1 if y > 1 else 0
		end_x := x + steps if x + steps < input.width - 1 else input.width - 1
		end_y := y + 1 if y < input.width - 1 else input.width - 1

		if debug {
			fmt.printf(
				"number: %d (%d x %d -> %d x %d -> %d x %d) -> %d\n",
				start_i,
				start_x,
				start_y,
				x,
				y,
				end_x,
				end_y,
				num,
			)
		}

		has_part: bool
		loop: for yy := start_y; yy <= end_y; yy += 1 {
			for xx := start_x; xx <= end_x; xx += 1 {
				check := input.grid[(yy * input.width) + xx]
				if !has_part {
					has_part = (check < '0' || check > '9') && check != '.'
				}
				if !debug && has_part {
					break loop
				}
				if debug {
					fmt.printf("%c", check)
				}
			}
			if debug {
				fmt.printf("\n")
			}
		}
		if debug {
			fmt.printf("has part: %t\n", has_part)
			fmt.printf("\n")
		}

		if has_part {
			result += Result1(num)
		}
	}

	return result
}

print_result1 :: proc(result: ^Result1) {
	fmt.printf("Task 1: %d\n", result^)
}
// --- Task 1 --- //


// --- Task 2 --- //
run_task2 :: proc(input: ^Input, debug: bool) -> Result2 {
	result: Result2

	if debug {
		print_input(input)
	}

	for i := 0; i < len(input.grid); i += 1 {
		r := input.grid[i]
		if r != '*' {
			continue
		}

		x := i % input.width
		y := i / input.width
		start_x := x - 1 if x > 1 else 0
		start_y := y - 1 if y > 1 else 0
		end_x := x + 1 if x < input.width - 1 else input.width - 1
		end_y := y + 1 if y < input.width - 1 else input.width - 1

		if debug {
			fmt.printf("number: %d (%d x %d -> %d x %d -> %d x %d)\n", i, start_x, start_y, x, y, end_x, end_y)
		}

		count: int
		total := 1
		loop: for yy := start_y; yy <= end_y; yy += 1 {
			for xx := start_x; xx <= end_x; xx += 1 {
				grid_i := (yy * input.width) + xx
				check := input.grid[grid_i]
				if check >= '0' && check <= '9' {
					left := xx
					for ; left >= 0; left -= 1 {
						check_i := (yy * input.width) + left
						if input.grid[check_i] < '0' || input.grid[check_i] > '9' {
							break
						}
					}
					left += 1
					num, steps := read_number(input.grid[(yy * input.width) + left:])
					xx = left + steps

					if debug {
						fmt.printf("valid: %d\n", num)
					}
					count += 1
					total *= num
				}
			}
		}
		if debug {
			fmt.printf("ratio: %d, count: %d\n", total, count)
		}

		if count == 2 {
			result += Result2(total)
		}
	}

	return result
}

print_result2 :: proc(result: ^Result2) {
	fmt.printf("Task 2: %d\n", result^)
}
// --- Task 2 --- //