#include "GameRenderer.h"
#include "../gameplay/Game.h"
#include "../graphics/Font.h"
#include "../gameplay/LineEffect.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdio>
#include <random>
#include <vector>

namespace {

// Color constants (copied from main.cpp)
static const SDL_Color COLORS[] = {
    {0, 0, 0, 255},        // 0: BLACK (empty)
    {0, 255, 255, 255},    // 1: I-piece - cyan
    {255, 255, 0, 255},    // 2: O-piece - yellow
    {128, 0, 128, 255},    // 3: T-piece - purple
    {0, 255, 0, 255},      // 4: S-piece - green
    {255, 0, 0, 255},      // 5: Z-piece - red
    {0, 0, 255, 255},      // 6: J-piece - blue
    {255, 165, 0, 255}     // 7: L-piece - orange
};

void GameRenderer::drawRect(SDL_Renderer* renderer, float x, float y, float w, float h, SDL_Color c) {
    SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
    SDL_FRect fr{x, y, w, h};
    SDL_RenderFillRect(renderer, &fr);
}

void GameRenderer::drawBlockTexture(SDL_Renderer* renderer, SDL_Texture* blocksTex, float x, float y, float size, int blockType) {
    if (!blocksTex || blockType < 0 || blockType >= PIECE_COUNT) {
        // Fallback to colored rectangle if texture isn't available
        SDL_Color color = (blockType >= 0 && blockType < PIECE_COUNT) ? COLORS[blockType + 1] : SDL_Color{128, 128, 128, 255};
        drawRect(renderer, x, y, size-1, size-1, color);
        return;
    }
    
    // JavaScript uses: sx = type * spriteSize, sy = 0, with 2px padding
    // Each sprite is 90px wide in the horizontal sprite sheet
    const int SPRITE_SIZE = 90;
    float srcX = blockType * SPRITE_SIZE + 2;  // Add 2px padding like JS
    float srcY = 2;                            // Add 2px padding from top like JS
    float srcW = SPRITE_SIZE - 4;              // Subtract 4px total padding like JS
    float srcH = SPRITE_SIZE - 4;              // Subtract 4px total padding like JS
    
    SDL_FRect srcRect = {srcX, srcY, srcW, srcH};
    SDL_FRect dstRect = {x, y, size, size};
    SDL_RenderTexture(renderer, blocksTex, &srcRect, &dstRect);
}

void GameRenderer::drawPiece(SDL_Renderer* renderer, SDL_Texture* blocksTex, const Game::Piece& piece, float ox, float oy, float tileSize, bool isGhost) {
    if (piece.type >= PIECE_COUNT) return;
    
    for (int cy = 0; cy < 4; ++cy) {
        for (int cx = 0; cx < 4; ++cx) {
            if (Game::cellFilled(piece, cx, cy)) {
                float px = ox + (piece.x + cx) * tileSize;
                float py = oy + (piece.y + cy) * tileSize;
                
                if (isGhost) {
                    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
                    
                    // Draw ghost piece as barely visible gray outline
                    SDL_SetRenderDrawColor(renderer, 180, 180, 180, 20); // Very faint gray
                    SDL_FRect rect = {px + 2, py + 2, tileSize - 4, tileSize - 4};
                    SDL_RenderFillRect(renderer, &rect);
                    
                    // Draw thin gray border
                    SDL_SetRenderDrawColor(renderer, 180, 180, 180, 30);
                    SDL_FRect border = {px + 1, py + 1, tileSize - 2, tileSize - 2};
                    SDL_RenderRect(renderer, &border);
                } else {
                    drawBlockTexture(renderer, blocksTex, px, py, tileSize, piece.type);
                }
            }
        }
    }
}

void GameRenderer::drawSmallPiece(SDL_Renderer* renderer, SDL_Texture* blocksTex, PieceType pieceType, float x, float y, float tileSize) {
    if (pieceType >= PIECE_COUNT) return;
    
    // Use the first rotation (index 0) for preview
    Game::Piece previewPiece;
    previewPiece.type = pieceType;
    previewPiece.rot = 0;
    previewPiece.x = 0;
    previewPiece.y = 0;
    
    // Center the piece in the preview area
    float offsetX = 0, offsetY = 0;
    if (pieceType == 0) { offsetX = tileSize * 0.5f; } // I-piece centering (assuming I = 0)
    else if (pieceType == 1) { offsetX = tileSize * 0.5f; } // O-piece centering (assuming O = 1)
    
    // Use semi-transparent alpha for preview blocks
    Uint8 previewAlpha = 180;
    if (blocksTex) {
        SDL_SetTextureAlphaMod(blocksTex, previewAlpha);
    }
    
    for (int cy = 0; cy < 4; ++cy) {
        for (int cx = 0; cx < 4; ++cx) {
            if (Game::cellFilled(previewPiece, cx, cy)) {
                float px = x + offsetX + cx * tileSize;
                float py = y + offsetY + cy * tileSize;
                drawBlockTexture(renderer, blocksTex, px, py, tileSize, pieceType);
            }
        }
    }
    
    // Reset alpha
    if (blocksTex) {
        SDL_SetTextureAlphaMod(blocksTex, 255);
    }
}

// Draw the hold panel (extracted for readability).
static void drawHoldPanel(SDL_Renderer* renderer,
                          Game* game,
                          FontAtlas* pixelFont,
                          SDL_Texture* blocksTex,
                          SDL_Texture* holdPanelTex,
                          float scoreX,
                          float statsW,
                          float gridY,
                          float finalBlockSize,
                          float statsY,
                          float statsH) {
    float holdBlockH = (finalBlockSize * 0.6f) * 4.0f;
    // Base panel height; enforce minimum but allow larger to fit texture
    float panelH = std::max(holdBlockH + 12.0f, 420.0f);
    // Increase height by ~20% of the hold block to give more vertical room
    float extraH = holdBlockH * 0.20f;
    panelH += extraH;

    const float holdGap = 18.0f;

    // Align X to the bottom score label (`scoreX`) plus an offset to the right
    float panelX = scoreX + 30.0f; // move ~30px right to align with score label
    float panelW = statsW + 32.0f;
    float panelY = gridY - panelH - holdGap;
    // Move panel a bit higher for spacing (about half the extra height)
    panelY -= extraH * 0.5f;
    float labelX = panelX + 40.0f; // shift HOLD label ~30px to the right
    float labelY = panelY + 8.0f;

    if (holdPanelTex) {
        int texW = 0, texH = 0;
        SDL_QueryTexture(holdPanelTex, nullptr, nullptr, &texW, &texH);
        if (texW > 0 && texH > 0) {
            // Fill panel width and compute destination height from texture aspect ratio
            float texAspect = float(texH) / float(texW);
            float dstW = panelW;
            float dstH = dstW * texAspect;
            // If texture height exceeds panel, expand panelH to fit texture comfortably
            if (dstH + 12.0f > panelH) {
                panelH = dstH + 12.0f;
                panelY = gridY - panelH - holdGap;
                labelY = panelY + 8.0f;
            }
            float dstX = panelX;
            float dstY = panelY + (panelH - dstH) * 0.5f;

            SDL_FRect panelDst{dstX, dstY, dstW, dstH};
            SDL_SetTextureBlendMode(holdPanelTex, SDL_BLENDMODE_BLEND);
            SDL_SetTextureScaleMode(holdPanelTex, SDL_SCALEMODE_LINEAR);
            SDL_RenderTexture(renderer, holdPanelTex, nullptr, &panelDst);
        } else {
            // Fallback to filling panel area if texture metrics unavailable
            SDL_SetRenderDrawColor(renderer, 12, 18, 32, 220);
            SDL_FRect panelDst{panelX, panelY, panelW, panelH};
            SDL_RenderFillRect(renderer, &panelDst);
        }
    } else {
        SDL_SetRenderDrawColor(renderer, 12, 18, 32, 220);
        SDL_FRect panelDst{panelX, panelY, panelW, panelH};
        SDL_RenderFillRect(renderer, &panelDst);
    }

    pixelFont->draw(renderer, labelX, labelY, "HOLD", 1.0f, {255, 220, 0, 255});

    if (game->held().type < PIECE_COUNT) {
        float previewW = finalBlockSize * 0.6f * 4.0f;
        float previewX = panelX + (panelW - previewW) * 0.5f;
        float previewY = panelY + (panelH - holdBlockH) * 0.5f;
        drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(game->held().type), previewX, previewY, finalBlockSize * 0.6f);
    }
}

