#include "step5.h"

#define TIME_CLEAR   30'000
#define TIME_PLANK   40'000
#define TIME_EMPTY   40'000
#define TIME_NOTHING 25'000
#define TIME_BLINK   35'000
#define TIME_UMMM    35'000
#define TIME_BLANK   40'000
#define TIME_WHAT    60'000
#define TIME_PLINK   40'000

__attribute__((unused))
static const char *TAG = "step5";

static const int TIMES_TO_SOLVE = 9;

static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> puzzle_dist(0, 7);
static std::uniform_int_distribution<> led_picker_dist(0, 20);
static std::uniform_int_distribution<> led_color_dist(0, 5);

void set_unique_leds(std::vector<int>& input_options, const int num, const int r, const int g, const int b) {
    for (int i = 0; i < num; i++) {
        std::uniform_int_distribution<> led_option_dist(0, input_options.size() - 1);
        int led = led_option_dist(gen);
        ESP_ERROR_CHECK(led_strip_set_pixel(leds, input_options[led], r, g, b));
        input_options.erase(input_options.begin() + led);
    }
}

void set_unique_leds_random_color(std::vector<int>& input_options, const int num, const int* r, const int* g, const int* b) {
    for (int i = 0; i < num; i++) {
        std::uniform_int_distribution<> led_option_dist(0, input_options.size() - 1);
        int led = led_option_dist(gen);

        std::uniform_int_distribution<> led_color_dist(0, sizeof(r) - 1);
        int color = led_color_dist(gen);
        ESP_ERROR_CHECK(led_strip_set_pixel(leds, input_options[led], r[color], g[color], b[color]));
        input_options.erase(input_options.begin() + led);
    }
}

std::vector<int> unique_values(std::vector<int>& input_options, int num) {
    std::vector<int> output_vec;
    for (int i = 0; i < num; i++) {
        std::uniform_int_distribution<> option_num_dist(0, input_options.size() - 1);
        int option_num = option_num_dist(gen);
        output_vec.push_back(input_options[option_num]);
        input_options.erase(input_options.begin() + option_num);
    }
    return output_vec;
}

bool submit_0(int green_indicators) {
    uint8_t state = get_switch_state();
    uint8_t flipped_state = 0;
    flipped_state |= (state & 0b1) << 3;
    flipped_state |= (state & 0b10) << 1;
    flipped_state |= (state & 0b100) >> 1;
    flipped_state |= (state & 0b1000) >> 3;

    if (flipped_state == green_indicators) {
        return true;
    } else {
        strike("Incorrect Switches");
        return false;
    }
}

bool submit_1(int num_indicators, std::string pressed_keys, uint8_t starting_switch_state) {
    if (num_indicators < 18) {
        if (get_switch_state() == 0b1111) {
            if (pressed_keys == "") {
                return true;
            } else {
                strike("Incorrect keypad!");
                return false;
            }
        } else {
            strike("Incorrect switches!");
            return false;
        }
    } else {
        if (starting_switch_state == get_switch_state()) {
            if (pressed_keys == "ADCB") {
                return true;
            } else {
                strike("Incorrect keypad!");
                return false;
            }
        } else {
            strike("Switches changed!");
            return false;
        }
    }
}

bool submit_2(bool* lit_leds, int green_pressed, int blue_pressed, int fingerprint_pressed, std::string keypad_string) {
    uint8_t switch_state = get_switch_state() & 0b11;

    bool rfid_correct = (lit_leds[0] && (green_pressed == 1)) || (!lit_leds[0] && (green_pressed == 0));
    ESP_LOGI(TAG, "rfid_correct: %i", rfid_correct);
    bool lcd_correct = (lit_leds[1] && (blue_pressed == 1)) || (!lit_leds[1] && (blue_pressed == 0));
    ESP_LOGI(TAG, "lcd_correct: %i", lcd_correct);
    bool speaker_correct = (lit_leds[2] && (fingerprint_pressed == 2)) || (!lit_leds[2] && (fingerprint_pressed == 0));
    ESP_LOGI(TAG, "speaker_correct: %i", speaker_correct);
    bool keypad_correct = (lit_leds[3] && (keypad_string == "12")) || (!lit_leds[3] && (keypad_string == ""));
    ESP_LOGI(TAG, "keypad_correct: %i", keypad_correct);
    bool tft_correct = (lit_leds[4] && (switch_state == 0b11)) || !lit_leds[4];
    ESP_LOGI(TAG, "tft_correct: %i", tft_correct);

    if (rfid_correct && lcd_correct && speaker_correct && keypad_correct && tft_correct) {
        return true;
    } else {
        strike("Incorrect state!");
        return false;
    }
}

