/*
 * File: UI_Control.c
 *
 * Created: Created: Friday August 2025 10:21:23
 *   Author: Chris
 */
#include "UI_Control.h"

#include "EEPROM_M24C64.h"

#include <string.h>


// ============================================================================================
// Includes


// ============================================================================================
// Defines
#define SELECTOR_INC(__SELECTOR__, __STEP__, __MIN__, __MAX__, __CIRCLE__)    if((*__SELECTOR__) <= (__MAX__ - __STEP__)) { (*__SELECTOR__)+=__STEP__; } else if(__CIRCLE__) { (*__SELECTOR__) = __MIN__; break; } else { break; }
#define SELECTOR_DEC(__SELECTOR__, __STEP__, __MIN__, __MAX__, __CIRCLE__)    if((*__SELECTOR__) >= (__MIN__ + __STEP__)) { (*__SELECTOR__)-=__STEP__; } else if(__CIRCLE__) { (*__SELECTOR__) = __MAX__; break; } else { break; }


// ============================================================================================
// Datatypes
typedef struct {
    // Configuration
    const Encoder_Acceleration_Config* config;
    
    // State tracking
    uint8_t Speed_Counter;             // Current speed counter
    uint8_t Acceleration_Level;        // Current acceleration level (0 = no accel)
    uint32_t Last_Activity_Time;       // Last encoder activity timestamp (ms)
    
    // Statistics (for debugging)
    uint32_t Total_Steps;              // Total encoder steps processed
    uint32_t Accelerated_Steps;        // Steps that used acceleration
} Encoder_Acceleration_Instance;


// ============================================================================================
// Variables

// Default configuration
static const Encoder_Acceleration_Config _Default_Acceleration_Config = {
    .Base_Step      = 1,
    .Base_Threshold = 5,        // Requires more steps to trigger
    .Level_Step     = 3,        // Bigger jumps between levels
    .Max_Level      = 3,        // Fewer levels
    .Timeout_ms     = 100,      // Longer timeout
    .Multipliers    = {1, 2, 3, 5, 7, 10, 13, 16, 20, 25}
};


static Encoder_Acceleration_Instance _Acceleration_Instance;
static bool _Acceleration_Initialized = false;
static bool _Acceleration_Enabled = false;
static uint32_t _Current_Time_MS = 0;


// ============================================================================================
// Function Declarations
uint8_t UI_Control_Acceleration_Process_Step(uint32_t current_time_ms);


/*******************************************************************
    Functions
*******************************************************************/
void UI_Control_Init(void)
{
    UI_Control_Acceleration_Init_Custom(&_Default_Acceleration_Config);
}

void UI_Control_Selector_Inc(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around)
{
	if (selector == NULL) {
        return;
    }

    _Current_Time_MS = CURRENT_TIME_ms;

    // Get acceleration multiplier (1 if not initialized or disabled)
    uint8_t Increment = UI_Control_Acceleration_Process_Step(_Current_Time_MS);
    
    // Apply increment with bounds checking
    for (uint8_t i = 0; i < Increment; i++)
    {
        if(_EEPROM_Content.Device_Configuration.Reverse_List_Scrolling == 0) {
            SELECTOR_INC(selector, _Acceleration_Instance.config->Base_Step, minimum, maximum, circle_around);
        } else {
            SELECTOR_DEC(selector, _Acceleration_Instance.config->Base_Step, minimum, maximum, circle_around);
        }
    }
}

void UI_Control_Selector_Dec(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around)
{
	if (selector == NULL) {
        return;
    }
    
    _Current_Time_MS = CURRENT_TIME_ms;

    // Get acceleration multiplier (1 if not initialized or disabled)
    uint8_t Decrement = UI_Control_Acceleration_Process_Step(_Current_Time_MS);
    
    // Apply decrement with bounds checking
    for (uint8_t i = 0; i < Decrement; i++)
    {
        if(_EEPROM_Content.Device_Configuration.Reverse_List_Scrolling == 0) {
            SELECTOR_DEC(selector, _Acceleration_Instance.config->Base_Step, minimum, maximum, circle_around);
        } else {
            SELECTOR_INC(selector, _Acceleration_Instance.config->Base_Step, minimum, maximum, circle_around);
        }
    }
}

void UI_Control_Acceleration_Init_Custom(const Encoder_Acceleration_Config* config)
{
    if (config == NULL) {
        config = &_Default_Acceleration_Config;
    }
    
    // Clear the instance
    memset(&_Acceleration_Instance, 0, sizeof(Encoder_Acceleration_Instance));
    
    // Set configuration
    _Acceleration_Instance.config = config;
    
    // Initialize state
    _Acceleration_Instance.Speed_Counter = 0;
    _Acceleration_Instance.Acceleration_Level = 0;
    _Acceleration_Instance.Last_Activity_Time = 0;
    _Acceleration_Instance.Total_Steps = 0;
    _Acceleration_Instance.Accelerated_Steps = 0;
    
    _Acceleration_Initialized = true;
    _Acceleration_Enabled = false;
}

