package main

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

Color :: enum {
	Red,
	Green,
	Blue,
}

Combination :: struct {
	num: int,
	col: Color,
}

Set :: [dynamic]Combination

Game :: struct {
	id:   int,
	sets: [dynamic]Set,
}

Input :: struct {
	games: [dynamic]Game,
}

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)

	input.games = make([dynamic]Game, 0, 10)

	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]

		game_and_sets := strings.split(line, ": ")
		current_game := strings.split(game_and_sets[0], " ")
		sets := strings.split(game_and_sets[1], "; ")

		game: Game
		game.id = strconv.atoi(current_game[1])
		game.sets = make([dynamic]Set, 0, 10)

		for set in sets {
			parsed_set := make([dynamic]Combination, 0, 10)

			combinations := strings.split(set, ", ")
			for combination in combinations {
				number_and_color := strings.split(combination, " ")

				col: Color
				switch number_and_color[1] {
				case "red":
					col = .Red
				case "green":
					col = .Green
				case "blue":
					col = .Blue
				}

				append(&parsed_set, Combination{num = strconv.atoi(number_and_color[0]), col = col})
			}

			append(&game.sets, parsed_set)
		}

		append(&input.games, game)
	}

	return input
}

free_input :: proc(input: ^Input) {
	for game in input.games {
		for set in game.sets {
			delete(set)
		}
		delete(game.sets)
	}
	delete(input.games)
}
// --- Input --- //


// --- Task 1 --- //
task1_rules := map[Color]int {
	.Red   = 12,
	.Green = 13,
	.Blue  = 14,
}
run_task1 :: proc(input: ^Input, debug: bool) -> Result1 {
	result: Result1

	if debug {
		fmt.printf("input: %#v\n", input)
	}

	for game in input.games {
		ok := true
		for set in game.sets {
			for combination in set {
				if task1_rules[combination.col] < combination.num {
					ok = false
					break
				}
			}
		}
		if ok {
			if debug {
				fmt.printf("Possible game: %d\n", game.id)
			}
			result += Result1(game.id)
		}
	}

	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 {
		fmt.printf("input: %#v\n", input)
	}

	for game in input.games {
		fewest_cubes_necessary := map[Color]int {
			.Red   = 0,
			.Green = 0,
			.Blue  = 0,
		}
		for set in game.sets {
			for combination in set {
				if num, _ := fewest_cubes_necessary[combination.col]; combination.num > num {
					fewest_cubes_necessary[combination.col] = combination.num
				}
			}
		}
		power := fewest_cubes_necessary[.Red] * fewest_cubes_necessary[.Green] * fewest_cubes_necessary[.Blue]
		if debug {
			fmt.printf("Possible game: %d\n", game.id)
			fmt.printf("Fewest cubes necessary: %#v -> %d\n", fewest_cubes_necessary, power)
		}
		result += Result2(power)
	}

	return result
}

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