// 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 <SPIFFS.h>
#include <USB.h>
#include <I2S_Audio.h>
#include <Switches.h>
#include <BLE.h>
#include <IR.h>
#include <WiFi.h>
#include "nvs_flash.h"
#include "HW_NeoPixels.h"
#include "Version.h"
#include "Reprogramming.h"

static const char *TAG = "KTag 2024A";
static SemaphoreHandle_t init_complete_semaphore;
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...");
    init_complete_semaphore = xSemaphoreCreateBinary();

    // 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();
    }
    ESP_ERROR_CHECK(ret);

    Initialize_SPIFFS(init_complete_semaphore);
    if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
    {
        KLOG_ERROR(TAG, "Timeout initializing SPIFFS!");
    }

    Initialize_USB(init_complete_semaphore);
    if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
    {
        KLOG_ERROR(TAG, "Timeout initializing USB!");
    }

    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();

        Initialize_SystemK_NeoPixels(init_complete_semaphore);
        if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
        {
            KLOG_ERROR(TAG, "Timeout initializing NeoPixels!");
        }

        Initialize_BLE();

        Initialize_IR(init_complete_semaphore);
        if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)
        {
            KLOG_ERROR(TAG, "Timeout initializing IR!");
        }
        if (Initialize_SystemK() != SYSTEMK_RESULT_SUCCESS)
        {
            KLOG_ERROR(TAG, "Error initializing SystemK!");
        }

        // Initialize the switches after SystemK, so xQueueEvents will have already been created.
        Initialize_Switches();

        vSemaphoreDelete(init_complete_semaphore);
        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;
}