bool submit_4(int* button_colors, int* switch_colors, uint8_t initial_switch_state) {
    if (button_colors[0] == 0 && button_colors[1] == 1 && button_colors[2] == 2 && button_colors[3] == 3) {
        uint8_t switch_state = get_switch_state();
        bool correct = true;
        
        for (int i = 0; i < 4; i++) {
            if (switch_colors[i] == 2) {
                if ((((switch_state ^ initial_switch_state) >> i) & 0b1) != 1) {
                    correct = false;
                    break;
                }
            } else {
                if (((switch_state >> i) & 0b1) != (switch_colors[i] & 0b1)) {
                    correct = false;
                    break;
                }
            }
        }

        if (correct) {
            return true;
        }
        strike("Incorrect switches!");
        return false;
    } else {
        strike("Incorrect buttons!");
        return false;
    }
}

bool submit_5(std::array<int, 4> indicator_numbers, std::array<int, 4> buttons_pressed) {
    if (buttons_pressed != indicator_numbers) {
        ESP_LOGI(TAG, "Button State: %i, %i, %i, %i, Indicator Numbers: %i, %i, %i, %i", buttons_pressed[0], buttons_pressed[1], buttons_pressed[2], buttons_pressed[3], indicator_numbers[0], indicator_numbers[1], indicator_numbers[2], indicator_numbers[3]);
        strike("Wrong button state!");
        return false;
    }

    uint8_t switch_state = get_switch_state();
    for (int i = 0; i < 4; i++) {
        ESP_LOGI(TAG, "Switch State: %i, Indicator Number: %i", ((switch_state >> i) & 0b1), indicator_numbers[i]);
        if (((switch_state >> i) & 0b1) == (indicator_numbers[i] & 0b1)) {
            strike("Wrong switch state!");
            return false;
        }
    }

    return true;
}

bool submit_6(bool* buttons_cycling, bool button_turned_on, int led_off) {
    for (int i = 0; i < sizeof(buttons_cycling); i++) {
        if ((buttons_cycling[i] == true) || (button_turned_on == false && led_off != -1)) {
            strike("Incorrect!");
            return false;
        }
    }
    return true;
}

