class MainMenu;

class MiniOverlay : public tsl::Gui {
private:
    char GPU_Load_c[32] = "";
    char Rotation_SpeedLevel_c[64] = "";
    char RAM_var_compressed_c[128] = "";
    char Battery_c[64] = "";
    char soc_temperature_c[64] = "";
    char skin_temperature_c[64] = "";

    uint32_t rectangleWidth;
    char Variables[512];
    size_t fontsize;
    MiniSettings settings;
    bool Initialized = false;
    ApmPerformanceMode performanceMode = ApmPerformanceMode_Invalid;
    uint64_t systemtickfrequency_impl = systemtickfrequency;
    int frameOffsetX, frameOffsetY;
    int topPadding, bottomPadding;
    bool isDragging = false;

    bool skipOnce = true;
    bool runOnce = true;

    size_t framePadding = 10;
    static constexpr int screenWidth = 1280;
    static constexpr int screenHeight = 720;

    struct ButtonState {
        std::atomic<bool> minusDragActive{false};
        std::atomic<bool> plusDragActive{false};
    } buttonState;

    Thread touchPollThread;
    std::atomic<bool> touchPollRunning{false};
public:
    MiniOverlay() { 
        tsl::hlp::requestForeground(false);
        disableJumpTo = true;
        //tsl::initializeUltrahandSettings();
        PowerConsumption = 0.0f;
        batTimeEstimate = -1;
        strcpy(Battery_c, "-.-- W-.-% [--:--]"); // Default display

        GetConfigSettings(&settings);
        apmGetPerformanceMode(&performanceMode);
        if (performanceMode == ApmPerformanceMode_Normal) {
            fontsize = settings.handheldFontSize;
        }
        else fontsize = settings.dockedFontSize;

        if (settings.disableScreenshots) {
            tsl::gfx::Renderer::get().removeScreenshotStacks();
        }
        mutexInit(&mutex_BatteryChecker);
        mutexInit(&mutex_Misc);
        //alphabackground = 0x0;
        frameOffsetX = settings.frameOffsetX;
        frameOffsetY = settings.frameOffsetY;
        framePadding = settings.framePadding;
        topPadding = 5;
        bottomPadding = 2;

        if (ult::limitedMemory) {
            tsl::gfx::Renderer::get().setLayerPos(std::max(std::min((int)(frameOffsetX*1.5 + 0.5) - tsl::impl::currentUnderscanPixels.first, 1280-32 - tsl::impl::currentUnderscanPixels.first), 0), 0);
        }
        
        FullMode = false;
        TeslaFPS = settings.refreshRate;
        systemtickfrequency_impl /= settings.refreshRate;
        deactivateOriginalFooter = true;
        realVoltsPolling = settings.realVolts;
        StartThreads();

        // Get initial battery readings BEFORE starting threads
        if (R_SUCCEEDED(psmCheck) && R_SUCCEEDED(i2cCheck)) {
            uint16_t data = 0;
            float tempA = 0.0;
            
            // Get initial power consumption
            Max17050ReadReg(MAX17050_AvgCurrent, &data);
            tempA = (1.5625 / (max17050SenseResistor * max17050CGain)) * (s16)data;
            PowerConsumption = tempA * batVoltageAvg / 1000000.0; // Rough initial estimate
            
            // Get initial battery info
            psmGetBatteryChargeInfoFields(psmService, &_batteryChargeInfoFields);
            
            // Get initial time estimate
            if (tempA >= 0) {
                batTimeEstimate = -1;
            } else {
                Max17050ReadReg(MAX17050_TTE, &data);
                const float batteryTimeEstimateInMinutes = (5.625 * data) / 60;
                if (batteryTimeEstimateInMinutes > (99.0*60.0)+59.0) {
                    batTimeEstimate = (99*60)+59;
                } else {
                    batTimeEstimate = (int16_t)batteryTimeEstimateInMinutes;
                }
            }
        } else {
            // Fallback if checks failed
            PowerConsumption = 0.0f;
            batTimeEstimate = -1;
            _batteryChargeInfoFields = {0};
        }
        
        // Now format the initial Battery_c string
        char remainingBatteryLife[8];
        const float drawW = (fabsf(PowerConsumption) < 0.01f) ? 0.0f : PowerConsumption;
        
        if (batTimeEstimate >= 0 && !(drawW <= 0.01f && drawW >= -0.01f)) {
            snprintf(remainingBatteryLife, sizeof(remainingBatteryLife),
                     "%d:%02d", batTimeEstimate / 60, batTimeEstimate % 60);
        } else {
            strcpy(remainingBatteryLife, "--:--");
        }
        
        if (!settings.invertBatteryDisplay) {
            snprintf(Battery_c, sizeof(Battery_c),
                     "%.2f W%.1f%% [%s]",
                     drawW,
                     (float)_batteryChargeInfoFields.RawBatteryCharge / 1000.0f,
                     remainingBatteryLife);
        } else {
            snprintf(Battery_c, sizeof(Battery_c),
                     "%.1f%% [%s]%.2f W",
                     (float)_batteryChargeInfoFields.RawBatteryCharge / 1000.0f,
                     remainingBatteryLife,
                     drawW);
        }




        // Start touch polling thread for instant response at low FPS
        touchPollRunning.store(true, std::memory_order_release);
        threadCreate(&touchPollThread, [](void* arg) -> void {
            MiniOverlay* overlay = static_cast<MiniOverlay*>(arg);
            
            // Allow only Player 1 and handheld mode
            const HidNpadIdType id_list[2] = { HidNpadIdType_No1, HidNpadIdType_Handheld };
            
            // Configure HID system to only listen to these IDs
            hidSetSupportedNpadIdType(id_list, 2);
            
            // Configure input for up to 2 supported controllers (P1 + Handheld)
            padConfigureInput(2, HidNpadStyleSet_NpadStandard | HidNpadStyleTag_NpadSystemExt);
            
            // Initialize separate pad states for both controllers
            PadState pad_p1;
            PadState pad_handheld;
            padInitialize(&pad_p1, HidNpadIdType_No1);
            padInitialize(&pad_handheld, HidNpadIdType_Handheld);
        
            u64 minusHoldStart = 0;
            u64 plusHoldStart = 0;
            static constexpr u64 HOLD_THRESHOLD_NS = 500'000'000ULL;
        
            HidTouchScreenState state = {0};
            bool inputDetected;
            size_t actualEntryCount;
        
            while (overlay->touchPollRunning.load(std::memory_order_acquire)) {
                // Only poll when rendering and not dragging
                {
                    inputDetected = false;
                    
                    // Check touch in bounds
                    if (hidGetTouchScreenStates(&state, 1) && state.count > 0) {
                        const int touchX = state.touches[0].x;
                        const int touchY = state.touches[0].y;
                        
                        // Calculate bounds (same logic as handleInput)
                        const uint32_t margin = (overlay->fontsize * 4);
                        const int overlayX = overlay->frameOffsetX;
                        const int overlayY = overlay->frameOffsetY;
                        const int overlayWidth = margin + overlay->rectangleWidth + (overlay->fontsize / 3);
                        
                        // Calculate height from Variables string
                        actualEntryCount = 1;
                        for (size_t i = 0; overlay->Variables[i] != '\0'; i++) {
                            if (overlay->Variables[i] == '\n') {
                                actualEntryCount++;
                            }
                        }
                        const int overlayHeight = ((overlay->fontsize + overlay->settings.spacing) * actualEntryCount) + 
                                                 (overlay->fontsize / 3) + overlay->settings.spacing + 
                                                 overlay->topPadding + overlay->bottomPadding;
                        
                        // Add touch padding
                        const int touchPadding = 4;
                        const int touchableX = overlayX - touchPadding;
                        const int touchableY = overlayY - touchPadding;
                        const int touchableWidth = overlayWidth + (touchPadding * 2);
                        const int touchableHeight = overlayHeight + (touchPadding * 2);
                        
                        // Check if touch is within bounds
                        if (touchX >= touchableX && touchX <= touchableX + touchableWidth &&
                            touchY >= touchableY && touchY <= touchableY + touchableHeight) {
                            inputDetected = true;
                        }
                    }
                    
                    // Poll buttons from both controllers
                    padUpdate(&pad_p1);
                    padUpdate(&pad_handheld);
                    //const u64 keysHeld_p1 = padGetButtons(&pad_p1);
                    //const u64 keysHeld_handheld = padGetButtons(&pad_handheld);
                    
                    // Combine input from both controllers
                    const u64 keysHeld = padGetButtons(&pad_p1) | padGetButtons(&pad_handheld);
                    const u64 now = armTicksToNs(armGetSystemTick());
                    
                    // Track MINUS hold duration
                    if ((keysHeld & KEY_MINUS) && !(keysHeld & ~KEY_MINUS & ALL_KEYS_MASK)) {
                        if (minusHoldStart == 0) {
                            minusHoldStart = now;
                        }
                        if (now - minusHoldStart >= HOLD_THRESHOLD_NS) {
                            // Long enough to start drag
                            inputDetected = true;
                            overlay->buttonState.minusDragActive.exchange(true, std::memory_order_acq_rel);
                        }
                    }
                    
                    // Track PLUS hold duration
                    else if ((keysHeld & KEY_PLUS) && !(keysHeld & ~KEY_PLUS & ALL_KEYS_MASK)) {
                        if (plusHoldStart == 0) {
                            plusHoldStart = now;
                        }
                        if (now - plusHoldStart >= HOLD_THRESHOLD_NS) {
                            // Long enough to start drag
                            inputDetected = true;
                            overlay->buttonState.plusDragActive.exchange(true, std::memory_order_acq_rel);
                        }
                    }

                    else {
                        minusHoldStart = plusHoldStart = 0;
                        overlay->buttonState.minusDragActive.exchange(false, std::memory_order_acq_rel);
                        overlay->buttonState.plusDragActive.exchange(false, std::memory_order_acq_rel);
                    }
                    
                    // Disable rendering on any input, re-enable when no input
                    static bool resetOnce = true;
                    if (inputDetected) {
                        if (resetOnce && isRendering) {
                            isRendering = false;
                            leventSignal(&renderingStopEvent);
                            resetOnce = false;
                        }
                    } else {
                        resetOnce = true;
                    }
                }

                if (ult::limitedMemory) {
                    static auto lastUnderscanPixels = std::make_pair(0, 0);

                    if (lastUnderscanPixels != tsl::impl::currentUnderscanPixels) {
                        for (int i = 0; i < 2; i++) {
                            tsl::gfx::Renderer::get().updateLayerSize();
                            tsl::gfx::Renderer::get().setLayerPos(std::max(std::min((int)(overlay->frameOffsetX*1.5 + 0.5) - tsl::impl::currentUnderscanPixels.first, 1280-32 - tsl::impl::currentUnderscanPixels.first), 0), 0);
                        }
                    }
                    lastUnderscanPixels = tsl::impl::currentUnderscanPixels;
                }
                
                svcSleepThread(32000000ULL); // 32ms polling
            }
        }, this, NULL, 0x1000, 0x2B, -2);
        threadStart(&touchPollThread);
    }
    ~MiniOverlay() {
        // Stop touch polling thread
        touchPollRunning.store(false, std::memory_order_release);
        threadWaitForExit(&touchPollThread);
        threadClose(&touchPollThread);

        CloseThreads();
        FullMode = true;
        fixForeground = true;
        //tsl::hlp::requestForeground(true);
        //alphabackground = 0xD;
        deactivateOriginalFooter = false;
    }