// Draw next piece panel (border/texture + preview)
static void drawNextPanel(SDL_Renderer* renderer,
                          FontAtlas* pixelFont,
                          SDL_Texture* nextPanelTex,
                          SDL_Texture* blocksTex,
                          Game* game,
                          float nextX,
                          float nextY,
                          float nextW,
                          float nextH,
                          float contentOffsetX,
                          float contentOffsetY,
                          float finalBlockSize) {
    if (nextPanelTex) {
        SDL_FRect dst{ nextX - contentOffsetX, nextY - contentOffsetY, nextW, nextH };
        SDL_SetTextureBlendMode(nextPanelTex, SDL_BLENDMODE_BLEND);
        SDL_RenderTexture(renderer, nextPanelTex, nullptr, &dst);
    } else {
        // Draw bordered panel as before
        SDL_SetRenderDrawColor(renderer, 100, 120, 200, 255);
        SDL_FRect outer{ nextX - 3 - contentOffsetX, nextY - 3 - contentOffsetY, nextW + 6, nextH + 6 };
        SDL_RenderFillRect(renderer, &outer);
        SDL_SetRenderDrawColor(renderer, 30, 35, 50, 255);
        SDL_FRect inner{ nextX - contentOffsetX, nextY - contentOffsetY, nextW, nextH };
        SDL_RenderFillRect(renderer, &inner);
    }

    // Label and small preview
    pixelFont->draw(renderer, nextX + 10, nextY - 20, "NEXT", 1.0f, {255, 220, 0, 255});
    if (game->next().type < PIECE_COUNT) {
        drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(game->next().type), nextX + 10, nextY + 5, finalBlockSize * 0.6f);
    }
}