void UI_Control_Acceleration_Set_Enabled(bool enable)
{
    _Acceleration_Enabled = enable;
    
    // Reset state when disabling
    if (!enable && _Acceleration_Initialized) {
        UI_Control_Acceleration_Reset();
    }
}

void UI_Control_Acceleration_Reset(void)
{
    if (!_Acceleration_Initialized) {
        return;
    }
    
    _Acceleration_Instance.Speed_Counter = 0;
    _Acceleration_Instance.Acceleration_Level = 0;
    // Don't reset timestamp - let natural timeout handle it
}

void UI_Control_Set_Current_Time(uint32_t current_time_ms)
{
    _Current_Time_MS = current_time_ms;
}

void UI_Control_Selector_Inc_Accelerated(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around, uint32_t current_time_ms)
{
    // This function is now redundant - just call the main function with time set
    UI_Control_Set_Current_Time(current_time_ms);
    UI_Control_Selector_Inc(selector, minimum, maximum, circle_around);
}

void UI_Control_Selector_Dec_Accelerated(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around, uint32_t current_time_ms)
{
    // This function is now redundant - just call the main function with time set
    UI_Control_Set_Current_Time(current_time_ms);
    UI_Control_Selector_Dec(selector, minimum, maximum, circle_around);
}

void UI_Control_Acceleration_Update(uint32_t current_time_ms)
{
    if (!_Acceleration_Initialized || _Acceleration_Instance.config == NULL) {
        return;
    }

    // Check for timeout
    if (_Acceleration_Instance.Last_Activity_Time > 0)
    {
        uint32_t time_since_activity = current_time_ms - _Acceleration_Instance.Last_Activity_Time;
        
        if (time_since_activity >= _Acceleration_Instance.config->Timeout_ms) {
            // Reset acceleration due to timeout
            _Acceleration_Instance.Speed_Counter = 0;
            _Acceleration_Instance.Acceleration_Level = 0;
            // Keep last_activity_time for future timeout calculations
        }
    }
}

uint8_t UI_Control_Acceleration_Get_Level(void)
{
    if (!_Acceleration_Initialized) {
        return 0;
    }

    return _Acceleration_Instance.Acceleration_Level;
}

bool UI_Control_Acceleration_Is_Active(void)
{
    if (!_Acceleration_Initialized || !_Acceleration_Enabled) {
        return false;
    }

    return (_Acceleration_Instance.Acceleration_Level > 0);
}

void UI_Control_Acceleration_Get_Stats(uint32_t* total_steps, uint32_t* accelerated_steps)
{
    if (!_Acceleration_Initialized)
    {
        if (total_steps) *total_steps = 0;
        if (accelerated_steps) *accelerated_steps = 0;
        return;
    }
    
    if (total_steps) {
        *total_steps = _Acceleration_Instance.Total_Steps;
    }

    if (accelerated_steps) {
        *accelerated_steps = _Acceleration_Instance.Accelerated_Steps;
    }
}


/*******************************************************************
    Internal Functions
*******************************************************************/
uint8_t UI_Control_Acceleration_Process_Step(uint32_t current_time_ms)
{
    if (!_Acceleration_Initialized || !_Acceleration_Enabled || _Acceleration_Instance.config == NULL) {
        return 1; // Fallback to normal increment
    }
    
    // Use provided time or stored time
    uint32_t Time_To_Use = (current_time_ms > 0) ? current_time_ms : _Current_Time_MS;
    
    const Encoder_Acceleration_Config* Config = _Acceleration_Instance.config;
    
    // Update activity timestamp
    _Acceleration_Instance.Last_Activity_Time = Time_To_Use;
    
    // Increment speed counter
    _Acceleration_Instance.Speed_Counter++;
    _Acceleration_Instance.Total_Steps++;
    
    // Calculate current acceleration level
    if (_Acceleration_Instance.Speed_Counter >= Config->Base_Threshold) {
        // Calculate level based on how many steps beyond threshold
        uint8_t excess_steps = _Acceleration_Instance.Speed_Counter - Config->Base_Threshold;
        _Acceleration_Instance.Acceleration_Level = 1 + (excess_steps / Config->Level_Step);
        
        // Clamp to maximum level
        if (_Acceleration_Instance.Acceleration_Level > Config->Max_Level) {
            _Acceleration_Instance.Acceleration_Level = Config->Max_Level;
        }
    } else {
        _Acceleration_Instance.Acceleration_Level = 0;
    }
    
    // Get multiplier for current level
    uint8_t increment = 1;
    if (_Acceleration_Instance.Acceleration_Level > 0) {
        // Array is 0-indexed, but level 1 = index 1, level 0 = index 0
        increment = Config->Multipliers[_Acceleration_Instance.Acceleration_Level];
        _Acceleration_Instance.Accelerated_Steps++;
    }
    
    return increment;
}