    resolutionCalls m_resolutionRenderCalls[8] = {0};
    resolutionCalls m_resolutionViewportCalls[8] = {0};
    resolutionCalls m_resolutionOutput[8] = {0};
    uint8_t resolutionLookup = 0;
    bool resolutionShow = false;

    virtual tsl::elm::Element* createUI() override {

        auto* Status = new tsl::elm::CustomDrawer([this](tsl::gfx::Renderer *renderer, u16 x, u16 y, u16 w, u16 h) {
            //static constexpr u16 frameWidth = 448;

            // Cache parsed settings and calculations
            static std::vector<std::string> showKeys;
            static std::string lastShowSetting;
            static bool needsRecalc = true;
            static std::vector<std::string> labelLines; // Store individual label lines
            static std::string lastVariables; // Track changes in Variables content
            static size_t entryCount = 0;
            static uint32_t cachedHeight = 0;
            static int cachedBaseX = 0, cachedBaseY = 0;
            static bool lastGameRunning = false; // Track game state changes
            
            // Check if we need to recalculate due to content changes
            const bool contentChanged = (std::string(Variables) != lastVariables) || 
                                (GameRunning != lastGameRunning);
            
            // Only recalculate if settings changed or content changed
            if (settings.show != lastShowSetting || !Initialized || contentChanged) {
                showKeys.clear();
                
                // Parse once and cache
                size_t start = 0, end = 0;
                const std::string& show = settings.show;
                while ((end = show.find('+', start)) != std::string::npos) {
                    showKeys.emplace_back(show.substr(start, end - start));
                    start = end + 1;
                }
                if (start < show.length()) {
                    showKeys.emplace_back(show.substr(start));
                }
                
                lastShowSetting = settings.show;
                lastVariables = Variables;
                lastGameRunning = GameRunning;
                needsRecalc = true;
            }
            
            // Initial width calculation (only once)
            if (!Initialized) {
                
                rectangleWidth = 0;
                //std::pair<u32, u32> dimensions;
                u32 width;
                
                for (const auto& key : showKeys) {
                    if (key == "CPU") {
                        //dimensions = renderer->drawString("[100%,100%,100%,100%]@4444.4", false, 0, 0, fontsize, renderer->a(0x0000));
                        if (!settings.realVolts) {
                            if (settings.showFullCPU)
                                width = renderer->getTextDimensions("[100%,100%,100%,100%]@4444.4", false, fontsize).first;
                            else
                                width = renderer->getTextDimensions("100%@4444.4", false, fontsize).first;
                        } else {
                            if (settings.showFullCPU)
                                width = renderer->getTextDimensions("[100%,100%,100%,100%]@4444.4444 mV", false, fontsize).first;
                            else
                                width = renderer->getTextDimensions("100%@4444.4444 mV", false, fontsize).first;
                        }
                    } else if (key == "GPU" || (key == "RAM" && settings.showRAMLoad)) {
                        //dimensions = renderer->drawString("100.0%@4444.4", false, 0, 0, fontsize, renderer->a(0x0000));

                        if (!settings.showRAMLoadCPUGPU) {
                            if (!settings.realVolts) {
                                width = renderer->getTextDimensions("100%@4444.4", false, fontsize).first;
                            } else {
                                width = renderer->getTextDimensions("100%@4444.4444 mV", false, fontsize).first;
                            }
                        } else {
                            if (!settings.realVolts) {
                                width = renderer->getTextDimensions("100%[100%,100%]@4444.4", false, fontsize).first;
                            } else {
                                width = renderer->getTextDimensions("100%[100%,100%]@4444.4444 mV", false, fontsize).first;
                            }
                        }
                    } else if (key == "RAM" && (!settings.showRAMLoad)) {
                        //dimensions = renderer->drawString("44444444MB@4444.4", false, 0, 0, fontsize, renderer->a(0x0000));
                        if (!settings.realVolts) {
                            width = renderer->getTextDimensions("100%@4444.4", false, fontsize).first;
                        } else {
                            if (isMariko) {
                                if (settings.showVDD2 && settings.decimalVDD2 && settings.showVDDQ)
                                    width = renderer->getTextDimensions("100%@4444.44444.4444 mV", false, fontsize).first;
                                else if (settings.showVDD2 && !settings.decimalVDD2 && settings.showVDDQ)
                                    width = renderer->getTextDimensions("100%@4444.44444444 mV", false, fontsize).first;
                                else if (settings.showVDD2 && settings.decimalVDD2)
                                    width = renderer->getTextDimensions("100%@4444.44444.4 mV", false, fontsize).first;
                                else if (settings.showVDD2 && !settings.decimalVDD2)
                                    width = renderer->getTextDimensions("100%@4444.44444 mV", false, fontsize).first;
                                else if (settings.showVDDQ)
                                    width = renderer->getTextDimensions("100%@4444.4444 mV", false, fontsize).first;
                            } else {
                                if (settings.decimalVDD2)
                                    width = renderer->getTextDimensions("100%@4444.44444.4 mV", false, fontsize).first;
                                else
                                    width = renderer->getTextDimensions("100%@4444.44444 mV", false, fontsize).first;
                            }
                        }
                    } else if (key == "SOC") {                // new block
                        if (!settings.realVolts)
                            if (settings.showFanPercentage)
                                width = renderer->getTextDimensions("88°C (100%)", false, fontsize).first;
                            else
                                width = renderer->getTextDimensions("88°C", false, fontsize).first;
                        else
                            if (settings.showSOCVoltage) {
                                if (settings.showFanPercentage)
                                    width = renderer->getTextDimensions("88°C 100%444 mV", false, fontsize).first;
                                else
                                    width = renderer->getTextDimensions("88°C444 mV", false, fontsize).first;
                            } else {
                                if (settings.showFanPercentage)
                                    width = renderer->getTextDimensions("88°C 100%", false, fontsize).first;
                                else
                                    width = renderer->getTextDimensions("88°C", false, fontsize).first;
                            }
                    } else if (key == "TMP") {
                        //dimensions = renderer->drawString("88.8\u00B0C88.8\u00B0C88.8\u00B0C (100%)", false, 0, 0, fontsize, renderer->a(0x0000));
                        if (!settings.realVolts) {
                            if (settings.showFanPercentage)
                                width = renderer->getTextDimensions("88\u00B0C 88\u00B0C 88\u00B0C 100%", false, fontsize).first;
                            else
                                width = renderer->getTextDimensions("88\u00B0C 88\u00B0C 88\u00B0C", false, fontsize).first;
                        } else {
                            if (settings.showSOCVoltage) {
                                if (settings.showFanPercentage)
                                    width = renderer->getTextDimensions("88\u00B0C 88\u00B0C 88\u00B0C 100%444 mV", false, fontsize).first;
                                else
                                    width = renderer->getTextDimensions("88\u00B0C 88\u00B0C 88\u00B0C444 mV", false, fontsize).first;
                            } else {
                                if (settings.showFanPercentage)
                                    width = renderer->getTextDimensions("88\u00B0C 88\u00B0C 88\u00B0C 100%", false, fontsize).first;
                                else
                                    width = renderer->getTextDimensions("88\u00B0C 88\u00B0C 88\u00B0C", false, fontsize).first;
                            }
                        }
                    } else if (key == "BAT") {
                        //dimensions = renderer->drawString("-44.44 W100.0% [44:44]", false, 0, 0, fontsize, renderer->a(0x0000));
                        width = renderer->getTextDimensions("-44.44 W100.0% [44:44]", false, fontsize).first;
                    } else if (key == "FPS") {
                        //dimensions = renderer->drawString("444.4", false, 0, 0, fontsize, renderer->a(0x0000));
                        width = renderer->getTextDimensions("444.4", false, fontsize).first;
                    } else if (key == "RES") {
                        //dimensions = renderer->drawString("3840x21603840x2160", false, 0, 0, fontsize, renderer->a(0x0000));
                        if (settings.showFullResolution)
                            width = renderer->getTextDimensions("3840x21603840x2160", false, fontsize).first;
                        else
                            width = renderer->getTextDimensions("2160p2160p", false, fontsize).first;
                    } else if (key == "READ") {
                        // Calculate width for read speed display
                        width = renderer->getTextDimensions("999.99 MiB/s", false, fontsize).first;
                    } else if (key == "MEM") {
                        // Calculate width for system memory display (System + divider + memory value)
                        const u32 memValueWidth = renderer->getTextDimensions("999.9 MB "+ult::FREE, false, fontsize).first;
                        const u32 dividerWidth = renderer->getTextDimensions(ult::DIVIDER_SYMBOL, false, fontsize).first;
                        const u32 systemWidth = renderer->getTextDimensions("System", false, fontsize).first;
                        width = memValueWidth + dividerWidth + systemWidth;
                    } else if (key == "DTC" && settings.showDTC) {
                        // Calculate width based on the datetime format
                        // Use multiple sample dates to ensure longest possible textual output
                    
                        char sampleDateTime[64];
                        size_t maxWidth = 0;
                    
                        // Representative dates that produce long names in most locales
                        constexpr int testDays[][3] = {
                            {2025, 11, 26}, // Wednesday – longest weekday
                            {2025, 9, 30},  // September – long month
                            {2025, 12, 31}, // December – another long month
                            {2025, 2, 28},  // February – to cover locales where it's long
                        };
                    
                        struct tm t = {};
                        for (auto &d : testDays) {
                            
                            t.tm_year = d[0] - 1900;
                            t.tm_mon  = d[1] - 1;
                            t.tm_mday = d[2];
                            t.tm_hour = 23;
                            t.tm_min  = 59;
                            t.tm_sec  = 59;
                            mktime(&t); // normalize (sets weekday, etc.)
                    
                            strftime(sampleDateTime, sizeof(sampleDateTime), settings.dtcFormat.c_str(), &t);
                            const size_t w = renderer->getTextDimensions(std::string(sampleDateTime) + "  ", false, fontsize).first;
                            if (w > maxWidth)
                                maxWidth = w;
                        }
                    
                        width = maxWidth;
                    
                    } else {
                        continue;
                    }
                    
                    if (rectangleWidth < width) {
                        rectangleWidth = width;
                    }
                }
                Initialized = true;
                needsRecalc = true;
            }
            
            // Recalculate layout when needed (including content changes)
            if (needsRecalc) {
                // Build label lines array for individual centering
                labelLines.clear();
                entryCount = 0;
                uint16_t flags = 0;
                
                bool shouldAdd;
                std::string labelText;
                for (const auto& key : showKeys) {
                    shouldAdd = false;
                    labelText = "";
                    
                    if (key == "CPU" && !(flags & 1)) {
                        shouldAdd = true;
                        labelText = "CPU";
                        flags |= 1;
                    } else if (key == "GPU" && !(flags & 2)) {
                        shouldAdd = true;
                        labelText = "GPU";
                        flags |= 2;
                    } else if (key == "RAM" && !(flags & 4)) {
                        shouldAdd = true;
                        labelText = "RAM";
                        flags |= 4;
                    } else if (key == "SOC" && !(flags & 8)) {
                        shouldAdd = true;
                        labelText = "SOC";
                        flags |= 8;
                    } else if (key == "TMP" && !(flags & 16)) {
                        shouldAdd = true;
                        labelText = "TMP";
                        flags |= 16;
                    } else if ((key == "BAT" || key == "DRAW") && !(flags & 32)) {
                        shouldAdd = true;
                        labelText = "BAT";
                        flags |= 32;
                    } else if (key == "FPS" && !(flags & 64) && GameRunning) {
                        shouldAdd = true;
                        labelText = "FPS";
                        flags |= 64;
                    } else if (key == "RES" && !(flags & 128) && GameRunning) {
                        shouldAdd = true;
                        labelText = "RES";
                        flags |= 128;
                        resolutionShow = true;
                    } else if (key == "READ" && !(flags & 512) && GameRunning) {
                        shouldAdd = true;
                        labelText = "READ";
                        flags |= 512;
                    } else if (key == "DTC" && !(flags & 256) && settings.showDTC) {
                        shouldAdd = true;
                        labelText = settings.useDTCSymbol ? "\uE007" : "DTC";
                        flags |= 256;
                    } else if (key == "MEM" && !(flags & 1024)) {
                        shouldAdd = true;
                        labelText = "MEM";
                        flags |= 1024;
                    }
                    
                    
                    if (shouldAdd) {
                        labelLines.push_back(labelText);
                        entryCount++;
                        
                        //if (settings.realVolts && key != "BAT" && key != "DRAW" && key != "FPS" && key != "RES") {
                        //    labelLines.push_back(""); // Empty line for voltage info
                        //    entryCount++;
                        //}
                    }
                }
                
                // Calculate actual entry count from Variables string
                size_t actualEntryCount = 1; // Start with 1 for the first line
                for (size_t i = 0; Variables[i] != '\0'; i++) {
                    if (Variables[i] == '\n') {
                        actualEntryCount++;
                    }
                }
                
                // Use the actual entry count for height calculation
                cachedHeight = ((fontsize + settings.spacing) * actualEntryCount) + (fontsize / 3) + settings.spacing + topPadding + bottomPadding;
                //const uint32_t margin = (fontsize * 4);
                
                cachedBaseX = 0;
                cachedBaseY = 0;
                
                needsRecalc = false;
            }
            
            // Fast rendering using cached values
            const uint32_t margin = (fontsize * 4);
            
            // Draw background
            const tsl::Color bgColor = !isDragging
                ? settings.backgroundColor // full opacity
                : settings.focusBackgroundColor;

            int clippingOffsetX = 0, clippingOffsetY = 0;
            
            int _frameOffsetX = ult::limitedMemory ? std::max(0, frameOffsetX - (1280-448)) : frameOffsetX;
            
            // Check X bounds and calculate clipping offset
            if (cachedBaseX + _frameOffsetX < int(framePadding)) {
                clippingOffsetX = framePadding - (cachedBaseX + _frameOffsetX);
            } else if ((cachedBaseX + _frameOffsetX + margin + rectangleWidth + (fontsize / 3)) > screenWidth - framePadding) {
                clippingOffsetX = (screenWidth - framePadding) - (cachedBaseX + _frameOffsetX + margin + rectangleWidth + (fontsize / 3));
            }
            
            // Check Y bounds and calculate clipping offset  
            if (cachedBaseY + frameOffsetY < int(framePadding)) {
                clippingOffsetY = framePadding - (cachedBaseY + frameOffsetY);
            } else if ((cachedBaseY + frameOffsetY + cachedHeight) > screenHeight - framePadding) {
                clippingOffsetY = (screenHeight - framePadding) - (cachedBaseY + frameOffsetY + cachedHeight);
            }
            
            // Apply to all drawing calls
            renderer->drawRoundedRectSingleThreaded(
                cachedBaseX + _frameOffsetX + clippingOffsetX, 
                cachedBaseY + frameOffsetY + clippingOffsetY, 
                margin + rectangleWidth + (fontsize / 3), 
                cachedHeight, 
                16, 
                a(bgColor)
            );
                        
            
            // Split Variables into lines for individual positioning
            std::vector<std::string> variableLines;
            const std::string variablesStr(Variables);
            size_t start = 0;
            size_t pos = 0;
            while ((pos = variablesStr.find('\n', start)) != std::string::npos) {
                variableLines.push_back(variablesStr.substr(start, pos - start));
                start = pos + 1;
            }
            if (start < variablesStr.length()) {
                variableLines.push_back(variablesStr.substr(start));
            }
            
            // Draw each label and variable line individually
            uint32_t currentY = cachedBaseY + fontsize + settings.spacing + topPadding;
            size_t labelIndex = 0;
            
            static const std::vector<std::string> specialChars = {""};
            static uint32_t labelWidth, labelCenterX;
            static std::vector<std::string> _variableLines;
            if (!delayUpdate)
                _variableLines = variableLines;
            for (size_t i = 0; i < _variableLines.size() && labelIndex < labelLines.size(); i++) {
                // Check for RES label without corresponding RES data
                if (labelIndex < labelLines.size() && labelLines[labelIndex] == "RES") {
                    //const std::string& currentLine = _variableLines[i];
                    // Check if this data line looks like resolution data (contains 'x' or 'p')
                    bool isResolutionData = m_resolutionOutput[0].width;
                    
                    if (!isResolutionData) {
                        // This is RES label but data isn't resolution data - skip the label
                        ++labelIndex;
                        // Don't increment i, so the current data line gets paired with the next label
                        --i;
                        continue;
                    }
                }

                // Draw label (centered in label region)
                if (!labelLines[labelIndex].empty()) {
                    labelWidth = renderer->getTextDimensions(labelLines[labelIndex], false, fontsize).first;
                    labelCenterX = cachedBaseX + (margin / 2) - (labelWidth / 2);
                    renderer->drawString(labelLines[labelIndex], false, labelCenterX + _frameOffsetX + clippingOffsetX, currentY + frameOffsetY + clippingOffsetY, fontsize, settings.catColor);
                }
                
                // Determine rendering method based on label type
                const std::string& currentLine = _variableLines[i];
                const int baseX = cachedBaseX + margin + _frameOffsetX + clippingOffsetX;
                const int baseY = currentY + frameOffsetY + clippingOffsetY;
                
                if (settings.useDynamicColors) {
                    if (labelIndex < labelLines.size() && labelLines[labelIndex] == "SOC") {
                        // SOC temperature rendering with gradient
                        const size_t degreesPos = currentLine.find("°");
                        if (degreesPos != std::string::npos) {
                            // Find the 'C' after the degrees symbol
                            const size_t cPos = currentLine.find("C", degreesPos);
                            if (cPos != std::string::npos) {
                                const size_t tempEnd = cPos + 1; // Include the 'C'
                                
                                // Extract temperature value and apply gradient
                                const int temp = atoi(currentLine.c_str());
                                const tsl::Color tempColor = tsl::GradientColor((float)temp);
                                
                                // Split into temperature part and remaining part
                                const std::string tempPart = currentLine.substr(0, tempEnd);
                                const std::string restPart = currentLine.substr(tempEnd);
                                
                                // Render temperature with gradient color
                                int currentX = baseX;
                                renderer->drawString(tempPart, false, currentX, baseY, fontsize, tempColor);
                                
                                // Render remaining text with normal color
                                if (!restPart.empty()) {
                                    currentX += renderer->getTextDimensions(tempPart, false, fontsize).first;
                                    renderer->drawStringWithColoredSections(restPart, false, specialChars, currentX, baseY, fontsize, settings.textColor, settings.separatorColor);
                                }
                            } else {
                                // Fallback: no C found after degrees, render normally
                                renderer->drawStringWithColoredSections(currentLine, false, specialChars, baseX, baseY, fontsize, settings.textColor, settings.separatorColor);
                            }
                        } else {
                            // Fallback: no degrees symbol found, render normally
                            renderer->drawStringWithColoredSections(currentLine, false, specialChars, baseX, baseY, fontsize, settings.textColor, settings.separatorColor);
                        }
                        
                    } else if (labelIndex < labelLines.size() && labelLines[labelIndex] == "TMP") {
                        // TMP multiple temperatures rendering with gradients
                        int currentX = baseX;
                        size_t pos = 0;
                        bool parseSuccess = true;
                        
                        // Parse up to 3 temperatures in the format "XX°C XX°C XX°C (XX%)"
                        for (int tempCount = 0; tempCount < 3 && parseSuccess && pos < currentLine.length(); tempCount++) {
                            // Find start of current temperature (skip any leading spaces)
                            while (pos < currentLine.length() && currentLine[pos] == ' ') {
                                renderer->drawString(" ", false, currentX, baseY, fontsize, settings.textColor);
                                currentX += renderer->getTextDimensions(" ", false, fontsize).first;
                                pos++;
                            }
                            
                            if (pos >= currentLine.length()) break;
                            
                            // Find degrees symbol
                            const size_t degreesPos = currentLine.find("°", pos);
                            if (degreesPos == std::string::npos) {
                                parseSuccess = false;
                                break;
                            }
                            
                            // Find 'C' after degrees symbol
                            const size_t cPos = currentLine.find("C", degreesPos);
                            if (cPos == std::string::npos) {
                                parseSuccess = false;
                                break;
                            }
                            
                            const size_t tempEnd = cPos + 1; // Include the 'C'
                            
                            // Extract and render temperature with gradient
                            const std::string tempPart = currentLine.substr(pos, tempEnd - pos);
                            const int temp = atoi(tempPart.c_str());
                            const tsl::Color tempColor = tsl::GradientColor((float)temp);
                            
                            renderer->drawString(tempPart, false, currentX, baseY, fontsize, tempColor);
                            currentX += renderer->getTextDimensions(tempPart, false, fontsize).first;
                            
                            pos = tempEnd;
                        }
                        
                        // Render any remaining text (like " (50%)" or voltage info)
                        if (pos < currentLine.length()) {
                            std::string restPart = currentLine.substr(pos);
                            renderer->drawStringWithColoredSections(restPart, false, specialChars, currentX, baseY, fontsize, settings.textColor, settings.separatorColor);
                        }
                        
                        // If parsing failed, fall back to normal rendering
                        if (!parseSuccess) {
                            renderer->drawStringWithColoredSections(currentLine, false, specialChars, baseX, baseY, fontsize, settings.textColor, settings.separatorColor);
                        }
                    } else if (labelIndex < labelLines.size() && labelLines[labelIndex] == "MEM") {
                        // MEM memory rendering with gradient color
                        // Extract numeric value for color determination
                        float sysValue = 0.0f;
                        sscanf(currentLine.c_str(), "%f", &sysValue);
                        
                        // Determine unit from string
                        const bool isGB = (currentLine.find("GB") != std::string::npos);
                        const float sysValueMB = isGB ? (sysValue * 1024.0f) : sysValue;
                        
                        // Apply same color scheme as in drawMemoryWidget
                        tsl::Color sysColor = settings.textColor;
                        if (sysValueMB >= 9.0f) {
                            sysColor = settings.useDynamicColors ? tsl::healthyRamTextColor : settings.textColor;
                        } else if (sysValueMB >= 3.0f) {
                            sysColor = settings.useDynamicColors ? tsl::neutralRamTextColor : settings.textColor;
                        } else {
                            sysColor = settings.useDynamicColors ? tsl::badRamTextColor : settings.textColor;
                        }
                        
                        // Draw the memory value with color on the left
                        int currentX = baseX;
                        renderer->drawStringWithColoredSections(currentLine, false, specialChars, currentX, baseY, fontsize, sysColor, settings.separatorColor);
                        currentX += renderer->getTextDimensions(currentLine, false, fontsize).first;
                        
                        // Draw separator " | "
                        renderer->drawString(ult::DIVIDER_SYMBOL, false, currentX, baseY, fontsize, settings.separatorColor);
                        currentX += renderer->getTextDimensions(ult::DIVIDER_SYMBOL, false, fontsize).first;
                        
                        // Draw "System" on the right
                        renderer->drawString("System", false, currentX, baseY, fontsize, settings.textColor);
                    } else {
                        // Normal rendering for all other line types (CPU, GPU, RAM, BAT, FPS, RES, DTC)
                        renderer->drawStringWithColoredSections(currentLine, false, specialChars, baseX, baseY, fontsize, settings.textColor, settings.separatorColor);
                    }
                } else {
                    // Normal rendering for all other line types (CPU, GPU, RAM, BAT, FPS, RES, DTC)
                    renderer->drawStringWithColoredSections(currentLine, false, specialChars, baseX, baseY, fontsize, settings.textColor, settings.separatorColor);
                }
                
                currentY += fontsize + settings.spacing;
                ++labelIndex;
            }
        });
        
        tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("", "");
        rootFrame->setContent(Status);
        return rootFrame;
    }

