// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKky+.`/ykKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKds/.  -+o:` ./sdNKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKNds+-` `-+hNKKKKNho:` `-+shNKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKNkhyo+:.  `-/sdNKKKKKKKKKKKKKky+:`  .-/oyhdNKKKKKKKKKK
// KKys++:-.````.-:+oykNKKKKKKKKKKKKKKKKKKKKKKNkhs+/-.````.-:/+syKK
// KK  -/+osydkNNNKKKkkkkkkkNKKKKKKKKKKKkkkkkkkkNKKKKNNkdhyso/:  KK
// KK  sKKKKKKKKKKKKK```````/KKKKKKKKKd-```````:kKKKKKKKKKKKKKd `KK
// KK- oKKKKKKKKKKKKK       :KKKKKKKKo`      `oNKKKKKKKKKKKKKKh :KK
// KK/ +KKKKKKKKKKKKK       :KKKKKKd-       -dKKKKKKKKKKKKKKKKy /KK
// KK+ /KKKKKKKKKKKKK       :KKKKKs`       +NKKKKKKKKKKKKKKKKKs +KK
// KKo :KKKKKKKKKKKKK       :KKKk:       .hKKKKKKKKKKKKKKKKKKKo oKK
// KKy -KKKKKKKKKKKKK       :KKy`       +NKKKKKKKKKKKKKKKKKKKK/ yKK
// KKd `KKKKKKKKKKKKK       :k/       .hKKKKKKKKKKKKKKKKKKKKKK: dKK
// KKN  NKKKKKKKKKKKK       ..       /kKKKKKKKKKKKKKKKKKKKKKKK. NKK
// KKK. dKKKKKKKKKKKK              .yKKKKKKKKKKKKKKKKKKKKKKKKN .KKK
// KKK+ oKKKKKKKKKKKK             -kKKKKKKKKKKKKKKKKKKKKKKKKKh +KKK
// KKKd .KKKKKKKKKKKK             `sNKKKKKKKKKKKKKKKKKKKKKKKK/ dKKK
// KKKK: hKKKKKKKKKKK               :kKKKKKKKKKKKKKKKKKKKKKKk :KKKK
// KKKKh -KKKKKKKKKKK       ``       .yKKKKKKKKKKKKKKKKKKKKK+ hKKKK
// KKKKK/ yKKKKKKKKKK   T   :d:        /kKKKKKKKKKKKKKKKKKKk`:KKKKK
// KKKKKk`.NKKKKKKKKK       :KNo`       .hKKKKKKKKKKKKKKKKK:`kKKKKK
// KKKKKKy /KKKKKKKKK   A   :KKKd-        +NKKKKKKKKKKKKKKo yKKKKKK
// KKKKKKK+ oKKKKKKKK       :KKKKN+`       -hKKKKKKKKKKKKy`+KKKKKKK
// KKKKKKKN/ sKKKKKKK   G   :KKKKKKh.       `oNKKKKKKKKKh`/KKKKKKKK
// KKKKKKKKN/`sKKKKKK       :KKKKKKKN/        -dKKKKKKKh`/NKKKKKKKK
// KKKKKKKKKK+ +NKKKK       :KKKKKKKKKy.       `sNKKKKs`+KKKKKKKKKK
// KKKKKKKKKKKs`:kKKK-------+KKKKKKKKKKk/--------oKKN+`sKKKKKKKKKKK
// KKKKKKKKKKKKh..yKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKd--dKKKKKKKKKKKK
// KKKKKKKKKKKKKN+`/kKKKKKKKKKKKKKKKKKKKKKKKKKKKKNo`+NKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKh-`sNKKKKKKKKKKKKKKKKKKKKKKKKNy.-hKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKs..sNKKKKKKKKKKKKKKKKKKKKNy-.yKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKNs..okKKKKKKKKKKKKKKKKNs-.sNKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKy-`/hKKKKKKKKKKKKd+`-yKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKd/`.odKKKKKKks-`/dKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKNs: .+yy+-`:sNKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKNy/..+yNKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
// KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK

#include <string.h>
#include <SystemK.h>
#include <System_Events.h>
#include <NVM.h>
#include <I2S_Audio.h>
#include <Switches.h>
#include <BLE.h>
#include <IR.h>
#include <WiFi.h>
#include <Console.h>
#include "nvs_flash.h"
#include "HW_NeoPixels.h"
#include "Version.h"
#include "Reprogramming.h"

static const char *TAG = "KTag 2024A";
static const uint16_t INITIALIZATION_TIMEOUT_IN_ms = 10 * 1000;

void app_main(void)
{
    KLOG_INFO(TAG, VERSION_AS_STR());

    KLOG_INFO(TAG, "Initializing app...");

    Initialize_System_Events();

    // Initialize NVS — it is used by both the BLE and WiFi drivers.
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    if (ret == ESP_OK)
    {
        xEventGroupSetBits(Get_System_Events(), SYS_NVS_READY);
        KLOG_INFO(TAG, "NVS initialized.");
    }
    else
    {
        KLOG_ERROR(TAG, "Error initializing NVS: %s", esp_err_to_name(ret));
    }

    Initialize_Console();

    Initialize_SPIFFS();
    Wait_For_System_Event(SYS_SPIFFS_READY, "Timeout initializing SPIFFS!", INITIALIZATION_TIMEOUT_IN_ms);

    Initialize_USB();
    Wait_For_System_Event(SYS_USB_FS_PRESENT, "Timeout initializing USB!", INITIALIZATION_TIMEOUT_IN_ms);

    Initialize_Settings();
    Wait_For_System_Event(SYS_SETTINGS_READY, "Timeout initializing Settings!", INITIALIZATION_TIMEOUT_IN_ms);

    if (OTA_File_Exists() == true)
    {
        KLOG_INFO(TAG, "Attempting OTA reprogramming from %s.", Get_OTA_Image_URL());
        KLOG_WARN(TAG, "This does not work reliably...use USB.");
        Initialize_WiFi();
    }
    else
    {
        Initialize_Audio();
        Wait_For_System_Event(SYS_AUDIO_READY, "Timeout initializing Audio!", INITIALIZATION_TIMEOUT_IN_ms);

        Initialize_SystemK_NeoPixels();
        Wait_For_System_Event(SYS_NEOPIXELS_READY, "Timeout initializing NeoPixels!", INITIALIZATION_TIMEOUT_IN_ms);

        Initialize_BLE();
        Wait_For_System_Event(SYS_BLE_READY, "Timeout initializing BLE!", INITIALIZATION_TIMEOUT_IN_ms);

        Initialize_IR();
        Wait_For_System_Event(SYS_IR_READY, "Timeout initializing IR!", INITIALIZATION_TIMEOUT_IN_ms);

        if (Initialize_SystemK() != SYSTEMK_RESULT_SUCCESS)
        {
            KLOG_ERROR(TAG, "Error initializing SystemK!");
        }

        // Initialize the switches after SystemK, so xQueueEvents will have already been created.
        if (Initialize_Switches() != ESP_OK)
        {
            KLOG_ERROR(TAG, "Error initializing the switches!");
        }

        KLOG_INFO(TAG, "Initialization complete.");
    }

    while (true)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

SystemKResult_T HW_Execute_Console_Command(const uint8_t *const command)
{
    SystemKResult_T result = SYSTEMK_RESULT_NOT_IMPLEMENTED;

    if (strncmp((const char *)command, "reprogram", 9) == 0)
    {
        Request_Reprogramming_From_USB();
        result = SYSTEMK_RESULT_SUCCESS;
    }

    return result;
}