// Draw score panel (right side)
static void drawScorePanel(SDL_Renderer* renderer,
                          FontAtlas* pixelFont,
                          Game* game,
                          float scoreX,
                          float gridY,
                          float GRID_H,
                          float finalBlockSize) {
    const float contentTopOffset = 0.0f;
    const float contentBottomOffset = 290.0f;
    const float contentPad = 36.0f;
    float scoreContentH = (contentBottomOffset - contentTopOffset) + contentPad;
    float baseY = gridY + (GRID_H - scoreContentH) * 0.5f;

    pixelFont->draw(renderer, scoreX, baseY + 0, "SCORE", 1.0f, {255, 220, 0, 255});
    char scoreStr[32];
    snprintf(scoreStr, sizeof(scoreStr), "%d", game->score());
    pixelFont->draw(renderer, scoreX, baseY + 25, scoreStr, 0.9f, {255, 255, 255, 255});

    pixelFont->draw(renderer, scoreX, baseY + 70, "LINES", 1.0f, {255, 220, 0, 255});
    char linesStr[16];
    snprintf(linesStr, sizeof(linesStr), "%03d", game->lines());
    pixelFont->draw(renderer, scoreX, baseY + 95, linesStr, 0.9f, {255, 255, 255, 255});

    pixelFont->draw(renderer, scoreX, baseY + 140, "LEVEL", 1.0f, {255, 220, 0, 255});
    char levelStr[16];
    snprintf(levelStr, sizeof(levelStr), "%02d", game->level());
    pixelFont->draw(renderer, scoreX, baseY + 165, levelStr, 0.9f, {255, 255, 255, 255});

    // Next level progress
    int startLv = game->startLevelBase();
    int firstThreshold = (startLv + 1) * 10;
    int linesDone = game->lines();
    int nextThreshold = 0;
    if (linesDone < firstThreshold) {
        nextThreshold = firstThreshold;
    } else {
        int blocksPast = linesDone - firstThreshold;
        nextThreshold = firstThreshold + ((blocksPast / 10) + 1) * 10;
    }
    int linesForNext = std::max(0, nextThreshold - linesDone);
    pixelFont->draw(renderer, scoreX, baseY + 200, "NEXT LVL", 1.0f, {255, 220, 0, 255});
    char nextStr[32];
    snprintf(nextStr, sizeof(nextStr), "%d  LINES", linesForNext);
    pixelFont->draw(renderer, scoreX, baseY + 225, nextStr, 0.9f, {80, 255, 120, 255});

    // Time display
    pixelFont->draw(renderer, scoreX, baseY + 265, "TIME", 1.0f, {255, 220, 0, 255});
    int totalSecs = static_cast<int>(game->elapsed());
    int mins = totalSecs / 60;
    int secs = totalSecs % 60;
    char timeStr[16];
    snprintf(timeStr, sizeof(timeStr), "%02d:%02d", mins, secs);
    pixelFont->draw(renderer, scoreX, baseY + 290, timeStr, 0.9f, {255, 255, 255, 255});
}