    virtual void update() override {
        if (triggerExitNow)
            return;
    
        if (!SaltySD) {
            SaltySD = CheckPort();
            if (SaltySD) {
                LoadSharedMemory();
                threadCreate(&t6, CheckIfGameRunning, NULL, NULL, 0x1000, 0x38, -2);
                threadStart(&t6);
            }
        }
    
        // Handle performance mode changes
        apmGetPerformanceMode(&performanceMode);
        if (performanceMode == ApmPerformanceMode_Normal) {
            if (fontsize != settings.handheldFontSize) {
                Initialized = false;
                fontsize = settings.handheldFontSize;
            }
        }
        else if (performanceMode == ApmPerformanceMode_Boost) {
            if (fontsize != settings.dockedFontSize) {
                Initialized = false;
                fontsize = settings.dockedFontSize;
            }
        }
    
        // Parse show settings to determine what to calculate
        std::vector<std::string> showKeys;
        {
            size_t start = 0, end = 0;
            const std::string& show = settings.show;
            while ((end = show.find('+', start)) != std::string::npos) {
                showKeys.emplace_back(show.substr(start, end - start));
                start = end + 1;
            }
            if (start < show.length()) {
                showKeys.emplace_back(show.substr(start));
            }
        }
    
        // Helper to check if a key is active
        static auto isActive = [&showKeys](const std::string& key) {
            return std::find(showKeys.begin(), showKeys.end(), key) != showKeys.end();
        };
    
        mutexLock(&mutex_Misc);
    
        // Variables to store formatted strings
        char MINI_CPU_compressed_c[42] = "";
        char MINI_CPU_volt_c[16] = "";
        char MINI_GPU_Load_c[20] = "";
        char MINI_GPU_volt_c[20] = "";
        char MINI_RAM_var_compressed_c[35] = "";
        char MINI_RAM_volt_c[32] = "";
        char MINI_MEM_RAM_c[32] = "";
        char MINI_SOC_volt_c[16] = "";
    
        // Only process CPU if needed
        if (isActive("CPU")) {
            char MINI_CPU_Usage0[7], MINI_CPU_Usage1[7], MINI_CPU_Usage2[7], MINI_CPU_Usage3[7];
            
            const uint64_t idle0 = idletick0.load(std::memory_order_acquire);
            const uint64_t idle1 = idletick1.load(std::memory_order_acquire);
            const uint64_t idle2 = idletick2.load(std::memory_order_acquire);
            const uint64_t idle3 = idletick3.load(std::memory_order_acquire);
            
            auto formatMiniUsage = [this](char* buf, size_t size, uint64_t idletick) {
                if (idletick > systemtickfrequency_impl) {
                    strcpy(buf, "0%");
                } else {
                    snprintf(buf, size, "%.0f%%",
                             (1.0 - (static_cast<double>(idletick) / systemtickfrequency_impl)) * 100.0);
                }
            };
            
            formatMiniUsage(MINI_CPU_Usage0, sizeof(MINI_CPU_Usage0), idle0);
            formatMiniUsage(MINI_CPU_Usage1, sizeof(MINI_CPU_Usage1), idle1);
            formatMiniUsage(MINI_CPU_Usage2, sizeof(MINI_CPU_Usage2), idle2);
            formatMiniUsage(MINI_CPU_Usage3, sizeof(MINI_CPU_Usage3), idle3);
    
            if (settings.showFullCPU) {
                if (settings.realFrequencies && realCPU_Hz) {
                    snprintf(MINI_CPU_compressed_c, sizeof(MINI_CPU_compressed_c), 
                        "[%s,%s,%s,%s]@%hu.%hhu", 
                        MINI_CPU_Usage0, MINI_CPU_Usage1, MINI_CPU_Usage2, MINI_CPU_Usage3, 
                        realCPU_Hz / 1000000, (realCPU_Hz / 100000) % 10);
                } else {
                    snprintf(MINI_CPU_compressed_c, sizeof(MINI_CPU_compressed_c), 
                        "[%s,%s,%s,%s]@%hu.%hhu", 
                        MINI_CPU_Usage0, MINI_CPU_Usage1, MINI_CPU_Usage2, MINI_CPU_Usage3, 
                        CPU_Hz / 1000000, (CPU_Hz / 100000) % 10);
                }
            } else {
                double usage0 = 0, usage1 = 0, usage2 = 0, usage3 = 0;
                sscanf(MINI_CPU_Usage0, "%lf%%", &usage0);
                sscanf(MINI_CPU_Usage1, "%lf%%", &usage1);
                sscanf(MINI_CPU_Usage2, "%lf%%", &usage2);
                sscanf(MINI_CPU_Usage3, "%lf%%", &usage3);
                
                double maxUsage = std::max({usage0, usage1, usage2, usage3});
                
                if (settings.realFrequencies && realCPU_Hz) {
                    snprintf(MINI_CPU_compressed_c, sizeof(MINI_CPU_compressed_c), 
                        "%.0f%%@%hu.%hhu", maxUsage,
                        realCPU_Hz / 1000000, (realCPU_Hz / 100000) % 10);
                } else {
                    snprintf(MINI_CPU_compressed_c, sizeof(MINI_CPU_compressed_c), 
                        "%.0f%%@%hu.%hhu", maxUsage,
                        CPU_Hz / 1000000, (CPU_Hz / 100000) % 10);
                }
            }
    
            if (settings.realVolts) {
                const uint32_t mv = realCPU_mV / 1000;
                snprintf(MINI_CPU_volt_c, sizeof(MINI_CPU_volt_c), "%u mV", mv);
            }
        }
    
        // Only process GPU if needed
        if (isActive("GPU")) {
            if (settings.realFrequencies && realGPU_Hz) {
                snprintf(MINI_GPU_Load_c, sizeof(MINI_GPU_Load_c),
                         "%hu%%%s%hu.%hhu",
                         GPU_Load_u / 10, "@",
                         realGPU_Hz / 1000000, (realGPU_Hz / 100000) % 10);
            } else {
                snprintf(MINI_GPU_Load_c, sizeof(MINI_GPU_Load_c),
                         "%hu%%%s%hu.%hhu",
                         GPU_Load_u / 10, "@",
                         GPU_Hz / 1000000, (GPU_Hz / 100000) % 10);
            }
    
            // Handle sleep exit
            if (settings.sleepExit) {
                const auto GPU_Hz_int = int(GPU_Hz / 1000000);
                static auto lastGPU_Hz_int = GPU_Hz_int;
                if (GPU_Hz_int == 0 && lastGPU_Hz_int != 0) {
                    isRendering = false;
                    leventSignal(&renderingStopEvent);
                    triggerExitNow = true;
                    mutexUnlock(&mutex_Misc);
                    return;
                }
                lastGPU_Hz_int = GPU_Hz_int;
            }
            
            if (settings.realVolts) {
                const uint32_t mv = realGPU_mV / 1000;
                snprintf(MINI_GPU_volt_c, sizeof(MINI_GPU_volt_c), "%u mV", mv);
            }
        }
    
        // Only process RAM if needed
        if (isActive("RAM")) {
            if (!settings.showRAMLoad) {
                const float ramTotalGiB = (RAM_Total_application_u + RAM_Total_applet_u +
                                     RAM_Total_system_u + RAM_Total_systemunsafe_u) /
                                    (1024.0f * 1024.0f);
                const float ramUsedGiB  = (RAM_Used_application_u + RAM_Used_applet_u +
                                     RAM_Used_system_u + RAM_Used_systemunsafe_u) /
                                    (1024.0f * 1024.0f);
            
                if (settings.realFrequencies && realRAM_Hz) {
                    snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
                             "%.0f/%.0fMB@%hu.%hhu",
                             ramUsedGiB, ramTotalGiB,
                             realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
                } else {
                    snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
                             "%.0f/%.0fMB@%hu.%hhu",
                             ramUsedGiB, ramTotalGiB,
                             RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
                }
            } else {
                unsigned PartLoadInt;
                
                PartLoadInt = partLoad[SysClkPartLoad_EMC] / 10;
                
                if (settings.showRAMLoadCPUGPU) {
                    unsigned ramCpuLoadInt = partLoad[SysClkPartLoad_EMCCpu] / 10;
                    int RAM_GPU_Load = partLoad[SysClkPartLoad_EMC] - partLoad[SysClkPartLoad_EMCCpu];
                    unsigned ramGpuLoadInt = RAM_GPU_Load / 10;
                    
                    if (settings.realFrequencies && realRAM_Hz) {
                        snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
                                    "%u%%[%u%%,%u%%]@%hu.%hhu",
                                    PartLoadInt, ramCpuLoadInt, ramGpuLoadInt,
                                    realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
                    } else {
                        snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
                                    "%u%%[%u%%,%u%%]@%hu.%hhu",
                                    PartLoadInt, ramCpuLoadInt, ramGpuLoadInt,
                                    RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
                    }
                } else {
                    if (settings.realFrequencies && realRAM_Hz) {
                        snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
                                    "%u%%@%hu.%hhu", PartLoadInt,
                                    realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
                    } else {
                        snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
                                    "%u%%@%hu.%hhu", PartLoadInt,
                                    RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
                    }
                }
            }
    
            if (settings.realVolts) {
                const float mv_vdd2_f = realVDD2_mV / 100000.0f;
                const uint32_t mv_vdd2_i = realVDD2_mV / 1000;
                const uint32_t mv_vddq   = realVDDQ_mV / 1000;
            
                if (isMariko) {
                    if (settings.showVDDQ && settings.showVDD2) {
                        if (settings.decimalVDD2)
                            snprintf(MINI_RAM_volt_c, sizeof(MINI_RAM_volt_c),
                                     "%.1f mV%u mV", mv_vdd2_f, mv_vddq);
                        else
                            snprintf(MINI_RAM_volt_c, sizeof(MINI_RAM_volt_c),
                                     "%u mV%u mV", mv_vdd2_i, mv_vddq);
                    } else if (settings.showVDDQ) {
                        snprintf(MINI_RAM_volt_c, sizeof(MINI_RAM_volt_c), "%u mV", mv_vddq);
                    } else if (settings.showVDD2) {
                        if (settings.decimalVDD2)
                            snprintf(MINI_RAM_volt_c, sizeof(MINI_RAM_volt_c), "%.1f mV", mv_vdd2_f);
                        else
                            snprintf(MINI_RAM_volt_c, sizeof(MINI_RAM_volt_c), "%u mV", mv_vdd2_i);
                    }
                } else {
                    if (settings.decimalVDD2)
                        snprintf(MINI_RAM_volt_c, sizeof(MINI_RAM_volt_c), "%.1f mV", mv_vdd2_f);
                    else
                        snprintf(MINI_RAM_volt_c, sizeof(MINI_RAM_volt_c), "%u mV", mv_vdd2_i);
                }
            }
        }
    
        // Only process MEM if needed
        if (isActive("MEM")) {
            const u64 freeRamBytes = RAM_Total_system_u - RAM_Used_system_u;
            
            float value;
            const char* unit;
            
            if (freeRamBytes >= 1024ULL * 1024 * 1024) {
                value = static_cast<float>(freeRamBytes) / (1024.0f * 1024.0f * 1024.0f);
                unit = "GB";
            } else {
                value = static_cast<float>(freeRamBytes) / (1024.0f * 1024.0f);
                unit = "MB";
            }
            
            int decimalPlaces;
            if (value >= 1000.0f) {
                decimalPlaces = 0;
            } else if (value >= 100.0f) {
                decimalPlaces = 1;
            } else if (value >= 10.0f) {
                decimalPlaces = 2;
            } else {
                decimalPlaces = 3;
            }
            
            snprintf(MINI_MEM_RAM_c, sizeof(MINI_MEM_RAM_c), "%.*f %s %s", 
                     decimalPlaces, value, unit, ult::FREE.c_str());
        }
    
        // Only process temperature/fan data if SOC or TMP is active
        if (isActive("TMP") || isActive("SOC")) {
            const int duty = safeFanDuty((int)Rotation_Duty);
    
            if (settings.realVolts && settings.showSOCVoltage) {
                const uint32_t mv = realSOC_mV / 1000;
                snprintf(MINI_SOC_volt_c, sizeof(MINI_SOC_volt_c), "%u mV", mv);
            }
    
            if (isActive("SOC")) {
                if (settings.showFanPercentage) {
                    snprintf(soc_temperature_c, sizeof(soc_temperature_c),
                             "%d°C %d%%", (int)SOC_temperatureF, duty);
                } else {
                    snprintf(soc_temperature_c, sizeof(soc_temperature_c),
                             "%d°C", (int)SOC_temperatureF);
                }
            }
    
            if (isActive("TMP")) {
                if (settings.showFanPercentage) {
                    snprintf(skin_temperature_c, sizeof(skin_temperature_c),
                        "%d\u00B0C %d\u00B0C %hu\u00B0C %d%%",
                        (int)SOC_temperatureF, (int)PCB_temperatureF,
                        skin_temperaturemiliC / 1000, duty);
                } else {
                    snprintf(skin_temperature_c, sizeof(skin_temperature_c),
                        "%d\u00B0C %d\u00B0C %hu\u00B0C",
                        (int)SOC_temperatureF, (int)PCB_temperatureF,
                        skin_temperaturemiliC / 1000);
                }
            }
        }
    
        // Only process resolution if RES is active and game is running
        if (isActive("RES") && GameRunning && NxFps) {
            if (!resolutionLookup) {
                (NxFps->renderCalls[0].calls) = 0xFFFF;
                resolutionLookup = 1;
            } else if (resolutionLookup == 1) {
                if ((NxFps->renderCalls[0].calls) != 0xFFFF) resolutionLookup = 2;
            } else {
                memcpy(&m_resolutionRenderCalls, &(NxFps->renderCalls), sizeof(m_resolutionRenderCalls));
                memcpy(&m_resolutionViewportCalls, &(NxFps->viewportCalls), sizeof(m_resolutionViewportCalls));
                qsort(m_resolutionRenderCalls, 8, sizeof(resolutionCalls), compare);
                qsort(m_resolutionViewportCalls, 8, sizeof(resolutionCalls), compare);
                memset(&m_resolutionOutput, 0, sizeof(m_resolutionOutput));
                size_t out_iter = 0;
                bool found = false;
                for (size_t i = 0; i < 8; i++) {
                    for (size_t x = 0; x < 8; x++) {
                        if (m_resolutionRenderCalls[i].width == 0) break;
                        if ((m_resolutionRenderCalls[i].width == m_resolutionViewportCalls[x].width) && 
                            (m_resolutionRenderCalls[i].height == m_resolutionViewportCalls[x].height)) {
                            m_resolutionOutput[out_iter].width = m_resolutionRenderCalls[i].width;
                            m_resolutionOutput[out_iter].height = m_resolutionRenderCalls[i].height;
                            m_resolutionOutput[out_iter].calls = (m_resolutionRenderCalls[i].calls > m_resolutionViewportCalls[x].calls) 
                                ? m_resolutionRenderCalls[i].calls : m_resolutionViewportCalls[x].calls;
                            out_iter++;
                            found = true;
                            break;
                        }
                    }
                    if (!found && m_resolutionRenderCalls[i].width != 0) {
                        m_resolutionOutput[out_iter].width = m_resolutionRenderCalls[i].width;
                        m_resolutionOutput[out_iter].height = m_resolutionRenderCalls[i].height;
                        m_resolutionOutput[out_iter].calls = m_resolutionRenderCalls[i].calls;
                        out_iter++;
                    }
                    found = false;
                    if (out_iter == 8) break;
                }
                if (out_iter < 8) {
                    const size_t out_iter_s = out_iter;
                    for (size_t x = 0; x < 8; x++) {
                        for (size_t y = 0; y < out_iter_s; y++) {
                            if (m_resolutionViewportCalls[x].width == 0) break;
                            if ((m_resolutionViewportCalls[x].width == m_resolutionOutput[y].width) && 
                                (m_resolutionViewportCalls[x].height == m_resolutionOutput[y].height)) {
                                found = true;
                                break;
                            }
                        }
                        if (!found && m_resolutionViewportCalls[x].width != 0) {
                            m_resolutionOutput[out_iter].width = m_resolutionViewportCalls[x].width;
                            m_resolutionOutput[out_iter].height = m_resolutionViewportCalls[x].height;
                            m_resolutionOutput[out_iter].calls = m_resolutionViewportCalls[x].calls;
                            out_iter++;            
                        }
                        found = false;
                        if (out_iter == 8) break;
                    }
                }
                qsort(m_resolutionOutput, 8, sizeof(resolutionCalls), compare);
            }
        } else if (!GameRunning && resolutionLookup != 0) {
            resolutionLookup = 0;
        }
        
        mutexUnlock(&mutex_Misc);
    
        // Battery/power draw - always update since BAT/DRAW might be displayed
        if (isActive("BAT") || isActive("DRAW")) {
            char remainingBatteryLife[8];
            const float drawW = (fabsf(PowerConsumption) < 0.01f) ? 0.0f : PowerConsumption;
            
            mutexLock(&mutex_BatteryChecker);
            
            if (batTimeEstimate >= 0 && !(drawW <= 0.01f && drawW >= -0.01f)) {
                snprintf(remainingBatteryLife, sizeof(remainingBatteryLife),
                         "%d:%02d", batTimeEstimate / 60, batTimeEstimate % 60);
            } else {
                strcpy(remainingBatteryLife, "--:--");
            }
            
            if (!settings.invertBatteryDisplay) {
                snprintf(Battery_c, sizeof(Battery_c),
                         "%.2f W%.1f%% [%s]",
                         drawW,
                         (float)_batteryChargeInfoFields.RawBatteryCharge / 1000.0f,
                         remainingBatteryLife);
            } else {
                snprintf(Battery_c, sizeof(Battery_c),
                         "%.1f%% [%s]%.2f W",
                         (float)_batteryChargeInfoFields.RawBatteryCharge / 1000.0f,
                         remainingBatteryLife,
                         drawW);
            }
            
            mutexUnlock(&mutex_BatteryChecker);
        }
    
        // Build Variables string
        char Temp[512] = "";
        uint16_t flags = 0;
        
        for (const auto& key : showKeys) {
            if (key == "CPU" && !(flags & 1)) {
                if (Temp[0]) strcat(Temp, "\n");
                strcat(Temp, MINI_CPU_compressed_c);
                if (settings.realVolts && MINI_CPU_volt_c[0]) {
                    strcat(Temp, "");
                    strcat(Temp, MINI_CPU_volt_c);
                }
                flags |= 1;
            }
            else if (key == "GPU" && !(flags & 2)) {
                if (Temp[0]) strcat(Temp, "\n");
                strcat(Temp, MINI_GPU_Load_c);
                if (settings.realVolts && MINI_GPU_volt_c[0]) {
                    strcat(Temp, "");
                    strcat(Temp, MINI_GPU_volt_c);
                }
                flags |= 2;
            }
            else if (key == "RAM" && !(flags & 4)) {
                if (Temp[0]) strcat(Temp, "\n");
                strcat(Temp, MINI_RAM_var_compressed_c);
                if (settings.realVolts && MINI_RAM_volt_c[0]) {
                    strcat(Temp, "");
                    strcat(Temp, MINI_RAM_volt_c);
                }
                flags |= 4;
            }
            else if (key == "SOC" && !(flags & 8)) {
                if (Temp[0]) strcat(Temp, "\n");
                strcat(Temp, soc_temperature_c);
                if (settings.realVolts && settings.showSOCVoltage && MINI_SOC_volt_c[0]) {
                    strcat(Temp, "");
                    strcat(Temp, MINI_SOC_volt_c);
                }
                flags |= 8;
            }
            else if (key == "TMP" && !(flags & 16)) {
                if (Temp[0]) strcat(Temp, "\n");
                strcat(Temp, skin_temperature_c);
                if (settings.realVolts && settings.showSOCVoltage && MINI_SOC_volt_c[0]) {
                    strcat(Temp, "");
                    strcat(Temp, MINI_SOC_volt_c);
                }
                flags |= 16;
            }
            else if ((key == "BAT" || key == "DRAW") && !(flags & 32)) {
                if (Temp[0]) strcat(Temp, "\n");
                strcat(Temp, Battery_c);
                flags |= 32;
            }
            else if (key == "FPS" && !(flags & 64) && GameRunning) {
                if (Temp[0]) strcat(Temp, "\n");
                char Temp_s[24];
                snprintf(Temp_s, sizeof(Temp_s), "%2.1f [%2.1f - %2.1f]", FPSavg, FPSmin, FPSmax);
                strcat(Temp, Temp_s);
                flags |= 64;
            }
            else if (key == "RES" && !(flags & 128) && GameRunning && m_resolutionOutput[0].width) {
                if (Temp[0]) strcat(Temp, "\n");
                char Temp_s[32] = "";
                static std::pair<uint16_t, uint16_t> old_res[2];
                
                uint16_t w0 = m_resolutionOutput[0].width;
                uint16_t h0 = m_resolutionOutput[0].height;
                uint16_t w1 = m_resolutionOutput[1].width;
                uint16_t h1 = m_resolutionOutput[1].height;
                
                if (w0 || w1) {
                    if (w0 && w1) {
                        if ((w0 == old_res[1].first && h0 == old_res[1].second) ||
                            (w1 == old_res[0].first && h1 == old_res[0].second)) {
                            std::swap(w0, w1);
                            std::swap(h0, h1);
                        }
                    }
                    
                    
                    // Format based on whether we show full resolution or just height (p)
                    if (settings.showFullResolution) {
                        if (!w1 || !h1)
                            snprintf(Temp_s, sizeof(Temp_s), "%dx%d", w0 ? w0 : w1, h0 ? h0 : h1);
                        else
                            snprintf(Temp_s, sizeof(Temp_s), "%dx%d%dx%d", w0, h0, w1, h1);
                    } else {
                        if (!w1 || !h1)
                            snprintf(Temp_s, sizeof(Temp_s), "%dp", h0 ? h0 : h1);
                        else
                            snprintf(Temp_s, sizeof(Temp_s), "%dp%dp", h0, h1);
                    }
                    
                    // Update last-frame cache
                    old_res[0] = {m_resolutionOutput[0].width, m_resolutionOutput[0].height};
                    old_res[1] = {m_resolutionOutput[1].width, m_resolutionOutput[1].height};
        
                    strcat(Temp, Temp_s);
                    flags |= 128;
                }
            }
            else if (key == "READ" && !(flags & 512) && GameRunning && NxFps) {
                if (Temp[0]) strcat(Temp, "\n");
                char Temp_s[24];
                if ((NxFps->readSpeedPerSecond) != 0.f) {
                    snprintf(Temp_s, sizeof(Temp_s), "%.2f MiB/s", (NxFps->readSpeedPerSecond) / 1048576.f);
                } else {
                    strcpy(Temp_s, "n/d");
                }
                strcat(Temp, Temp_s);
                flags |= 512;
            }
            else if (key == "DTC" && !(flags & 256) && settings.showDTC) {
                if (Temp[0]) strcat(Temp, "\n");
                char dateTimeStr[64];
                time_t rawtime = time(NULL);
                struct tm *timeinfo = localtime(&rawtime);
                strftime(dateTimeStr, sizeof(dateTimeStr), settings.dtcFormat.c_str(), timeinfo);
                strcat(Temp, dateTimeStr);
                flags |= 256;
            }
            else if (key == "MEM" && !(flags & 1024)) {
                if (Temp[0]) strcat(Temp, "\n");
                strcat(Temp, MINI_MEM_RAM_c);
                flags |= 1024;
            }
        }
        
        strcpy(Variables, Temp);
    
        // Enable rendering if needed
        if (!skipOnce) {
            if (runOnce) {
                isRendering = true;
                leventClear(&renderingStopEvent);
                runOnce = false;
            }
        } else {
            skipOnce = false;
        }
    }
                
    virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
        // Static variables to maintain drag state between function calls
        static bool oldTouchDetected = false;
        static bool oldMinusHeld = false;
        static bool oldPlusHeld = false;

        static HidTouchState initialTouchPos = {0};
        static int initialFrameOffsetX = 0;
        static int initialFrameOffsetY = 0;
        static constexpr int TOUCH_THRESHOLD = 8;
        static bool hasMoved = false;
        //static u64 lastTouchTime = 0;
        //static constexpr u64 TOUCH_DEBOUNCE_NS = 16000000ULL; // 16ms debounce
    
        // Get current time for debouncing
        //const u64 currentTime = armTicksToNs(armGetSystemTick());
        
        // Better touch detection - check if coordinates are within reasonable screen bounds
        const bool currentTouchDetected = (touchPos.x > 0 && touchPos.y > 0 && 
                                    touchPos.x < screenWidth && touchPos.y < screenHeight);
        
        // Debounce touch detection
        //if (currentTouchDetected && !oldTouchDetected) {
        //    if (currentTime - lastTouchTime < TOUCH_DEBOUNCE_NS) {
        //        currentTouchDetected = false; // Ignore rapid touch changes
        //    } else {
        //        lastTouchTime = currentTime;
        //    }
        //}

        static bool clearOnRelease = false;