void step5(void) {
    StarCodeHandler star_codes[] = {
        {
            .code = "*2648",
            .display_text = "Starting...",
            .should_exit = true,
            .callback = nullptr,
        },
    };
    int len = sizeof(star_codes)/sizeof(StarCodeHandler);
    do_star_codes(star_codes, len);

    std::vector<int> all_leds;

    for (int i = 0; i < 21; i++) {
        all_leds.push_back(i);
    }

    const int INDICATOR_RED[6] = {20, 0, 0, 10, 10, 5};
    const int INDICATOR_GREEN[6] = {0, 0, 10, 5, 0, 7};
    const int INDICATOR_BLUE[6] = {0, 10, 0, 0, 5, 5};
    static std::uniform_int_distribution<> led_number_dist(0, 21);

    int last_cycle_tick = xTaskGetTickCount();
    int scrambled_times = 0;
    while (scrambled_times < 5) {
        if ((xTaskGetTickCount() - last_cycle_tick) > pdMS_TO_TICKS(100)) {
            clean_bomb();
            std::vector<int> led_options = all_leds;
            set_unique_leds_random_color(led_options, led_number_dist(gen), INDICATOR_RED, INDICATOR_GREEN, INDICATOR_BLUE);
            ESP_ERROR_CHECK(led_strip_refresh(leds));
            last_cycle_tick = xTaskGetTickCount();
            scrambled_times++;
        }
    }

    clean_bomb();
    int solved_puzzles = 0;
    while (solved_puzzles < TIMES_TO_SOLVE) {
        lcd_set_cursor_pos(1, 1);
        bool solved_correctly = false;

        int puzzle = puzzle_dist(gen);
        // int puzzle = 2;

        switch (puzzle) {
            case 0: {
                lcd_print("Clear");
                set_module_time(TIME_CLEAR);
                start_module_timer();

                std::vector<int> indicator_options = all_leds;

                // set green
                std::uniform_int_distribution<> green_indicators_dist(1, 15);
                uint8_t green_indicators = green_indicators_dist(gen);
                set_unique_leds(indicator_options, green_indicators, 0, 10, 0);

                // set non-green
                const int NON_GREEN_INDICATOR_RED[3] = {20, 0, 10};
                const int NON_GREEN_INDICATOR_GREEN[3] = {0, 0, 0};
                const int NON_GREEN_INDICATOR_BLUE[3] = {0, 10, 5};
                std::uniform_int_distribution<> non_green_indicators_dist(0, (20 - green_indicators));
                set_unique_leds_random_color(indicator_options, non_green_indicators_dist(gen), NON_GREEN_INDICATOR_RED, NON_GREEN_INDICATOR_GREEN, NON_GREEN_INDICATOR_BLUE);

                ESP_ERROR_CHECK(led_strip_refresh(leds));

                // wait for submit
                KeypadKey key;
                while (1) {
                    if (get_module_time() <= 0 || (get_pressed_keypad(&key) && (char_of_keypad_key(key) == '#'))) {
                        solved_correctly = submit_0(green_indicators);
                        break;
                    }
                    vTaskDelay(pdMS_TO_TICKS(10));
                }

                break;
            }
            case 1: {
                lcd_print("Blank");
                set_module_time(TIME_BLANK);
                start_module_timer();
                
                std::uniform_int_distribution<> on_indicators_dist(16, 21);
                uint8_t indicators_num = on_indicators_dist(gen);

                std::vector<int> indicator_options = all_leds;
                const int INDICATOR_RED[6] = {20, 0, 0, 10, 10, 5};
                const int INDICATOR_GREEN[6] = {0, 0, 10, 5, 0, 7};
                const int INDICATOR_BLUE[6] = {0, 10, 0, 0, 5, 5};
                
                set_unique_leds_random_color(indicator_options, indicators_num, INDICATOR_RED, INDICATOR_BLUE, INDICATOR_GREEN);
                ESP_ERROR_CHECK(led_strip_refresh(leds));

                uint8_t starting_switch_state = get_switch_state();
                std::string pressed_keys;

                KeypadKey key;
                while (1) {
                    if (get_pressed_keypad(&key)) {
                        char keypad_char = char_of_keypad_key(key);
                        if (keypad_char == '#') {
                            solved_correctly = submit_1(indicators_num, pressed_keys, starting_switch_state);
                            break;
                        } else {
                            pressed_keys += char_of_keypad_key(key);
                        }
                    }
                    if (get_module_time() <= 0) {
                        solved_correctly = submit_1(indicators_num, pressed_keys, starting_switch_state);
                        break;
                    }
                    vTaskDelay(pdMS_TO_TICKS(10));
                }
                break;
            }
            case 2: {
                set_module_time(TIME_EMPTY);
                start_module_timer();

                std::map<int, int> idx_to_led_map = {
                    {0, 10},
                    {1, 12},
                    {2, 9},
                    {3, 11},
                    {4, 6},
                };

                std::uniform_int_distribution<> lit_led_dist(0, 1);

                // rfid, lcd, speaker, keypad, tft
                bool lit_leds[5] = {static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen)), static_cast<bool>(lit_led_dist(gen))};

                for (int i = 0; i < 5; i++) {
                    if (lit_leds[i]) {
                        int color = led_color_dist(gen);
                        ESP_ERROR_CHECK(led_strip_set_pixel(leds, idx_to_led_map[i], INDICATOR_RED[color], INDICATOR_GREEN[color], INDICATOR_BLUE[color]));
                    }
                }
                ESP_ERROR_CHECK(led_strip_refresh(leds));

                int green_button_pressed = 0;
                int blue_button_pressed = 0;
                int fingerprint_sensor_pressed = 0;
                std::string keypad_string;

                KeypadKey key;
                ButtonKey button;

                while (1) {
                    if (get_pressed_button(&button)) {
                        uint8_t button_state = get_button_state();

                        if ((button_state & 0b1) == 0b1) {
                            green_button_pressed++;
                            if ((green_button_pressed > 1) || !lit_leds[0]) {
                                strike("Too many times!");
                                break;
                            }
                        }
                        if ((button_state & 0b1000) == 0b1000) {
                            blue_button_pressed++;
                            if ((blue_button_pressed > 1) || !lit_leds[1]) {
                                strike("Too many times!");
                                break;
                            }
                        }
                    }
                    if (get_touch_pressed()) {
                        fingerprint_sensor_pressed++;
                        // ESP_LOGI(TAG, "touch sensor pressed");
                        if ((fingerprint_sensor_pressed > 2) || !lit_leds[2]) {
                                strike("Too many times!");
                                break;
                            }
                    }
                    if (get_pressed_keypad(&key)) {
                        char keypad_char = char_of_keypad_key(key);
                        if (keypad_char == '#') {
                            solved_correctly = submit_2(lit_leds, green_button_pressed, blue_button_pressed, fingerprint_sensor_pressed, keypad_string);
                            break;
                        } else {
                            // ESP_LOGI(TAG, "pressed %c", char_of_keypad_key(key));
                            keypad_string += char_of_keypad_key(key);
                        }
                    }
                    if (get_module_time() <= 0) {
                        solved_correctly = submit_2(lit_leds, green_button_pressed, blue_button_pressed, fingerprint_sensor_pressed, keypad_string);
                        break;
                    }
                    vTaskDelay(pdMS_TO_TICKS(10));
                }

                break;
            }
            case 3: {
                lcd_print("Nothing");
                set_module_time(TIME_NOTHING);
                start_module_timer();

                const int COLOR_RED[5] = {0, 20, 10, 0};
                const int COLOR_GREEN[5] = {20, 0, 10, 0};
                const int COLOR_BLUE[5] = {0, 0, 0, 20};

                static std::uniform_int_distribution<> color_dist(0, 3);

                int tft_color = color_dist(gen);
                int speaker_color = color_dist(gen);
                int s3_color = color_dist(gen);

                ESP_ERROR_CHECK(led_strip_set_pixel(leds, 6, COLOR_RED[tft_color], COLOR_GREEN[tft_color], COLOR_BLUE[tft_color]));
                ESP_ERROR_CHECK(led_strip_set_pixel(leds, 9, COLOR_RED[speaker_color], COLOR_GREEN[speaker_color], COLOR_BLUE[speaker_color]));
                ESP_ERROR_CHECK(led_strip_set_pixel(leds, 14, COLOR_RED[s3_color], COLOR_GREEN[s3_color], COLOR_BLUE[s3_color]));
                ESP_ERROR_CHECK(led_strip_refresh(leds));

                int buttons_pressed = 0;

                ButtonKey button;
                while (1) {
                    if (get_pressed_button(&button)) {
                        buttons_pressed++;
                        uint8_t button_state = get_button_state();

                        if (buttons_pressed == 1) {
                            if ((button_state >> tft_color) != 0b1) {
                                strike("Wrong button!");
                                break;
                            }
                        } else if (buttons_pressed == 2) {
                            if ((button_state >> speaker_color) != 0b1) {
                                strike("Wrong button!");
                                break;
                            }
                        } else if (buttons_pressed == 3) {
                            if ((button_state >> s3_color) != 0b1) {
                                strike("Wrong button!");
                            } else {
                                solved_correctly = true;
                                break;
                            }
                        }

                    }
                    if (get_module_time() <= 0) {
                        strike("Ran out of time!");
                        break;
                    }
                    vTaskDelay(pdMS_TO_TICKS(10));
                }
                break;
            }
            case 4: {
                lcd_print("Blink");
                set_module_time(TIME_BLINK);
                start_module_timer();

                // buttons
                const int BUTTON_COLOR_RED[4] = {0, 20, 10, 0};
                const int BUTTON_COLOR_GREEN[4] = {10, 0, 5, 0};
                const int BUTTON_COLOR_BLUE[4] = {0, 0, 0, 10};

                static std::uniform_int_distribution<> button_color_dist(0, 3);
                int button_colors[4] = {button_color_dist(gen), button_color_dist(gen), button_color_dist(gen), button_color_dist(gen)};

                for (int i = 0; i < 4; i++) {
                    ESP_ERROR_CHECK(led_strip_set_pixel(leds, (20 - i), BUTTON_COLOR_RED[button_colors[i]], BUTTON_COLOR_GREEN[button_colors[i]], BUTTON_COLOR_BLUE[button_colors[i]]));
                }

                // switches
                const int SWITCH_COLOR_RED[3] = {20, 0, 10};
                const int SWITCH_COLOR_GREEN[3] = {0, 10, 5};

                static std::uniform_int_distribution<> switch_color_dist(0, 2);

                int switch_colors[4] = {switch_color_dist(gen), switch_color_dist(gen), switch_color_dist(gen), switch_color_dist(gen)};

                for (int i = 0; i < 4; i++) {
                    ESP_ERROR_CHECK(led_strip_set_pixel(leds, (16 - i), SWITCH_COLOR_RED[switch_colors[i]], SWITCH_COLOR_GREEN[switch_colors[i]], 0));
                }

                ESP_ERROR_CHECK(led_strip_refresh(leds));

                ButtonKey button;
                KeypadKey key;
                uint8_t starting_switch_state = get_switch_state();

                while (1) {
                    if (get_pressed_button(&button)) {
                        uint8_t button_state = get_button_state();

                        for (int i = 0; i < 4; i++) {
                            if (((button_state >> i) & 0b1) == 0b1) {
                                button_colors[i]++;
                                if (button_colors[i] > 3) {
                                    button_colors[i] = 0;
                                }
                                ESP_ERROR_CHECK(led_strip_set_pixel(leds, (20 - i), BUTTON_COLOR_RED[button_colors[i]], BUTTON_COLOR_GREEN[button_colors[i]], BUTTON_COLOR_BLUE[button_colors[i]]));
                            }

                        }
                        ESP_ERROR_CHECK(led_strip_refresh(leds));
                    }
                    if (get_module_time() <= 0 || (get_pressed_keypad(&key) && (char_of_keypad_key(key) == '#'))) {
                        solved_correctly = submit_4(button_colors, switch_colors, starting_switch_state);
                        break;
                    }
                    vTaskDelay(pdMS_TO_TICKS(10));
                }
                break;
            }
            case 5: {
                lcd_print("Ummm");
                set_module_time(TIME_UMMM);
                start_module_timer();

                std::uniform_int_distribution<> indicator_number_dist(0, 5);

                const int INDICATOR_RED[4] = {0, 20, 10, 0};
                const int INDICATOR_GREEN[4] = {10, 0, 5, 0};
                const int INDICATOR_BLUE[4] = {0, 0, 0, 10};

                // green, red, yellow, blue
                std::array<int, 4> indicator_numbers = {indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen)};
                std::vector<int> indicator_options = all_leds;

                for (int i = 0; i < 4; i++) {
                    set_unique_leds(indicator_options, indicator_numbers[i], INDICATOR_RED[i], INDICATOR_GREEN[i], INDICATOR_BLUE[i]);
                }
                ESP_ERROR_CHECK(led_strip_refresh(leds));

                std::array<int, 4> buttons_pressed = {0, 0, 0, 0};
                ButtonKey button;
                KeypadKey key;

                while (1) {
                    if (get_pressed_button(&button)) {
                        uint8_t button_state = get_button_state();
                        bool failed = false;

                        for (int i = 0; i < 4; i++) {
                            if (((button_state >> i) & 0b1) == 0b1) {
                                buttons_pressed[i]++;
                                if (buttons_pressed[i] > indicator_numbers[i]) {
                                    strike("Too many times!");
                                    failed = true;
                                    break;
                                }
                            }
                        }
                        if (failed) {
                            break;
                        }
                    }
                    if (get_module_time() <= 0 || (get_pressed_keypad(&key) && (char_of_keypad_key(key) == '#'))) {
                        ESP_LOGI(TAG, "Keypad Char: %c, time: %lu", char_of_keypad_key(key), get_module_time());
                        solved_correctly = submit_5(indicator_numbers, buttons_pressed);
                        break;
                    }
                    vTaskDelay(pdMS_TO_TICKS(10));
                }

                break;
            }
            case 6: {
                lcd_print("Plank");
                set_module_time(TIME_PLANK);
                start_module_timer();

                std::uniform_int_distribution<> led_color_dist(0, 5);
                std::uniform_int_distribution<> led_off_dist(-1, 3);

                // red, purple, blue, white, green, yellow
                const uint8_t COLORS_RED[6] = {20, 10, 0, 5, 0, 10};
                const uint8_t COLORS_GREEN[6] = {0, 0, 0, 7, 10, 5};
                const uint8_t COLORS_BLUE[6] = {0, 10, 10, 5, 0, 0};

                int button_colors[4];
                bool buttons_cycling[4];
                int led_off = led_off_dist(gen);

                for (int i = 0; i < 4; i++) {
                    if (led_off != i) {
                        button_colors[i] = led_color_dist(gen);
                        ESP_ERROR_CHECK(led_strip_set_pixel(leds, (20 - i), COLORS_RED[button_colors[i]], COLORS_GREEN[button_colors[i]], COLORS_BLUE[button_colors[i]]));
                        buttons_cycling[i] = true;
                    } else {
                        button_colors[i] = -1;
                        buttons_cycling[i] = false;
                    }
                }
                ESP_ERROR_CHECK(led_strip_refresh(leds));

                const uint8_t CORRECT_COLORS[4] = {4, 0, 5, 2};
                TickType_t lastCycleTime = xTaskGetTickCount();
                bool button_turned_on = false;

                ButtonKey button;
                KeypadKey key;

                std::uniform_int_distribution<> led_turn_on_dist(0, 3);
                while (1) {
                    if (get_pressed_button(&button)) {
                        uint8_t button_state = get_button_state();
                        bool failed = false;

                        for (int i = 0; i < 4; i++) {
                            if (((button_state >> i) & 0b1) == 0b1) {
                                if (button_colors[i] == -1) {
                                    if (button_turned_on) {
                                        strike("Pressed after on!");
                                        failed = true;
                                        break;
                                    } else if (led_turn_on_dist(gen) == 0) {
                                        button_turned_on = true;
                                        led_strip_set_pixel(leds, (20 - i), COLORS_RED[CORRECT_COLORS[i]], COLORS_GREEN[CORRECT_COLORS[i]], COLORS_BLUE[CORRECT_COLORS[i]]);
                                        led_strip_refresh(leds);
                                    }
                                } else if (button_colors[i] != CORRECT_COLORS[i]) {
                                    strike("Wrong time!");
                                    failed = true;
                                    break;
                                } else {
                                    buttons_cycling[i] = false;
                                }
                            }
                        }
                        if (failed) {
                            break;
                        }
                    }
                    if ((xTaskGetTickCount() - lastCycleTime) >= pdMS_TO_TICKS(500)) {
                        for (int i = 0; i < 4; i++) {
                            if (buttons_cycling[i]) {
                                button_colors[i]++;
                                if (button_colors[i] > 5) {
                                    button_colors[i] = 0;
                                }
                                ESP_ERROR_CHECK(led_strip_set_pixel(leds, (20 - i), COLORS_RED[button_colors[i]], COLORS_GREEN[button_colors[i]], COLORS_BLUE[button_colors[i]]));
                            }
                        }
                        ESP_ERROR_CHECK(led_strip_refresh(leds));

                        lastCycleTime = xTaskGetTickCount();
                    }
                    if (get_module_time() <= 0 || (get_pressed_keypad(&key) && (char_of_keypad_key(key) == '#'))) {
                        solved_correctly = submit_6(buttons_cycling, button_turned_on, led_off);
                        break;
                    }
                    vTaskDelay(pdMS_TO_TICKS(10));
                }
                break;
            }
            case 7: {
                lcd_print("What");
                set_module_time(TIME_WHAT);
                start_module_timer();

                std::uniform_int_distribution<> math_number_dist(1, 9);

                std::vector<float> math_numbers;
                std::vector<int> math_operations;

                std::map<int, char> operation_map = {
                    {0, '+'},
                    {1, '-'},
                    {2, '*'},
                    {3, '/'},
                };

                int expression_answer = -1;
                std::string display_expression;

                while (expression_answer < 0) {
                    math_numbers = {static_cast<float>(math_number_dist(gen)), static_cast<float>(math_number_dist(gen)), static_cast<float>(math_number_dist(gen)), static_cast<float>(math_number_dist(gen))};
                    std::vector<int> possible_math_operations = {0, 1, 2, 3}; 
                    math_operations = unique_values(possible_math_operations, 3);

                    display_expression = std::to_string(static_cast<int>(math_numbers[0]));
                    for (int i = 0; i < 3; i++) {
                        display_expression += operation_map[math_operations[i]];
                        display_expression += std::to_string(static_cast<int>(math_numbers[i + 1]));
                    }

                    // Solve
                    for (int j = 0; j < 3; j++) {
                        bool found = false;
                        for (int i = 0; i < math_operations.size(); i++) {
                            if (math_operations[i] == 2) {
                                // ESP_LOGI(TAG, "i = %i, condensing %f * %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] * math_numbers[i+1]));
                                math_numbers[i] = math_numbers[i] * math_numbers[i + 1];
                                math_numbers.erase(math_numbers.begin() + i + 1);
                                math_operations.erase(math_operations.begin() + i);
                                found = true;
                                break;
                            } else if (math_operations[i] == 3) {
                                // ESP_LOGI(TAG, "i = %i, condensing %f / %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] / math_numbers[i+1]));
                                math_numbers[i] = math_numbers[i] / math_numbers[i + 1];
                                math_numbers.erase(math_numbers.begin() + i + 1);
                                math_operations.erase(math_operations.begin() + i);
                                found = true;
                                break;
                            }
                        }
                        if (found) continue;
                        for (int i = 0; i < math_operations.size(); i++) {
                            if (math_operations[i] == 0) {
                                // ESP_LOGI(TAG, "i = %i, condensing %f + %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] + math_numbers[i+1]));
                                math_numbers[i] = math_numbers[i] + math_numbers[i + 1];
                                math_numbers.erase(math_numbers.begin() + i + 1);
                                math_operations.erase(math_operations.begin() + i);
                                found = true;
                                break;
                            } else if (math_operations[i] == 1) {
                                // ESP_LOGI(TAG, "i = %i, condensing %f - %f to %f", i, math_numbers[i], math_numbers[i + 1], (math_numbers[i] - math_numbers[i+1]));
                                math_numbers[i] = math_numbers[i] - math_numbers[i + 1];
                                math_numbers.erase(math_numbers.begin() + i + 1);
                                math_operations.erase(math_operations.begin() + i);
                                found = true;
                                break;
                            }
                        }
                    }
                    expression_answer = static_cast<int>(std::floor(math_numbers[0]));
                }

                // display expression
                lcd_print(1, 2, display_expression.c_str());

                // set LEDs

                // blue, red, green, yellow
                const int INDICATOR_RED[4] = {0, 20, 0, 10};
                const int INDICATOR_GREEN[4] = {0, 0, 10, 5};
                const int INDICATOR_BLUE[4] = {10, 0, 0, 0};

                std::uniform_int_distribution<> add_sub_indicator_dist(1, 6);
                std::uniform_int_distribution<> mult_div_indicator_dist(1, 3);

                int modifier_indicators[4] = {add_sub_indicator_dist(gen), add_sub_indicator_dist(gen), mult_div_indicator_dist(gen), mult_div_indicator_dist(gen)};


                while ((((expression_answer + (modifier_indicators[0] * 3) - modifier_indicators[1]) * std::pow(3, modifier_indicators[2])) / std::pow(2, modifier_indicators[3])) < 0) {
                    modifier_indicators[0] = add_sub_indicator_dist(gen);
                    modifier_indicators[1] = add_sub_indicator_dist(gen);
                    modifier_indicators[2] = mult_div_indicator_dist(gen);
                    modifier_indicators[3] = mult_div_indicator_dist(gen);
                }

                expression_answer += modifier_indicators[0] * 3;
                expression_answer -= modifier_indicators[1];
                expression_answer *= std::pow(3, modifier_indicators[2]);
                expression_answer /= std::pow(2, modifier_indicators[3]);

                std::vector<int> led_options = all_leds;
                for (int i = 0; i < 4; i++) {
                    set_unique_leds(led_options, modifier_indicators[i], INDICATOR_RED[i], INDICATOR_GREEN[i], INDICATOR_BLUE[i]);
                }

                ESP_ERROR_CHECK(led_strip_refresh(leds));

                std::string answer_string = std::to_string(expression_answer);
                std::string entered_string = "";

                ESP_LOGI(TAG, "Solved full answer: %s", answer_string.c_str());

                KeypadKey key;

                while (1) {
                    if (get_pressed_keypad(&key)) {
                        if (key == KeypadKey::star) {
                            // clear
                            entered_string = "";
                        } else if (key == KeypadKey::pound) {
                            // submit
                            if (entered_string != answer_string) {
                                strike("Incorrect answer!");
                            } else {
                                solved_correctly = true;
                            }
                            break;
                        } else {
                            entered_string += char_of_keypad_key(key);
                        }

                        lcd_clear();
                        lcd_print(1, 1, "What");
                        lcd_print(1, 2, display_expression.c_str());
                        lcd_print(1, 3, entered_string.c_str());
                    }
                    if (get_module_time() <= 0) {
                        strike("Ran out of time!");
                        break;
                    }

                    vTaskDelay(pdMS_TO_TICKS(10));
                }

                break;
            }
            case 8: {
                lcd_print("Plink");
                set_module_time(TIME_PLINK);
                start_module_timer();

                std::uniform_int_distribution<> indicator_number_dist(0, 4);

                // ESP_LOGI(TAG, "Green: %i, Red: %i, Yellow: %i, Blue: %i", green_indicators, red_indicators, yellow_indicators, blue_indicators);

                // green, red, yellow, blue, purple
                const int INDICATOR_RED[5] = {0, 20, 10, 0, 10};
                const int INDICATOR_GREEN[5] = {10, 0, 5, 0, 0};
                const int INDICATOR_BLUE[5] = {0, 0, 0, 10, 5};

                int solved_times = 0;
                bool failed = false;
                while (solved_times < 3 && !failed) {
                    int indicator_numbers[5] = {indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen), indicator_number_dist(gen)};

                    std::vector<int> led_options = all_leds;
                    for (int i = 0; i < 5; i++) {
                        set_unique_leds(led_options, indicator_numbers[i], INDICATOR_RED[i], INDICATOR_GREEN[i], INDICATOR_BLUE[i]);
                    }


                    ESP_ERROR_CHECK(led_strip_refresh(leds));

                    std::uniform_int_distribution<> answer_color_dist(0, 4);

                    std::map<int, std::string> color_name_map = {
                        {0, "Green"},
                        {1, "Red"},
                        {2, "Yellow"},
                        {3, "Blue"},
                        {4, "Purple"},
                    };

                    int answer_color = answer_color_dist(gen);

                    std::string color_string = color_name_map[answer_color];
                    std::string answer_num = std::to_string(indicator_numbers[answer_color]);

                    // ESP_LOGI(TAG, "color string: %s", color_string.c_str());

                    lcd_print(1, 2, color_string.c_str());

                    std::string entered_string;

                    KeypadKey key;
                    while (1) {
                        if (get_pressed_keypad(&key)) {
                            bool failed = false;

                            if (key == KeypadKey::star) {
                                // clear
                                entered_string = "";
                            } else if (key == KeypadKey::pound) {
                                // submit
                                if (entered_string != answer_num) {
                                    strike("Incorrect answer!");
                                    failed = true;
                                } else {
                                    solved_correctly = true;;
                                }
                                break;
                            } else {
                                entered_string += char_of_keypad_key(key);
                            }

                            if (failed) {
                                break;
                            }

                            lcd_clear();
                            lcd_print(1, 1, "Plink");
                            lcd_print(1, 2, color_string.c_str());
                            lcd_print(1, 3, entered_string.c_str());
                        }
                        if (get_module_time() <= 0) {
                            strike("Ran out of time!");
                            break;
                        }


                        vTaskDelay(pdMS_TO_TICKS(10));
                    }

                    break;

                }
                if (!failed) {
                    solved_correctly = true;
                }
            }


        }
        stop_module_timer();
        if (solved_correctly) {
            solved_puzzles++;
            play_raw(MOUNT_POINT "/correct.pcm");
            vTaskDelay(pdMS_TO_TICKS(500));
            solved_correctly = false;
        } else {
            vTaskDelay(pdMS_TO_TICKS(3000));
        }
        clear_all_pressed_released();
        clean_bomb();
    }
}
