#include "step0.h"

static const char* TAG = "step0";

extern uint32_t initial_game_time;
extern uint32_t skip_to_step;

static void set_game_time(void);
static void skip_to_step1(void) { skip_to_step = 1; }
static void skip_to_step2(void) { skip_to_step = 2; }
static void skip_to_step3(void) { skip_to_step = 3; }
static void skip_to_step4(void) { skip_to_step = 4; }
static void skip_to_step5(void) { skip_to_step = 5; }
static void skip_to_step6(void) { skip_to_step = 6; }
static void try_step1(void) { clean_bomb(); step1(); }
static void try_step2(void) { clean_bomb(); step2(); }
static void try_step3(void) { clean_bomb(); step3(); }
static void try_step4(void) { clean_bomb(); step4(); }
static void try_step5(void) { clean_bomb(); step5(); }
static void try_step6(void) { clean_bomb(); step6(); }
static void issue_strike(void) { strike("Strike Issued"); }
static void debug_switches(void);
static void battery_stats(void) {
    BaseType_t xReturned;
    TaskHandle_t xHandle = NULL;
    xReturned = xTaskCreate(bat_monitor_task, "bat_monitor", 4096, NULL, 5, &xHandle);

    KeypadKey k;
    while (!get_pressed_keypad(&k) || k != KeypadKey::pound) vTaskDelay(pdMS_TO_TICKS(10));

    if (xReturned == pdPASS) {
        vTaskDelete(xHandle);
    }
}

/// Wait for "*9819"
void step0(void) {
    led_strip_set_pixel(leds, Led::speaker, 0, 0, 20);
    led_strip_refresh(leds);
    StarCodeHandler star_codes[] = {
        {
            .code = "*9819",
            .display_text = "Diffusal Initiated",
            .should_exit = true,
            .callback = nullptr,
        },
        {
            .code = "*59861",
            .display_text = "Set Up Wires",
            .should_exit = false,
            .callback = setup_wires,
        },
        {
            .code = "*59862",
            .display_text = "Set Game Time",
            .should_exit = false,
            .callback = set_game_time,
        },
        {
            .code = "*59863",
            .display_text = "Debug Switches",
            .should_exit = false,
            .callback = debug_switches,
        },
        {
            .code = "*59864",
            .display_text = "Battery Stats",
            .should_exit = false,
            .callback = battery_stats,
        },
        {
            .code = "*59871",
            .display_text = "Try Step 1",
            .should_exit = false,
            .callback = try_step1,
        },
        {
            .code = "*59872",
            .display_text = "Try Step 2",
            .should_exit = false,
            .callback = try_step2,
        },
        {
            .code = "*59873",
            .display_text = "Try Step 3",
            .should_exit = false,
            .callback = try_step3,
        },
        {
            .code = "*59874",
            .display_text = "Try Step 4",
            .should_exit = false,
            .callback = try_step4,
        },
        {
            .code = "*59875",
            .display_text = "Try Step 5",
            .should_exit = false,
            .callback = try_step5,
        },
        {
            .code = "*59876",
            .display_text = "Try Step 6",
            .should_exit = false,
            .callback = try_step6,
        },
        {
            .code = "*59881",
            .display_text = "Skip To Step 1",
            .should_exit = true,
            .callback = skip_to_step1,
        },
        {
            .code = "*59882",
            .display_text = "Skip To Step 2",
            .should_exit = true,
            .callback = skip_to_step2,
        },
        {
            .code = "*59883",
            .display_text = "Skip To Step 3",
            .should_exit = true,
            .callback = skip_to_step3,
        },
        {
            .code = "*59884",
            .display_text = "Skip To Step 4",
            .should_exit = true,
            .callback = skip_to_step4,
        },
        {
            .code = "*59885",
            .display_text = "Skip To Step 5",
            .should_exit = true,
            .callback = skip_to_step5,
        },
        {
            .code = "*59886",
            .display_text = "Skip To Step 6",
            .should_exit = true,
            .callback = skip_to_step6,
        },
        {
            .code = "*1111",
            .display_text = "Issue Strike",
            .should_exit = false,
            .callback = issue_strike,
        },
    };
    int len = sizeof(star_codes)/sizeof(StarCodeHandler);
    do_star_codes(star_codes, len);
}


static const int CURSOR_POS_MAP[5] = {1, 3, 4, 6, 7};
static char str_buf[18] = {0};
static void _update_display(uint8_t* digits, uint8_t cursor_pos) {
    sprintf(str_buf, "%d:%d%d:%d%d", digits[0], digits[1], digits[2], digits[3], digits[4]);
    lcd_clear();
    lcd_print(1, 1, str_buf);
    cursor_pos = MAX(0, MIN(4, cursor_pos));
    int mapped_cursor_pos = CURSOR_POS_MAP[cursor_pos];
    lcd_set_cursor_pos(mapped_cursor_pos, 1);
}