void GameRenderer::renderPlayingState(
    SDL_Renderer* renderer,
    Game* game,
    FontAtlas* pixelFont,
    LineEffect* lineEffect,
    SDL_Texture* blocksTex,
    SDL_Texture* statisticsPanelTex,
    SDL_Texture* scorePanelTex,
    SDL_Texture* nextPanelTex,
    SDL_Texture* holdPanelTex,
    float logicalW,
    float logicalH,
    float logicalScale,
    float winW,
    float winH,
        bool showExitConfirmPopup,
        int exitPopupSelectedButton,
        bool suppressPauseVisuals
) {
    (void)exitPopupSelectedButton;
    if (!game || !pixelFont) return;
    
    // Calculate actual content area (centered within the window)
    float contentScale = logicalScale;
    float contentW = logicalW * contentScale;
    float contentH = logicalH * contentScale;
    float contentOffsetX = (winW - contentW) * 0.5f / contentScale;
    float contentOffsetY = (winH - contentH) * 0.5f / contentScale;
    
    // Helper lambda for drawing rectangles with content offset
    auto drawRectWithOffset = [&](float x, float y, float w, float h, SDL_Color c) {
        SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
        SDL_FRect fr{x + contentOffsetX, y + contentOffsetY, w, h};
        SDL_RenderFillRect(renderer, &fr);
    };

    // Responsive layout that scales with window size while maintaining margins
    const float MIN_MARGIN = 40.0f;
    const float TOP_MARGIN = 60.0f;
    const float PANEL_WIDTH = 180.0f;
    const float PANEL_SPACING = 30.0f;
    const float NEXT_PIECE_HEIGHT = 120.0f;
    const float BOTTOM_MARGIN = 60.0f;
    
        float yCursor = statsY + 44.0f;
    const float availableWidth = logicalW - (MIN_MARGIN * 2) - (PANEL_WIDTH * 2) - (PANEL_SPACING * 2);
    const float availableHeight = logicalH - TOP_MARGIN - BOTTOM_MARGIN - NEXT_PIECE_HEIGHT;
    
    const float maxBlockSizeW = availableWidth / Game::COLS;
        // Draw hold panel via helper
        drawHoldPanel(renderer, game, pixelFont, blocksTex, holdPanelTex, scoreX, statsW, gridY, finalBlockSize, statsY, statsH);
    
    // Draw grid lines (solid so grid remains legible over background)
    SDL_SetRenderDrawColor(renderer, 40, 45, 60, 255);
    for (int x = 1; x < Game::COLS; ++x) {
        float lineX = gridX + x * finalBlockSize;
        SDL_RenderLine(renderer, lineX, gridY, lineX, gridY + GRID_H);
    }
    for (int y = 1; y < Game::ROWS; ++y) {
        float lineY = gridY + y * finalBlockSize;
        SDL_RenderLine(renderer, gridX, lineY, gridX + GRID_W, lineY);
    }

    Uint64 nowTicks = SDL_GetTicks();
    float deltaSeconds = (g_lastSparkTick == 0) ? (1.0f / 60.0f) : static_cast<float>(nowTicks - g_lastSparkTick) / 1000.0f;
    g_lastSparkTick = nowTicks;
    updateSparks(std::max(0.0f, deltaSeconds));
    drawSparks(renderer, gridX, gridY, finalBlockSize);
    
    // Draw block statistics panel border
    drawRectWithOffset(statsX - 3 - contentOffsetX, statsY - 3 - contentOffsetY, statsW + 6, statsH + 6, {100, 120, 200, 255});
    drawRectWithOffset(statsX - contentOffsetX, statsY - contentOffsetY, statsW, statsH, {30, 35, 50, 255});
    
    // Draw next piece panel
    drawNextPanel(renderer, pixelFont, nextPanelTex, blocksTex, game, nextX, nextY, nextW, nextH, contentOffsetX, contentOffsetY, finalBlockSize);

    // Draw the game board
    const auto &board = game->boardRef();
    for (int y = 0; y < Game::ROWS; ++y) {
        for (int x = 0; x < Game::COLS; ++x) {
            int v = board[y * Game::COLS + x];
            if (v > 0) {
                float bx = gridX + x * finalBlockSize;
                float by = gridY + y * finalBlockSize;
                drawBlockTexture(renderer, blocksTex, bx, by, finalBlockSize, v - 1);
            }
        }
    }
    
    bool allowActivePieceRender = !game->isPaused() || suppressPauseVisuals;

    // Draw ghost piece (where current piece will land)
    if (allowActivePieceRender) {
        Game::Piece ghostPiece = game->current();
        // Find landing position
        while (true) {
            Game::Piece testPiece = ghostPiece;
            testPiece.y++;
            bool collision = false;
            
            // Simple collision check
            for (int cy = 0; cy < 4; ++cy) {
                for (int cx = 0; cx < 4; ++cx) {
                    if (Game::cellFilled(testPiece, cx, cy)) {
                        int gx = testPiece.x + cx;
                        int gy = testPiece.y + cy;
                        if (gy >= Game::ROWS || gx < 0 || gx >= Game::COLS || 
                            (gy >= 0 && board[gy * Game::COLS + gx] != 0)) {
                            collision = true;
                            break;
                        }
                    }
                }
                if (collision) break;
            }
            
            if (collision) break;
            ghostPiece = testPiece;
        }
        
        // Draw ghost piece
        drawPiece(renderer, blocksTex, ghostPiece, gridX, gridY, finalBlockSize, true);
    }
    
    // Draw the falling piece
    if (allowActivePieceRender) {
        drawPiece(renderer, blocksTex, game->current(), gridX, gridY, finalBlockSize, false);
    }
    
    // Draw line clearing effects
    if (lineEffect && lineEffect->isActive()) {
        lineEffect->render(renderer, static_cast<int>(gridX), static_cast<int>(gridY), static_cast<int>(finalBlockSize));
    }
                                float yCursor = statsY + 44.0f;
    // Draw next piece preview
    pixelFont->draw(renderer, nextX + 10, nextY - 20, "NEXT", 1.0f, {255, 220, 0, 255});
    if (game->next().type < PIECE_COUNT) {
        // Nudge preview slightly upward to remove thin bottom artifact
        drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(game->next().type), nextX + 10, nextY + 5, finalBlockSize * 0.6f);
    }
    
    // Draw block statistics (left panel)
    pixelFont->draw(renderer, statsX + 10, statsY + 10, "BLOCKS", 1.0f, {255, 220, 0, 255});
    
    const auto& blockCounts = game->getBlockCounts();
                                    float previewY = rowTop - 4.0f;
    for (int i = 0; i < PIECE_COUNT; ++i) totalBlocks += blockCounts[i];

    const float rowPadding = 18.0f;
    const float rowWidth = statsW - rowPadding * 2.0f;
    const float rowSpacing = 12.0f;
    float yCursor = statsY + 44.0f;

    for (int i = 0; i < PIECE_COUNT; ++i) {
        float rowTop = yCursor;
        float previewSize = finalBlockSize * 0.52f;
        float previewX = statsX + rowPadding;
        float previewY = rowTop - 14.0f;

        // Determine actual piece height so bars never overlap blocks
        Game::Piece previewPiece{};
        previewPiece.type = static_cast<PieceType>(i);
        previewPiece.rot = 0;
        previewPiece.x = 0;
        previewPiece.y = 0;
        int maxCy = -1;
        for (int cy = 0; cy < 4; ++cy) {
            for (int cx = 0; cx < 4; ++cx) {
                if (Game::cellFilled(previewPiece, cx, cy)) {
                    maxCy = std::max(maxCy, cy);
                }
            }
        }
        float pieceHeight = (maxCy >= 0 ? maxCy + 1.0f : 1.0f) * previewSize;

        int count = blockCounts[i];
        char countStr[16];
        snprintf(countStr, sizeof(countStr), "%d", count);
        int countW = 0, countH = 0;
        pixelFont->measure(countStr, 1.0f, countW, countH);
        // Horizontal shift to push the counts/percent a bit more to the right
        const float statsNumbersShift = 20.0f;
        // Small left shift for progress bar so the track aligns better with the design
        const float statsBarShift = -10.0f;
        float countX = previewX + rowWidth - static_cast<float>(countW) + statsNumbersShift;
        float countY = previewY + 9.0f;

        int perc = (totalBlocks > 0) ? int(std::round(100.0 * double(count) / double(totalBlocks))) : 0;
        char percStr[16];
        snprintf(percStr, sizeof(percStr), "%d%%", perc);
        int percW = 0, percH = 0;
        pixelFont->measure(percStr, 0.8f, percW, percH);

        float barX = previewX + statsBarShift;
        float barY = previewY + pieceHeight + 12.0f;
        float barH = 6.0f;
        float barW = rowWidth;
        float percY = barY + barH + 8.0f;
        float fillW = barW * (perc / 100.0f);
        fillW = std::clamp(fillW, 0.0f, barW);

        float cardTop = rowTop - 14.0f;
        float rowBottom = percY + 16.0f;
        SDL_FRect rowBg{
            previewX - 12.0f,
            cardTop,
            rowWidth + 24.0f,
            rowBottom - cardTop
        };
        SDL_SetRenderDrawColor(renderer, 18, 26, 40, 200);
        SDL_RenderFillRect(renderer, &rowBg);
        SDL_SetRenderDrawColor(renderer, 70, 100, 150, 210);
        SDL_RenderRect(renderer, &rowBg);

        drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(i), previewX, previewY, previewSize);
        pixelFont->draw(renderer, countX, countY, countStr, 1.0f, {245, 245, 255, 255});
        // Draw percent right-aligned near the same right edge as the count
        float percX = previewX + rowWidth - static_cast<float>(percW) + statsNumbersShift;
        pixelFont->draw(renderer, percX, percY, percStr, 0.8f, {215, 225, 240, 255});

        SDL_SetRenderDrawColor(renderer, 110, 120, 140, 200);
        SDL_FRect track{barX, barY, barW, barH};
        SDL_RenderFillRect(renderer, &track);
        SDL_Color pc = COLORS[i + 1];
        SDL_SetRenderDrawColor(renderer, pc.r, pc.g, pc.b, 255);
        SDL_FRect fill{barX, barY, fillW, barH};
        SDL_RenderFillRect(renderer, &fill);

        yCursor = rowBottom + rowSpacing;
    }
    
    // Draw score panel
    drawScorePanel(renderer, pixelFont, game, scoreX, gridY, GRID_H, finalBlockSize);
    
    // Gravity HUD
    char gms[64];
    double gms_val = game->getGravityMs();
    double gfps = gms_val > 0.0 ? (1000.0 / gms_val) : 0.0;
    snprintf(gms, sizeof(gms), "GRAV: %.0f ms (%.2f fps)", gms_val, gfps);
    pixelFont->draw(renderer, logicalW - 260, 10, gms, 0.9f, {200, 200, 220, 255});
    
    // Hold panel (always visible): draw background & label; preview shown only when a piece is held.
    {
        float holdBlockH = (finalBlockSize * 0.6f) * 4.0f;
        // Base panel height; enforce minimum but allow larger to fit texture
        float panelH = std::max(holdBlockH + 12.0f, 420.0f);
        // Increase height by ~20% of the hold block to give more vertical room
        float extraH = holdBlockH * 0.50f;
        panelH += extraH;
        const float holdGap = 18.0f;

        // Align X to the bottom score label (`scoreX`) plus an offset to the right
        float panelX = scoreX + 30.0f; // move ~30px right to align with score label
        float panelW = statsW + 32.0f;
        float panelY = gridY - panelH - holdGap;
        // Move panel a bit higher for spacing (about half the extra height)
        panelY -= extraH * 0.5f;
        float labelX = panelX + 40.0f; // shift HOLD label ~30px to the right
        float labelY = panelY + 8.0f;

        if (holdPanelTex) {
            int texW = 0, texH = 0;
            SDL_QueryTexture(holdPanelTex, nullptr, nullptr, &texW, &texH);
            if (texW > 0 && texH > 0) {
                // If the texture is taller than the current panel, expand panelH
                float texAspect = float(texH) / float(texW);
                float desiredTexH = panelW * texAspect;
                if (desiredTexH + 12.0f > panelH) {
                    panelH = desiredTexH + 12.0f;
                    // Recompute vertical placement after growing panelH
                    panelY = gridY - panelH - holdGap;
                    labelY = panelY + 8.0f;
                }

                // Fill panel width and compute destination height from texture aspect ratio
                float texAspect = float(texH) / float(texW);
                float dstW = panelW;
                float dstH = dstW * texAspect * 1.2f;
                // If texture height exceeds panel, expand panelH to fit texture comfortably
                if (dstH + 12.0f > panelH) {
                    panelH = dstH + 12.0f;
                    panelY = gridY - panelH - holdGap;
                    labelY = panelY + 8.0f;
                }
                float dstX = panelX;
                float dstY = panelY + (panelH - dstH) * 0.5f;

                SDL_FRect panelDst{dstX, dstY, dstW, dstH};
                SDL_SetTextureBlendMode(holdPanelTex, SDL_BLENDMODE_BLEND);
                SDL_SetTextureScaleMode(holdPanelTex, SDL_SCALEMODE_LINEAR);
                SDL_RenderTexture(renderer, holdPanelTex, nullptr, &panelDst);
            } else {
                // Fallback to filling panel area if texture metrics unavailable
                SDL_SetRenderDrawColor(renderer, 12, 18, 32, 220);
                SDL_FRect panelDst{panelX, panelY, panelW, panelH};
                SDL_RenderFillRect(renderer, &panelDst);
            }
        } else {
            SDL_SetRenderDrawColor(renderer, 12, 18, 32, 220);
            SDL_FRect panelDst{panelX, panelY, panelW, panelH};
            SDL_RenderFillRect(renderer, &panelDst);
        }

        pixelFont->draw(renderer, labelX, labelY, "HOLD", 1.0f, {255, 220, 0, 255});

        if (game->held().type < PIECE_COUNT) {
            float previewW = finalBlockSize * 0.6f * 4.0f;
            float previewX = panelX + (panelW - previewW) * 0.5f;
            float previewY = panelY + (panelH - holdBlockH) * 0.5f;
            drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(game->held().type), previewX, previewY, finalBlockSize * 0.6f);
        }
    }
    
    // Pause overlay (suppressed when requested, e.g., countdown)
    if (!suppressPauseVisuals && game->isPaused() && !showExitConfirmPopup) {
        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
        for (int i = -4; i <= 4; ++i) {
            float spread = static_cast<float>(std::abs(i));
            Uint8 alpha = Uint8(std::max(8.f, 32.f - spread * 4.f));
            SDL_SetRenderDrawColor(renderer, 24, 32, 48, alpha);
            SDL_FRect blurRect{
                gridX - spread * 2.0f,
                gridY - spread * 1.5f,
                GRID_W + spread * 4.0f,
                GRID_H + spread * 3.0f
            };
            SDL_RenderFillRect(renderer, &blurRect);
        }
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 180);
        SDL_FRect pauseOverlay{0, 0, logicalW, logicalH};
        SDL_RenderFillRect(renderer, &pauseOverlay);
        
        pixelFont->draw(renderer, logicalW * 0.5f - 80, logicalH * 0.5f - 20, "PAUSED", 2.0f, {255, 255, 255, 255});
        pixelFont->draw(renderer, logicalW * 0.5f - 120, logicalH * 0.5f + 30, "Press P to resume", 0.8f, {200, 200, 220, 255});
        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
    }
    
    if (showExitConfirmPopup) {
        SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 200);
        SDL_FRect fullWin{0.f, 0.f, winW, winH};
        SDL_RenderFillRect(renderer, &fullWin);

        const float panelW = 640.0f;
        const float panelH = 320.0f;
        SDL_FRect panel{
            (logicalW - panelW) * 0.5f + contentOffsetX,
            (logicalH - panelH) * 0.5f + contentOffsetY,
            panelW,
            panelH
        };

        SDL_FRect shadow{panel.x + 6.0f, panel.y + 10.0f, panel.w, panel.h};
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 140);
        SDL_RenderFillRect(renderer, &shadow);
        for (int i = 0; i < 5; ++i) {
            SDL_FRect glow{panel.x - float(i * 2), panel.y - float(i * 2), panel.w + float(i * 4), panel.h + float(i * 4)};
            SDL_SetRenderDrawColor(renderer, 0, 180, 255, Uint8(44 - i * 7));
            SDL_RenderRect(renderer, &glow);
        }

        SDL_SetRenderDrawColor(renderer, 18, 30, 52, 255);
        SDL_RenderFillRect(renderer, &panel);
        SDL_SetRenderDrawColor(renderer, 70, 120, 210, 255);
        SDL_RenderRect(renderer, &panel);

        SDL_FRect inner{panel.x + 24.0f, panel.y + 98.0f, panel.w - 48.0f, panel.h - 146.0f};
        SDL_SetRenderDrawColor(renderer, 16, 24, 40, 235);
        SDL_RenderFillRect(renderer, &inner);
        SDL_SetRenderDrawColor(renderer, 40, 80, 140, 235);
        SDL_RenderRect(renderer, &inner);

        const std::string title = "EXIT GAME?";
        int titleW = 0, titleH = 0;
        const float titleScale = 1.8f;
        pixelFont->measure(title, titleScale, titleW, titleH);
        pixelFont->draw(renderer, panel.x + (panel.w - titleW) * 0.5f, panel.y + 30.0f, title, titleScale, {255, 230, 140, 255});

        std::array<std::string, 2> lines = {
            "Are you sure you want to quit?",
            "Current progress will be lost."
        };
        float lineY = inner.y + 22.0f;
        const float lineScale = 1.05f;
        for (const auto& line : lines) {
            int lineW = 0, lineH = 0;
            pixelFont->measure(line, lineScale, lineW, lineH);
            float textX = panel.x + (panel.w - lineW) * 0.5f;
            pixelFont->draw(renderer, textX, lineY, line, lineScale, SDL_Color{210, 220, 240, 255});
            lineY += lineH + 10.0f;
        }

        const float horizontalPad = 28.0f;
        const float buttonGap = 32.0f;
        const float buttonH = 66.0f;
        float buttonW = (inner.w - horizontalPad * 2.0f - buttonGap) * 0.5f;
        float buttonY = inner.y + inner.h - buttonH - 24.0f;

        auto drawButton = [&](float x, const char* label, SDL_Color base) {
            SDL_FRect btn{x, buttonY, buttonW, buttonH};
            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 120);
            SDL_FRect btnShadow{btn.x + 4.0f, btn.y + 6.0f, btn.w, btn.h};
            SDL_RenderFillRect(renderer, &btnShadow);
            SDL_SetRenderDrawColor(renderer, base.r, base.g, base.b, base.a);
            SDL_RenderFillRect(renderer, &btn);
            SDL_SetRenderDrawColor(renderer, 90, 130, 200, 255);
            SDL_RenderRect(renderer, &btn);

            int textW = 0, textH = 0;
            const float labelScale = 1.4f;
            pixelFont->measure(label, labelScale, textW, textH);
            float textX = btn.x + (btn.w - textW) * 0.5f;
            float textY = btn.y + (btn.h - textH) * 0.5f;
            pixelFont->draw(renderer, textX, textY, label, labelScale, SDL_Color{255, 255, 255, 255});
        };

        float yesX = inner.x + horizontalPad;
        float noX = yesX + buttonW + buttonGap;
        drawButton(yesX, "YES", SDL_Color{185, 70, 70, 255});
        drawButton(noX, "NO", SDL_Color{60, 95, 150, 255});
    }
}
