Newer
Older
ESP32-RetroPlay / main / main.c
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h" // Needed for xQueueCreate
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_log.h" // For ESP_LOG macros

// Include our modular task headers
#include "tasks/button_led_comm.h" // Keep this for LED queue
#include "tasks/button_task.h"     // Now expects a game input queue handle
#include "tasks/led_task.h"        // Keep this for LED task
#include "tasks/display_task.h"    // Generic display task
#include "tasks/game/jump_bird_game.h" // Jump Bird game instance header
#include "tasks/game/arkanoid_game.h" // Arkanoid game instance header
#include "tasks/game/game_menu.h"     // New: Game menu header

static const char *TAG_MAIN = "APP_MAIN";

// Define the global LED control queue here (its definition)
QueueHandle_t xLedControlQueue; // Keep this definition

/**
 * @brief Main application entry point.
 * Initializes the system, prints chip information, and starts the necessary tasks.
 */
void app_main(void)
{
    // --- 1. Create the LED control queue ---
    xLedControlQueue = xQueueCreate(1, sizeof(bool));
    if (xLedControlQueue == NULL)
    {
        ESP_LOGE(TAG_MAIN, "Failed to create LED control queue! Halting.");
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "LED control queue created successfully.");

    // --- 2. Start the LED control task ---
    ESP_LOGI(TAG_MAIN, "Attempting to start LED control task...");
    if (start_led_control_task() != ESP_OK)
    {
        ESP_LOGE(TAG_MAIN, "Failed to start LED control task! Halting.");
        vQueueDelete(xLedControlQueue);
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "LED control task created successfully.");

    // --- 3. Create a generic button input queue for the menu and games ---
    // This queue will be used by button_task to send input, and by menu/game to receive.
    QueueHandle_t button_input_queue = xQueueCreate(1, sizeof(int)); // Size of int for -1, 0, 1 signals
    if (button_input_queue == NULL) {
        ESP_LOGE(TAG_MAIN, "Failed to create generic button input queue! Halting.");
        vQueueDelete(xLedControlQueue);
        while (1) { vTaskDelay(100); }
    }
    ESP_LOGI(TAG_MAIN, "Generic button input queue created successfully.");

    // --- 4. Start the button monitor task ---
    // This task will send input signals to the generic button_input_queue
    ESP_LOGI(TAG_MAIN, "Attempting to start button monitor task...");
    if (start_button_monitor_task(button_input_queue, xLedControlQueue) != ESP_OK)
    {
        ESP_LOGE(TAG_MAIN, "Failed to start button monitor task! Halting.");
        vQueueDelete(xLedControlQueue);
        vQueueDelete(button_input_queue);
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "Button Monitor task created successfully.");

    // --- 5. Start the display task early, so it can display the menu ---
    ESP_LOGI(TAG_MAIN, "Starting display task for menu display...");
    // Pass the button_input_queue to the display task so it can handle game over screen input
    if (start_display_task(button_input_queue) != ESP_OK)
    {
        ESP_LOGE(TAG_MAIN, "Failed to start display task! Halting.");
        vQueueDelete(xLedControlQueue);
        vQueueDelete(button_input_queue);
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "Display task created successfully, ready for menu.");


    // --- 6. Define the menu items ---
    const menu_item_t game_menu_items[] = {
        {.name = "JUMP BIRD", .game_instance = &JumpBirdGame},
        {.name = "ARKANOID",  .game_instance = &ArkanoidGame},
    };
    const size_t num_menu_items = sizeof(game_menu_items) / sizeof(game_menu_items[0]);

    // --- Main application loop to manage game and menu transitions ---
    while (true) {
        // --- 7. Run the game selection menu ---
        ESP_LOGI(TAG_MAIN, "Running game selection menu...");
        const menu_item_t *selected_menu_item = run_game_menu(game_menu_items, num_menu_items, button_input_queue);

        if (selected_menu_item == NULL) {
            ESP_LOGE(TAG_MAIN, "No game selected from menu! This should not happen. Restarting menu.");
            continue; // Go back to the start of the loop
        }
        ESP_LOGI(TAG_MAIN, "Game selected from menu: %s", selected_menu_item->name);

        // --- 8. Tell the display task to switch to the selected game ---
        ESP_LOGI(TAG_MAIN, "Setting display to active game: %s...", selected_menu_item->name);
        display_set_game_active(selected_menu_item->game_instance); // Use the new function

        // --- 9. Wait for the game to finish and signal return to menu ---
        ESP_LOGI(TAG_MAIN, "Waiting for game to finish...");
        display_event_t event_from_display;
        // Block until display_task signals a return to menu
        if (xQueueReceive(display_event_queue, &event_from_display, portMAX_DELAY) == pdTRUE) {
            if (event_from_display == DISPLAY_EVENT_RETURN_TO_MENU) {
                ESP_LOGI(TAG_MAIN, "Received signal to return to menu. Looping back.");
                // Loop will restart, showing the menu again
            } else {
                ESP_LOGW(TAG_MAIN, "Received unknown event from display queue: %d", event_from_display);
            }
        } else {
            ESP_LOGE(TAG_MAIN, "Failed to receive event from display queue. This should not happen.");
            // Consider error handling or reset here
        }
        // Loop will now continue to the next iteration, re-running the menu
    }
}