package day_10

//import "core:container/queue"
import "core:log"
import "core:os"
import "core:strconv"
import "core:strings"

Machine :: struct {
	lights:       int `fmt:"10b"`,
	combinations: []int,
	buttons:      [][]int,
	joltages:     []int,
}

Input :: struct {
	machines: []Machine,
}

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.machines = make([]Machine, len(lines))
	for line, i in lines {
		chunks := strings.split(line, " ")
		defer delete(chunks)

		machine: Machine
		for l, l_i in chunks[0][1:len(chunks[0]) - 1] {
			machine.lights |= (1 if l == '#' else 0) << uint(l_i)
		}

		machine.buttons = make([][]int, len(chunks) - 2)
		machine.combinations = make([]int, len(chunks) - 2)
		for c, c_i in chunks[1:len(chunks) - 1] {
			num_splits := strings.count(c, ",")
			machine.buttons[c_i] = make([]int, len(c) - 2 - num_splits)

			val: int
			s_i: int
			for s in c[1:len(c) - 1] {
				if s == ',' {
					continue
				}

				b := s - '0'
				val |= 1 << uint(b)

				machine.buttons[c_i][s_i] = int(b)
				s_i += 1
			}
			machine.combinations[c_i] = val

			log.debugf("%s -> %10b [%d]", c, val, val)
			log.debugf("%s -> %v", c, machine.buttons[c_i])
		}

		joltages := strings.split(chunks[len(chunks) - 1][1:len(chunks[len(chunks) - 1]) - 1], ",")
		log.debugf("%v", joltages)
		defer delete(joltages)
		machine.joltages = make([]int, len(joltages))
		for j, j_i in joltages {
			machine.joltages[j_i] = strconv.parse_int(j) or_else 0
		}

		input.machines[i] = machine
	}

	log.debugf("%v", input.machines)

	return input
}

free_input :: proc(input: ^Input) {
	for machine in input.machines {
		for button in machine.buttons {
			delete(button)
		}
		delete(machine.buttons)
		delete(machine.combinations)
		delete(machine.joltages)
	}
	delete(input.machines)
}
// --- Input --- //


// --- Helpers --- //
bit_count := proc(x: int) -> int {
	x := x
	count: int
	for x != 0 {
		count += int(x & 1)
		x >>= 1
	}
	return count
}
// --- Helpers --- //


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

	for machine in input.machines {
		m := uint(len(machine.combinations))
		best := 9999
		total_subsets := 1 << m
		for subset in 0 ..< total_subsets {
			presses := bit_count(subset)
			if presses >= best {
				continue
			}

			state: int
			for i in 0 ..< m {
				if ((subset >> i) & 1) != 0 {
					state ~= machine.combinations[i]
				}
			}

			if state == machine.lights {
				best = presses
			}
		}

		log.debugf("%10b -> %d", machine.lights, best)
		result += best
	}

	return result
}

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


// --- Task 2 --- //
State :: struct {
	counters: [10]int,
}

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

	/*q: queue.Queue(State)
	queue.init(&q, 128)
	defer queue.destroy(&q)

	next: for machine, m_i in input.machines {
		log.debugf("machine %v", m_i)

		queue.clear(&q)

		start: State
		queue.push_back(&q, start)
		dist := make(map[[10]int]int)
		defer delete(dist)
		visited := make(map[[10]int]bool)
		defer delete(visited)
		for queue.len(q) > 0 {
			current := queue.pop_front(&q)

			done := true
			for j, j_i in machine.joltages {
				if current.counters[j_i] != j {
					done = false
					break
				}
			}
			if done {
				log.infof("FOUND %d", dist[current.counters])
				result += dist[current.counters]
				continue next
			}

			for button in machine.buttons {
				next: [10]int
				copy(next[:], current.counters[:])
				for b in button {
					next[b] += 1
				}

				exceeds: bool
				for j, j_i in machine.joltages {
					if next[j_i] > j {
						exceeds = true
						break
					}
				}
				if exceeds {
					continue
				}

				if _, ok := visited[next]; !ok {
					visited[next] = true
					dist[next] = dist[current.counters] + 1
					queue.push_back(&q, State{counters = next})
				}
			}
		}
	}*/

	return result
}

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

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

	result1 := run_task1(input)
	print_result1(result1)

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