        if (clearOnRelease && !isRendering) {
            clearOnRelease = false;
            isRendering = true;
            leventClear(&renderingStopEvent);
        }
        
        // Calculate overlay bounds
        const uint32_t margin = (fontsize * 4);
        
        // Cache bounds calculation
        static int cachedBaseX = 0;
        static int cachedBaseY = 0;
        static uint32_t cachedOverlayHeight = 0;
        static bool boundsNeedUpdate = true;
        static std::string lastVariables = "";
        
        // Only recalculate bounds when Variables change or first time
        if (boundsNeedUpdate || std::string(Variables) != lastVariables) {
            // Calculate actual entry count from Variables string
            size_t actualEntryCount = 1;
            for (size_t i = 0; Variables[i] != '\0'; i++) {
                if (Variables[i] == '\n') {
                    actualEntryCount++;
                }
            }
            
            cachedOverlayHeight = ((fontsize + settings.spacing) * actualEntryCount) + 
                                 (fontsize / 3) + settings.spacing + topPadding + bottomPadding;
            
            // Position calculation based on settings
            cachedBaseX = 0;
            cachedBaseY = 0;
            
            boundsNeedUpdate = false;
            lastVariables = Variables;
        }
        
        const int overlayX = frameOffsetX;//ult::limitedMemory ? cachedBaseX + std::max(0, frameOffsetX - (1280-448)) : cachedBaseX + frameOffsetX;
        const int overlayY = cachedBaseY + frameOffsetY;
        const int overlayWidth = margin + rectangleWidth + (fontsize / 3);
        const int overlayHeight = cachedOverlayHeight;
        