static void set_game_time(void) {
    uint8_t hours = (initial_game_time / (1000*60*60)) % 10;
    uint8_t minutes = (initial_game_time / (1000*60)) % 60;
    uint8_t seconds = (initial_game_time / (1000)) % 60;
    uint8_t digits[5] = {hours, (uint8_t)(minutes / 10), (uint8_t)(minutes % 10), (uint8_t)(seconds / 10), (uint8_t)(seconds % 10)};
    uint8_t cursor_pos = 0;
    lcd_set_cursor_vis(true);

    _update_display(digits, cursor_pos);

    KeypadKey key;
    ButtonKey button;
    while (1) {
        while (get_pressed_keypad(&key)) {
            if (key == KeypadKey::star) {
                digits[0] = 0;
                digits[1] = 0;
                digits[2] = 0;
                digits[3] = 0;
                digits[4] = 0;
                cursor_pos = 0;
            } else if (key == KeypadKey::pound) {
                // submit
                if (digits[0] != 0 || digits[1] != 0 || digits[2] != 0 || digits[3] != 0 || digits[4] != 0) {
                    uint32_t new_game_time = 0;
                    new_game_time += digits[0] * (1000*60*60);
                    new_game_time += (digits[1] * 10 + digits[2]) * (1000*60);
                    new_game_time += (digits[3] * 10 + digits[4]) * (1000);
                    initial_game_time = new_game_time;
                }

                clean_bomb();
                led_strip_set_pixel(leds, Led::speaker, 0, 0, 20);
                led_strip_refresh(leds);
                return;
            } else {
                int just_pressed = -1;
                switch (key) {
                    case KeypadKey::k0:
                    just_pressed = 0;
                    break;
                    case KeypadKey::k1:
                    just_pressed = 1;
                    break;
                    case KeypadKey::k2:
                    just_pressed = 2;
                    break;
                    case KeypadKey::k3:
                    just_pressed = 3;
                    break;
                    case KeypadKey::k4:
                    just_pressed = 4;
                    break;
                    case KeypadKey::k5:
                    just_pressed = 5;
                    break;
                    case KeypadKey::k6:
                    just_pressed = 6;
                    break;
                    case KeypadKey::k7:
                    just_pressed = 7;
                    break;
                    case KeypadKey::k8:
                    just_pressed = 8;
                    break;
                    case KeypadKey::k9:
                    just_pressed = 9;
                    break;
                    default:
                    break;
                }

                if (just_pressed != -1) {
                    digits[cursor_pos] = just_pressed;
                    cursor_pos = MIN(4, cursor_pos+1);
                }
            }

            _update_display(digits, cursor_pos);
        }

        while (get_pressed_button(&button)) {
            if (button == ButtonKey::b1) {
                cursor_pos = MAX(0, cursor_pos-1);
            } else if (button == ButtonKey::b2) {
                cursor_pos = MIN(4, cursor_pos+1);
            }
            _update_display(digits, cursor_pos);
        }

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}


static void print_4bin_rev(char* out_str, uint8_t n) {
    out_str[0] = ((n & 0b0001) ? '1' : '0');
    out_str[1] = ((n & 0b0010) ? '1' : '0');
    out_str[2] = ((n & 0b0100) ? '1' : '0');
    out_str[3] = ((n & 0b1000) ? '1' : '0');
    out_str[4] = '\0';
}

static void debug_switches(void) {
    clean_bomb();
    uint8_t switch_state = 0xFF;
    uint8_t button_state = 0xFF;

    char bin_buf[5] = {0};
    char buf[21] = {0};

    KeypadKey key;
    ButtonKey button;
    SwitchKey switch_;
    while (1) {
        if (get_pressed_button(&button)) {
            sprintf(buf, "Button Pressed: %d  ", button);
            lcd_print(0, 3, buf);
            ESP_LOGI(TAG, "%s", buf);
        }
        if (get_released_button(&button)) {
            sprintf(buf, "Button Released: %d ", button);
            lcd_print(0, 3, buf);
            ESP_LOGI(TAG, "%s", buf);
        }
        if (get_flipped_down_switch(&switch_)) {
            sprintf(buf, "Switch Down: %d     ", switch_);
            lcd_print(0, 3, buf);
            ESP_LOGI(TAG, "%s", buf);
        }
        if (get_flipped_up_switch(&switch_)) {
            sprintf(buf, "Switch Up: %d       ", switch_);
            lcd_print(0, 3, buf);
            ESP_LOGI(TAG, "%s", buf);
        }

        uint8_t new_switch_state = get_switch_state();
        if (new_switch_state != switch_state) {
            switch_state = new_switch_state;
            print_4bin_rev(bin_buf, switch_state);
            sprintf(buf, "s: %s", bin_buf);
            lcd_print(1, 0, buf);
            ESP_LOGI(TAG, "%s", buf);
        }

        uint8_t new_button_state = get_button_state();
        if (new_button_state != button_state) {
            button_state = new_button_state;
            print_4bin_rev(bin_buf, button_state);
            sprintf(buf, "b: %s", bin_buf);
            lcd_print(1, 1, buf);
            ESP_LOGI(TAG, "%s", buf);
        }

        if (get_pressed_keypad(&key) && key == KeypadKey::pound) {
            return;
        }

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

