/*
 * This program source code file is part of the KTag project.
 *
 * 🛡 <https://ktag.clubk.club> 🃞
 *
 * Copyright © 2024-2026 Joseph P. Kearney and the KTag developers.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 * details.
 *
 * There should be a copy of the GNU Affero General Public License in the LICENSE
 * file in the root of this repository.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <SystemK.h>
#include <System_Events.h>
#include <NVM.h>

static const char *TAG = "Settings";

// Cache variable declarations
// 1 if this device is configured for a right-handed player, 0 otherwise.
static uint8_t Cached_Is_Right_Handed = 1;
// Value from CONFIG_KTAG_MIN_AUDIO_VOLUME to CONFIG_KTAG_MAX_AUDIO_VOLUME representing the current volume.
static uint8_t Cached_Audio_Volume = CONFIG_KTAG_MAX_AUDIO_VOLUME;
// Selected team.
static uint8_t Cached_Team_ID = BASIC_TEAMS_MINIMUM;
// Unique-per-team identification of a player.
static uint8_t Cached_Player_ID = 0;
// Selected weapon.
static uint8_t Cached_Weapon_ID = DUBUQUE_PROTOCOL_ID;
// Maximum health for the game.
static uint8_t Cached_Max_Health = 100;
// Number of special weapons (currently only bombs) obtained when reentering after being tagged out.
static uint8_t Cached_N_Special_Weapons_On_Reentry = 1;
// Time (in milliseconds) after starting a game before the countdown begins.
static uint32_t Cached_T_Start_Game_in_ms = 30000;
// Duration of a game (in milliseconds). If this is zero or UINT32_MAX, the game is untimed.
static uint32_t Cached_T_Game_Length_in_ms = 600000;
// Color in addition to the team color used to identify a player's device. The format is Brightness-Red-Green-Blue, where a brightness of 0xFF indicates a false flag.
static uint32_t Cached_Secondary_Color = 0xFE000000;


SystemKResult_T SETTINGS_get_uint8_t(SystemKSettingID_T id, uint8_t *value)
{
    SystemKResult_T result = SYSTEMK_RESULT_UNSPECIFIED_FAILURE;

    switch (id)
    {
    case SYSTEMK_SETTING_IS_RIGHT_HANDED:
        *value = Cached_Is_Right_Handed;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_AUDIO_VOLUME:
        *value = Cached_Audio_Volume;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_TEAMID:
        *value = Cached_Team_ID;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_PLAYERID:
        *value = Cached_Player_ID;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_WEAPONID:
        *value = Cached_Weapon_ID;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_MAX_HEALTH:
        *value = Cached_Max_Health;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_N_SPECIAL_WEAPONS_ON_REENTRY:
        *value = Cached_N_Special_Weapons_On_Reentry;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    default:
        result = SYSTEMK_RESULT_WRONG_DATATYPE;
        break;
    }

    return result;
}


SystemKResult_T SETTINGS_set_uint8_t(SystemKSettingID_T id, uint8_t value)
{
    SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;

    switch (id)
    {
    case SYSTEMK_SETTING_IS_RIGHT_HANDED:
        Cached_Is_Right_Handed = value;
        result = KV_Set_Value_uint8(CONFIG_FILE, "Is_Right_Handed", &value);
        break;

    case SYSTEMK_SETTING_AUDIO_VOLUME:
        Cached_Audio_Volume = value;
        result = KV_Set_Value_uint8(CONFIG_FILE, "Audio_Volume", &value);
        break;

    case SYSTEMK_SETTING_TEAMID:
        Cached_Team_ID = value;
        result = KV_Set_Value_uint8(CONFIG_FILE, "Team_ID", &value);
        break;

    case SYSTEMK_SETTING_PLAYERID:
        Cached_Player_ID = value;
        result = KV_Set_Value_uint8(CONFIG_FILE, "Player_ID", &value);
        break;

    case SYSTEMK_SETTING_WEAPONID:
        Cached_Weapon_ID = value;
        result = KV_Set_Value_uint8(CONFIG_FILE, "Weapon_ID", &value);
        break;

    case SYSTEMK_SETTING_MAX_HEALTH:
        Cached_Max_Health = value;
        result = KV_Set_Value_uint8(CONFIG_FILE, "Max_Health", &value);
        break;

    case SYSTEMK_SETTING_N_SPECIAL_WEAPONS_ON_REENTRY:
        Cached_N_Special_Weapons_On_Reentry = value;
        result = KV_Set_Value_uint8(CONFIG_FILE, "N_Special_Weapons_On_Reentry", &value);
        break;

    default:
        result = SYSTEMK_RESULT_WRONG_DATATYPE;
        break;
    }

    return result;
}


SystemKResult_T SETTINGS_get_uint32_t(SystemKSettingID_T id, uint32_t *value)
{
    SystemKResult_T result = SYSTEMK_RESULT_UNSPECIFIED_FAILURE;

    switch (id)
    {
    case SYSTEMK_SETTING_T_START_GAME_in_ms:
        *value = Cached_T_Start_Game_in_ms;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_T_GAME_LENGTH_in_ms:
        *value = Cached_T_Game_Length_in_ms;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_SECONDARY_COLOR:
        *value = Cached_Secondary_Color;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    case SYSTEMK_SETTING_DEVICE_TYPE:
        *value = BLE_DEVICE_TYPE_32ESPECIAL;
        result = SYSTEMK_RESULT_SUCCESS;
        break;

    default:
        result = SYSTEMK_RESULT_WRONG_DATATYPE;
        break;
    }

    return result;
}

SystemKResult_T SETTINGS_set_uint32_t(SystemKSettingID_T id, uint32_t value)
{
    SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;

    switch (id)
    {
    case SYSTEMK_SETTING_T_START_GAME_in_ms:
        Cached_T_Start_Game_in_ms = value;
        result = KV_Set_Value_uint32(CONFIG_FILE, "T_Start_Game_in_ms", &value);
        break;

    case SYSTEMK_SETTING_T_GAME_LENGTH_in_ms:
        Cached_T_Game_Length_in_ms = value;
        result = KV_Set_Value_uint32(CONFIG_FILE, "T_Game_Length_in_ms", &value);
        break;

    case SYSTEMK_SETTING_SECONDARY_COLOR:
        Cached_Secondary_Color = value;
        result = KV_Set_Value_uint32(CONFIG_FILE, "Secondary_Color", &value);
        break;

    default:
        result = SYSTEMK_RESULT_WRONG_DATATYPE;
        break;
    }

    return result;
}

SystemKResult_T SETTINGS_get_device_name(char *name)
{
    char buffer[KV_MAX_VALUE_LENGTH];
    SystemKResult_T result = KV_Get_Value_string(CONFIG_FILE, "Device_Name", buffer);

    if (result == SYSTEMK_RESULT_SUCCESS)
    {
        memcpy(name, buffer, SYSTEMK_MAX_CHARS_IN_DEVICE_NAME);
        buffer[SYSTEMK_MAX_CHARS_IN_DEVICE_NAME] = 0x00;
    }

    return result;
}

SystemKResult_T SETTINGS_set_device_name(char *name)
{
    SystemKResult_T result = KV_Set_Value_string(CONFIG_FILE, "Device_Name", name);
    return result;
}

// Settings are saved on change in this implementation.
SystemKResult_T SETTINGS_Save(void)
{
    return SYSTEMK_RESULT_SUCCESS;
}


SystemKResult_T Initialize_Settings(void)
{
    SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;
    SystemKResult_T temp_result;
    uint32_t value_uint32_t;
    uint8_t value_uint8_t;

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

    // Initialize Is_Right_Handed
    temp_result = KV_Get_Value_uint8(CONFIG_FILE, "Is_Right_Handed", &value_uint8_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint8(DEFAULT_CONFIG_FILE, "Is_Right_Handed", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint8_t = 1;
            KLOG_WARN(TAG, "Is_Right_Handed not found in config files; using default value of 1.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint8(CONFIG_FILE, "Is_Right_Handed", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set Is_Right_Handed in config file.");
        }
    }
    Cached_Is_Right_Handed = value_uint8_t;

    // Initialize Audio_Volume
    temp_result = KV_Get_Value_uint8(CONFIG_FILE, "Audio_Volume", &value_uint8_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint8(DEFAULT_CONFIG_FILE, "Audio_Volume", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint8_t = CONFIG_KTAG_MAX_AUDIO_VOLUME;
            KLOG_WARN(TAG, "Audio_Volume not found in config files; using default value of CONFIG_KTAG_MAX_AUDIO_VOLUME.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint8(CONFIG_FILE, "Audio_Volume", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set Audio_Volume in config file.");
        }
    }
    Cached_Audio_Volume = value_uint8_t;

    // Initialize Team_ID
    temp_result = KV_Get_Value_uint8(CONFIG_FILE, "Team_ID", &value_uint8_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint8(DEFAULT_CONFIG_FILE, "Team_ID", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint8_t = BASIC_TEAMS_MINIMUM;
            KLOG_WARN(TAG, "Team_ID not found in config files; using default value of BASIC_TEAMS_MINIMUM.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint8(CONFIG_FILE, "Team_ID", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set Team_ID in config file.");
        }
    }
    Cached_Team_ID = value_uint8_t;

    // Initialize Player_ID
    temp_result = KV_Get_Value_uint8(CONFIG_FILE, "Player_ID", &value_uint8_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint8(DEFAULT_CONFIG_FILE, "Player_ID", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint8_t = 0;
            KLOG_WARN(TAG, "Player_ID not found in config files; using default value of 0.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint8(CONFIG_FILE, "Player_ID", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set Player_ID in config file.");
        }
    }
    Cached_Player_ID = value_uint8_t;

    // Initialize Weapon_ID
    temp_result = KV_Get_Value_uint8(CONFIG_FILE, "Weapon_ID", &value_uint8_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint8(DEFAULT_CONFIG_FILE, "Weapon_ID", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint8_t = DUBUQUE_PROTOCOL_ID;
            KLOG_WARN(TAG, "Weapon_ID not found in config files; using default value of DUBUQUE_PROTOCOL_ID.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint8(CONFIG_FILE, "Weapon_ID", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set Weapon_ID in config file.");
        }
    }
    Cached_Weapon_ID = value_uint8_t;

    // Initialize Max_Health
    temp_result = KV_Get_Value_uint8(CONFIG_FILE, "Max_Health", &value_uint8_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint8(DEFAULT_CONFIG_FILE, "Max_Health", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint8_t = 100;
            KLOG_WARN(TAG, "Max_Health not found in config files; using default value of 100.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint8(CONFIG_FILE, "Max_Health", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set Max_Health in config file.");
        }
    }
    Cached_Max_Health = value_uint8_t;

    // Initialize N_Special_Weapons_On_Reentry
    temp_result = KV_Get_Value_uint8(CONFIG_FILE, "N_Special_Weapons_On_Reentry", &value_uint8_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint8(DEFAULT_CONFIG_FILE, "N_Special_Weapons_On_Reentry", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint8_t = 1;
            KLOG_WARN(TAG, "N_Special_Weapons_On_Reentry not found in config files; using default value of 1.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint8(CONFIG_FILE, "N_Special_Weapons_On_Reentry", &value_uint8_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set N_Special_Weapons_On_Reentry in config file.");
        }
    }
    Cached_N_Special_Weapons_On_Reentry = value_uint8_t;

    // Initialize T_Start_Game_in_ms
    temp_result = KV_Get_Value_uint32(CONFIG_FILE, "T_Start_Game_in_ms", &value_uint32_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint32(DEFAULT_CONFIG_FILE, "T_Start_Game_in_ms", &value_uint32_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint32_t = 30000;
            KLOG_WARN(TAG, "T_Start_Game_in_ms not found in config files; using default value of 30000.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint32(CONFIG_FILE, "T_Start_Game_in_ms", &value_uint32_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set T_Start_Game_in_ms in config file.");
        }
    }
    Cached_T_Start_Game_in_ms = value_uint32_t;

    // Initialize T_Game_Length_in_ms
    temp_result = KV_Get_Value_uint32(CONFIG_FILE, "T_Game_Length_in_ms", &value_uint32_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint32(DEFAULT_CONFIG_FILE, "T_Game_Length_in_ms", &value_uint32_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint32_t = 600000;
            KLOG_WARN(TAG, "T_Game_Length_in_ms not found in config files; using default value of 600000.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint32(CONFIG_FILE, "T_Game_Length_in_ms", &value_uint32_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set T_Game_Length_in_ms in config file.");
        }
    }
    Cached_T_Game_Length_in_ms = value_uint32_t;

    // Initialize Secondary_Color
    temp_result = KV_Get_Value_uint32(CONFIG_FILE, "Secondary_Color", &value_uint32_t);
    if (temp_result != SYSTEMK_RESULT_SUCCESS)
    {
        // Try to get from default config
        temp_result = KV_Get_Value_uint32(DEFAULT_CONFIG_FILE, "Secondary_Color", &value_uint32_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            // Use hardcoded default
            value_uint32_t = 0xFE000000;
            KLOG_WARN(TAG, "Secondary_Color not found in config files; using default value of 0xFE000000.");
        }
        // Save to config file
        temp_result = KV_Set_Value_uint32(CONFIG_FILE, "Secondary_Color", &value_uint32_t);
        if (temp_result != SYSTEMK_RESULT_SUCCESS)
        {
            result = temp_result;
            KLOG_ERROR(TAG, "Failed to set Secondary_Color in config file.");
        }
    }
    Cached_Secondary_Color = value_uint32_t;

    xEventGroupSetBits(Get_System_Events(), SYS_SETTINGS_READY);
    KLOG_INFO(TAG, "Settings initialized.");

    return result;
}