        // Add padding to make touch detection more forgiving
        static constexpr int touchPadding = 4;
        const int touchableX = overlayX - touchPadding;
        const int touchableY = overlayY - touchPadding;
        const int touchableWidth = overlayWidth + (touchPadding * 2);
        const int touchableHeight = overlayHeight + (touchPadding * 2);
        
        // Screen boundaries for clamping
        const int minX = -cachedBaseX + framePadding;
        const int maxX = screenWidth - overlayWidth - cachedBaseX - framePadding;
        const int minY = -cachedBaseY + framePadding;
        const int maxY = screenHeight - overlayHeight - cachedBaseY - framePadding;
    
        const bool minusDragReady = buttonState.minusDragActive.load(std::memory_order_acquire);
        const bool plusDragReady = buttonState.plusDragActive.load(std::memory_order_acquire);

        // Check button states
        const bool currentMinusHeld = (keysHeld & KEY_MINUS) && !(keysHeld & ~KEY_MINUS & ALL_KEYS_MASK) && minusDragReady;
        const bool currentPlusHeld = (keysHeld & KEY_PLUS) && !(keysHeld & ~KEY_PLUS & ALL_KEYS_MASK) && plusDragReady;
            
        // Handle touch dragging
        if (currentTouchDetected && !isDragging) {
            // Touch detected and not currently dragging - check if we should start
            const int touchX = touchPos.x;
            const int touchY = touchPos.y;
            
            if (!oldTouchDetected) {
                // Touch just started - check if within overlay bounds
                if (touchX >= touchableX && touchX <= touchableX + touchableWidth &&
                    touchY >= touchableY && touchY <= touchableY + touchableHeight) {
                    
                    // Start touch dragging
                    isDragging = true;
                    //isRendering = false;
                    //leventSignal(&renderingStopEvent);
                    triggerRumbleClick.store(true, std::memory_order_release);
                    triggerOnSound.store(true, std::memory_order_release);
                    hasMoved = false;
                    initialTouchPos = touchPos;
                    initialFrameOffsetX = frameOffsetX;
                    initialFrameOffsetY = frameOffsetY;
                }
            }
        } else if (currentTouchDetected && isDragging && !currentMinusHeld && !currentPlusHeld) {
            // Continue touch dragging (only if neither MINUS nor PLUS is held)
            const int touchX = touchPos.x;
            const int touchY = touchPos.y;
            const int deltaX = touchX - initialTouchPos.x;
            const int deltaY = touchY - initialTouchPos.y;
            
            // Check if we've moved enough to consider this a drag
            if (!hasMoved) {
                const int totalMovement = abs(deltaX) + abs(deltaY);
                if (totalMovement >= TOUCH_THRESHOLD) {
                    hasMoved = true;
                }
            }
            
            if (hasMoved) {
                // Update frame offsets with boundary checking
                const int newFrameOffsetX = std::max(minX, std::min(maxX, initialFrameOffsetX + deltaX));
                const int newFrameOffsetY = std::max(minY, std::min(maxY, initialFrameOffsetY + deltaY));
                
                frameOffsetX = newFrameOffsetX;
                frameOffsetY = newFrameOffsetY;

                if (ult::limitedMemory) {
                    tsl::gfx::Renderer::get().setLayerPos(std::max(std::min((int)(frameOffsetX*1.5 + 0.5) - tsl::impl::currentUnderscanPixels.first, 1280-32 - tsl::impl::currentUnderscanPixels.first), 0), 0);
                }
                boundsNeedUpdate = true;
            }
        } else if (!currentTouchDetected && oldTouchDetected && isDragging && !currentMinusHeld && !currentPlusHeld) {
            // Touch just released and we were touch dragging
            if (hasMoved) {
                // Save position when touch drag ends
                auto iniData = ult::getParsedDataFromIniFile(configIniPath);
                iniData["mini"]["frame_offset_x"] = std::to_string(frameOffsetX);
                iniData["mini"]["frame_offset_y"] = std::to_string(frameOffsetY);
                ult::saveIniFileData(configIniPath, iniData);
            }
            
            // Reset touch drag state
            isDragging = false;
            hasMoved = false;
            //isRendering = true;
            //leventClear(&renderingStopEvent);
            clearOnRelease = true;
            triggerRumbleDoubleClick.store(true, std::memory_order_release);
            triggerOffSound.store(true, std::memory_order_release);
        }
        

        // Handle joystick dragging (MINUS + right joystick OR PLUS + left joystick)
        if ((currentMinusHeld || currentPlusHeld) && !isDragging) {
            // Start joystick dragging
            isDragging = true;
            //isRendering = false;
            //leventSignal(&renderingStopEvent);
            triggerRumbleClick.store(true, std::memory_order_release);
            triggerOnSound.store(true, std::memory_order_release);
        } else if ((currentMinusHeld || currentPlusHeld) && isDragging) {
            // Continue joystick dragging
            static constexpr int JOYSTICK_DEADZONE = 20;
            
            // Choose the appropriate joystick based on which button is held
            const HidAnalogStickState& activeJoystick = currentMinusHeld ? joyStickPosRight : joyStickPosLeft;
            
            // Only move if joystick is outside deadzone
            if (abs(activeJoystick.x) > JOYSTICK_DEADZONE || abs(activeJoystick.y) > JOYSTICK_DEADZONE) {
                // Calculate joystick magnitude (distance from center)
                const float magnitude = sqrt((float)(activeJoystick.x * activeJoystick.x + activeJoystick.y * activeJoystick.y));
                const float normalizedMagnitude = magnitude / 32767.0f; // Normalize to 0-1 range
                
                // Single smooth curve: stays very slow for wide range, then accelerates
                static constexpr float baseSensitivity = 0.00008f;  // Higher so small movements register
                static constexpr float maxSensitivity = 0.0005f;
                
                // Use x^8 curve - stays very low until ~70% then curves up sharply
                const float curveValue = pow(normalizedMagnitude, 8.0f);
                const float currentSensitivity = baseSensitivity + (maxSensitivity - baseSensitivity) * curveValue;
                
                // Calculate movement delta with fractional accumulation
                static float accumulatedX = 0.0f;
                static float accumulatedY = 0.0f;
                
                accumulatedX += (float)activeJoystick.x * currentSensitivity;
                accumulatedY += -(float)activeJoystick.y * currentSensitivity;
                
                // Extract integer movement and keep fractional part
                const int deltaX = (int)accumulatedX;
                const int deltaY = (int)accumulatedY;
                accumulatedX -= deltaX;
                accumulatedY -= deltaY;
                
                // Update frame offsets with boundary checking
                const int newFrameOffsetX = std::max(minX, std::min(maxX, frameOffsetX + deltaX));
                const int newFrameOffsetY = std::max(minY, std::min(maxY, frameOffsetY + deltaY));
                
                frameOffsetX = newFrameOffsetX;
                frameOffsetY = newFrameOffsetY;

                if (ult::limitedMemory) {
                    tsl::gfx::Renderer::get().setLayerPos(std::max(std::min((int)(frameOffsetX*1.5 + 0.5) - tsl::impl::currentUnderscanPixels.first, 1280-32 - tsl::impl::currentUnderscanPixels.first), 0), 0);
                }
                boundsNeedUpdate = true;
            }
        } else if (((!currentMinusHeld && oldMinusHeld) || (!currentPlusHeld && oldPlusHeld)) && isDragging) {
            // Button just released - stop joystick dragging
            auto iniData = ult::getParsedDataFromIniFile(configIniPath);
            iniData["mini"]["frame_offset_x"] = std::to_string(frameOffsetX);
            iniData["mini"]["frame_offset_y"] = std::to_string(frameOffsetY);
            ult::saveIniFileData(configIniPath, iniData);
            isDragging = false;
            //isRendering = true;
            //leventClear(&renderingStopEvent);
            clearOnRelease = true;
            triggerRumbleDoubleClick.store(true, std::memory_order_release);
            triggerOffSound.store(true, std::memory_order_release);
        }
        
        // Update state for next frame
        oldTouchDetected = currentTouchDetected;
        oldMinusHeld = currentMinusHeld;
        oldPlusHeld = currentPlusHeld;
        
        // Handle existing key input logic (but don't interfere with dragging)
        if (!isDragging) {
            if (isKeyComboPressed(keysHeld, keysDown)) {
                isRendering = false;
                leventSignal(&renderingStopEvent);
                //triggerRumbleDoubleClick.store(true, std::memory_order_release);
                skipOnce = true;
                runOnce = true;
                //TeslaFPS = 60;
                if (skipMain) {
                    //lastSelectedItem = "Mini";
                    lastMode = "returning";
                    tsl::goBack();
                }
                else {
                    tsl::setNextOverlay(filepath.c_str(), "--lastSelectedItem Mini");
                    tsl::Overlay::get()->close();
                }
                return true;
            }
            else if (((keysDown & KEY_L) && (keysDown & KEY_ZL)) || ((keysDown & KEY_L) && (keysHeld & KEY_ZL)) || ((keysHeld & KEY_L) && (keysDown & KEY_ZL))) { 
                FPSmin = 254; 
                FPSmax = 0; 
            }
        }
        
        // Return true if we handled the input (during dragging)
        return isDragging;
    }
};