package com.pixelmintdrop.game

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.BlurMaskFilter
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.Shader
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.animation.LinearInterpolator
import android.hardware.display.DisplayManager
import android.view.Display
import com.pixelmintdrop.model.GameBoard
import com.pixelmintdrop.model.Tetromino
import com.pixelmintdrop.model.TetrominoType
import kotlin.math.abs
import kotlin.math.min

/**
 * GameView that renders the Tetris game and handles touch input
 */
class GameView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    companion object {
        private const val TAG = "GameView"
    }

    // Game board model
    private var gameBoard = GameBoard()
    private var gameHaptics: GameHaptics? = null
    
    // Game state
    private var isRunning = false
    var isPaused = false  // Changed from private to public to allow access from MainActivity
    private var score = 0
    
    // Callbacks
    var onNextPieceChanged: (() -> Unit)? = null
    
    // Rendering
    private val blockPaint = Paint().apply {
        color = Color.WHITE
        isAntiAlias = true
    }
    
    private val borderGlowPaint = Paint().apply {
        color = Color.WHITE
        alpha = 90 // Increased from 60
        isAntiAlias = true
        style = Paint.Style.STROKE
        strokeWidth = 2f
        maskFilter = BlurMaskFilter(12f, BlurMaskFilter.Blur.OUTER) // Increased from 8f
    }
    
    private val ghostBlockPaint = Paint().apply {
        color = Color.WHITE
        alpha = 80  // 30% opacity
        isAntiAlias = true
    }
    
    private val gridPaint = Paint().apply {
        color = Color.parseColor("#222222")  // Very dark gray
        alpha = 20  // Reduced from 40 to be more subtle
        isAntiAlias = true
        strokeWidth = 1f
        style = Paint.Style.STROKE
        maskFilter = null  // Ensure no blur effect on grid lines
    }
    
    private val glowPaint = Paint().apply {
        color = Color.WHITE
        alpha = 40  // Reduced from 80 for more subtlety
        isAntiAlias = true
        style = Paint.Style.STROKE
        strokeWidth = 1.5f
        maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.OUTER)
    }
    
    private val blockGlowPaint = Paint().apply {
        color = Color.WHITE
        alpha = 60
        isAntiAlias = true
        style = Paint.Style.FILL
        maskFilter = BlurMaskFilter(12f, BlurMaskFilter.Blur.OUTER)
    }
    
    // Add a new paint for the pulse effect
    private val pulsePaint = Paint().apply {
        color = Color.CYAN
        alpha = 255
        isAntiAlias = true
        style = Paint.Style.FILL
        maskFilter = BlurMaskFilter(32f, BlurMaskFilter.Blur.OUTER)  // Increased from 16f to 32f
    }
    
    // Pre-allocate paint objects to avoid GC
    private val tmpPaint = Paint()
    
    // Calculate block size based on view dimensions and board size
    private var blockSize = 0f
    private var boardLeft = 0f
    private var boardTop = 0f
    
    // Game loop handler and runnable
    private val handler = Handler(Looper.getMainLooper())
    private val gameLoopRunnable = object : Runnable {
        override fun run() {
            if (isRunning && !isPaused) {
                update()
                invalidate()
                handler.postDelayed(this, gameBoard.dropInterval)
            }
        }
    }
    
    // Touch parameters
    private var lastTouchX = 0f
    private var lastTouchY = 0f
    private var startX = 0f
    private var startY = 0f
    private var lastTapTime = 0L
    private var lastRotationTime = 0L
    private var lastMoveTime = 0L
    private var lastHardDropTime = 0L  // Track when the last hard drop occurred
    private val hardDropCooldown = 250L  // Reduced from 500ms to 250ms
    private var touchFreezeUntil = 0L  // Time until which touch events should be ignored
    private val pieceLockFreezeTime = 300L  // Time to freeze touch events after piece locks
    private var minSwipeVelocity = 1200  // Increased from 800 to require more deliberate swipes
    private val maxTapMovement = 30f    // Increased from 20f to 30f for more lenient tap detection
    private val minTapTime = 100L       // Minimum time for a tap (in milliseconds)
    private val rotationCooldown = 100L // Reduced from 150L to allow faster rotation taps
    private val moveCooldown = 50L      // Minimum time between move haptics (in milliseconds)
    private val doubleTapTimeout = 400L // Increased from 300ms to 400ms for more lenient double tap detection
    private var lastTapX = 0f           // X coordinate of last tap
    private var lastTapY = 0f           // Y coordinate of last tap
    private var lastHoldTime = 0L       // Track when the last hold occurred
    private val holdCooldown = 250L     // Minimum time between holds
    private var lockedDirection: Direction? = null  // Track the locked movement direction
    private val minMovementThreshold = 0.3f  // Reduced from 0.5f for more sensitive horizontal movement
    private val directionLockThreshold = 1.0f  // Reduced from 1.5f to make direction locking less aggressive
    private val isStrictDirectionLock = true   // Re-enabled strict direction locking to prevent diagonal inputs
    private val minHardDropDistance = 2.5f     // Increased from 1.5f to require more deliberate hard drops
    private val minHoldDistance = 2.0f         // Minimum distance (in blocks) for hold gesture
    private val maxSoftDropDistance = 1.5f     // Maximum distance for soft drop before considering hard drop

    // Block skin
    private var currentBlockSkin: String = "block_skin_1"
    private val blockSkinPaints = mutableMapOf<String, Paint>()
    private var currentThemeColor = Color.WHITE
    
    private enum class Direction {
        HORIZONTAL, VERTICAL
    }
    
    // Callback for game events
    var onGameStateChanged: ((score: Int, level: Int, lines: Int) -> Unit)? = null
    var onGameOver: ((score: Int) -> Unit)? = null
    var onLineClear: ((Int) -> Unit)? = null // New callback for line clear events
    var onPieceMove: (() -> Unit)? = null // New callback for piece movement
    var onPieceLock: (() -> Unit)? = null // New callback for piece locking
    
    // Animation state
    private var pulseAnimator: ValueAnimator? = null
    private var pulseAlpha = 0f
    private var isPulsing = false
    private var linesToPulse = mutableListOf<Int>()  // Track which lines are being cleared
    private var gameOverAnimator: ValueAnimator? = null
    private var gameOverAlpha = 0f
    private var isGameOverAnimating = false
    // Add new game over animation properties
    private var gameOverBlocksY = mutableListOf<Float>()
    private var gameOverBlocksX = mutableListOf<Float>()
    private var gameOverBlocksRotation = mutableListOf<Float>()
    private var gameOverBlocksSpeed = mutableListOf<Float>()
    private var gameOverBlocksSize = mutableListOf<Float>()
    private var gameOverColorTransition = 0f
    private var gameOverShakeAmount = 0f
    private var gameOverFinalAlpha = 0.8f
    
    private val ghostPaint = Paint().apply {
        style = Paint.Style.STROKE
        strokeWidth = 2f
        color = Color.WHITE
        alpha = 180  // Increased from 100 for better visibility
    }

    private val ghostBackgroundPaint = Paint().apply {
        style = Paint.Style.FILL
        color = Color.WHITE
        alpha = 30  // Very light background for better contrast
    }

    private val ghostBorderPaint = Paint().apply {
        style = Paint.Style.STROKE
        strokeWidth = 1f
        color = Color.WHITE
        alpha = 100  // Subtle border for better definition
    }
    
    init {
        // Start with paused state
        pause()
        
        // Load saved block skin
        val prefs = context.getSharedPreferences("pixelmintdrop_progression", Context.MODE_PRIVATE)
        currentBlockSkin = prefs.getString("selected_block_skin", "block_skin_1") ?: "block_skin_1"
        
        // Connect our callbacks to the GameBoard
        gameBoard.onPieceMove = { onPieceMove?.invoke() }
        gameBoard.onPieceLock = { 
            // Freeze touch events for a brief period after a piece locks
            touchFreezeUntil = System.currentTimeMillis() + pieceLockFreezeTime
            Log.d(TAG, "Piece locked - freezing touch events until ${touchFreezeUntil}")
            onPieceLock?.invoke() 
        }
        gameBoard.onLineClear = { lineCount, clearedLines -> 
            Log.d(TAG, "Received line clear from GameBoard: $lineCount lines")
            try {
                onLineClear?.invoke(lineCount)
                // Use the lines that were cleared directly
                linesToPulse.clear()
                linesToPulse.addAll(clearedLines)
                Log.d(TAG, "Found ${linesToPulse.size} lines to pulse")
                startPulseAnimation(lineCount)
                Log.d(TAG, "Forwarded line clear callback")
            } catch (e: Exception) {
                Log.e(TAG, "Error forwarding line clear callback", e)
            }
        }
        
        // Force hardware acceleration - This is critical for performance
        setLayerType(LAYER_TYPE_HARDWARE, null)
        
        // Set better frame rate using modern APIs
        val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
        val display = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            displayManager.getDisplay(Display.DEFAULT_DISPLAY)
        } else {
            displayManager.displays.firstOrNull()
        }
        display?.let { disp ->
            val refreshRate = disp.refreshRate
            // Set game loop interval based on refresh rate, but don't go faster than the base interval
            val targetFps = refreshRate.toInt()
            if (targetFps > 0) {
                gameBoard.dropInterval = gameBoard.dropInterval.coerceAtMost(1000L / targetFps)
            }
        }
        
        // Enable edge-to-edge rendering
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            setSystemGestureExclusionRects(listOf(Rect(0, 0, width, height)))
        }
        
        // Initialize block skin paints
        initializeBlockSkinPaints()
    }
    
    /**
     * Initialize paints for different block skins
     */
    private fun initializeBlockSkinPaints() {
        // Classic skin
        blockSkinPaints["block_skin_1"] = Paint().apply {
            color = Color.WHITE
            isAntiAlias = true
            style = Paint.Style.FILL
        }
        
        // Neon skin
        blockSkinPaints["block_skin_2"] = Paint().apply {
            color = Color.parseColor("#FF00FF")
            isAntiAlias = true
            maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.OUTER)
        }
        
        // Retro skin
        blockSkinPaints["block_skin_3"] = Paint().apply {
            color = Color.parseColor("#FF5A5F")
            isAntiAlias = false  // Pixelated look
            style = Paint.Style.FILL
        }
        
        // Minimalist skin
        blockSkinPaints["block_skin_4"] = Paint().apply {
            color = Color.BLACK
            isAntiAlias = true
            style = Paint.Style.FILL
        }
        
        // Galaxy skin
        blockSkinPaints["block_skin_5"] = Paint().apply {
            color = Color.parseColor("#66FCF1")
            isAntiAlias = true
            maskFilter = BlurMaskFilter(12f, BlurMaskFilter.Blur.OUTER)
        }
    }
    
    /**
     * Set the current block skin
     */
    fun setBlockSkin(skinId: String) {
        Log.d("BlockSkin", "Setting block skin from: $currentBlockSkin to: $skinId")
        
        // Check if the skin exists in our map
        if (!blockSkinPaints.containsKey(skinId)) {
            Log.e("BlockSkin", "Warning: Unknown block skin: $skinId - available skins: ${blockSkinPaints.keys}")
            // Fall back to default skin if the requested one doesn't exist
            if (blockSkinPaints.containsKey("block_skin_1")) {
                currentBlockSkin = "block_skin_1"
            }
        } else {
            // Set the skin
            currentBlockSkin = skinId
        }
        
        // Save the selection to SharedPreferences
        val prefs = context.getSharedPreferences("pixelmintdrop_progression", Context.MODE_PRIVATE)
        prefs.edit().putString("selected_block_skin", skinId).commit()
        
        // Force a refresh of the view
        invalidate()
        
        // Log confirmation
        Log.d("BlockSkin", "Block skin is now: $currentBlockSkin")
    }
    
    /**
     * Get the current block skin
     */
    fun getCurrentBlockSkin(): String = currentBlockSkin
    
    /**
     * Start the game
     */
    fun start() {
        // Reset game over animation state
        isGameOverAnimating = false
        gameOverAlpha = 0f
        gameOverBlocksY.clear()
        gameOverBlocksX.clear()
        gameOverBlocksRotation.clear()
        gameOverBlocksSpeed.clear()
        gameOverBlocksSize.clear()
        gameOverColorTransition = 0f
        gameOverShakeAmount = 0f
        
        isPaused = false
        isRunning = true
        gameBoard.startGame()  // Add this line to ensure a new piece is spawned
        handler.post(gameLoopRunnable)
        invalidate()
    }
    
    /**
     * Pause the game
     */
    fun pause() {
        isPaused = true
        handler.removeCallbacks(gameLoopRunnable)
        invalidate()
    }
    
    /**
     * Reset the game
     */
    fun reset() {
        isRunning = false
        isPaused = true
        
        // Reset game over animation state
        isGameOverAnimating = false
        gameOverAlpha = 0f
        gameOverBlocksY.clear()
        gameOverBlocksX.clear()
        gameOverBlocksRotation.clear()
        gameOverBlocksSpeed.clear()
        gameOverBlocksSize.clear()
        gameOverColorTransition = 0f
        gameOverShakeAmount = 0f
        
        gameBoard.reset()
        gameBoard.startGame()  // Add this line to ensure a new piece is spawned
        handler.removeCallbacks(gameLoopRunnable)
        invalidate()
    }
    
    /**
     * Update game state (called on game loop)
     */
    private fun update() {
        if (gameBoard.isGameOver) {
            // Only trigger game over handling once when transitioning to game over state
            if (isRunning) {
                Log.d(TAG, "Game has ended - transitioning to game over state")
                isRunning = false
                isPaused = true
                
                // Always trigger animation for each game over
                Log.d(TAG, "Triggering game over animation from update()")
                startGameOverAnimation()
                onGameOver?.invoke(gameBoard.score)
            }
            return
        }
        
        // Update the game state
        gameBoard.update()
        
        // Update UI with current game state
        onGameStateChanged?.invoke(gameBoard.score, gameBoard.level, gameBoard.lines)
    }
    
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        
        // Force hardware acceleration - Critical for performance
        setLayerType(LAYER_TYPE_HARDWARE, null)
        
        // Update gesture exclusion rect for edge-to-edge rendering
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            setSystemGestureExclusionRects(listOf(Rect(0, 0, w, h)))
        }
        
        calculateDimensions(w, h)
    }
    
    /**
     * Calculate dimensions for the board and blocks based on view size
     */
    private fun calculateDimensions(width: Int, height: Int) {
        val horizontalBlocks = gameBoard.width
        val verticalBlocks = gameBoard.height
        // Go back to zero padding for flush border
        val borderPadding = 0f

        // Calculate available drawing area (entire view)
        val availableWidth = width.toFloat() - (borderPadding * 2)
        val availableHeight = height.toFloat() - (borderPadding * 2)

        // Calculate potential block sizes based on fitting width and height separately
        val blockSizeBasedOnWidth = availableWidth / horizontalBlocks
        val blockSizeBasedOnHeight = availableHeight / verticalBlocks

        // Use the smaller block size to ensure the entire board fits within the padded area
        blockSize = minOf(blockSizeBasedOnWidth, blockSizeBasedOnHeight)

        // Calculate the final dimensions of the board using the determined block size
        val finalBoardWidth = blockSize * horizontalBlocks
        val finalBoardHeight = blockSize * verticalBlocks

        // Center the final board area within the entire view
        boardLeft = (width.toFloat() - finalBoardWidth) / 2
        boardTop = (height.toFloat() - finalBoardHeight) / 2

        // Log dimensions for debugging
        Log.d(TAG, "Board dimensions (Small Padding, Centered): view($width, $height), block($blockSize), board($finalBoardWidth, $finalBoardHeight), pos($boardLeft, $boardTop)")
    }
    
    override fun onDraw(canvas: Canvas) {
        // Set hardware layer type during draw for better performance
        val wasHardwareAccelerated = isHardwareAccelerated
        if (!wasHardwareAccelerated) {
            setLayerType(LAYER_TYPE_HARDWARE, null)
        }
        
        super.onDraw(canvas)
        
        // Draw background (already black from theme)
        
        // Draw board border glow
        drawBoardBorder(canvas)
        
        // Draw grid (very subtle)
        drawGrid(canvas)
        
        // Draw locked pieces
        drawLockedBlocks(canvas)
        
        if (!gameBoard.isGameOver && isRunning) {
            // Draw ghost piece (landing preview)
            drawGhostPiece(canvas)
            
            // Draw active piece
            drawActivePiece(canvas)
        }
        
        // Draw game over effect if animating
        if (isGameOverAnimating) {
            // First layer - full screen glow
            val gameOverPaint = Paint().apply {
                color = Color.RED  // Change to red for more striking game over indication
                alpha = (230 * gameOverAlpha).toInt()  // Increased opacity
                isAntiAlias = true
                style = Paint.Style.FILL
                // Only apply blur if alpha is greater than 0
                if (gameOverAlpha > 0) {
                    maskFilter = BlurMaskFilter(64f * gameOverAlpha, BlurMaskFilter.Blur.OUTER)
                }
            }
            
            // Apply screen shake if active
            if (gameOverShakeAmount > 0) {
                canvas.save()
                val shakeOffsetX = (Math.random() * 2 - 1) * gameOverShakeAmount * 20  // Doubled for more visible shake
                val shakeOffsetY = (Math.random() * 2 - 1) * gameOverShakeAmount * 20
                canvas.translate(shakeOffsetX.toFloat(), shakeOffsetY.toFloat())
            }
            
            canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), gameOverPaint)
            
            // Second layer - color transition effect
            val gameOverPaint2 = Paint().apply {
                // Transition from bright red to theme color
                val transitionColor = if (gameOverColorTransition < 0.5f) {
                    Color.RED
                } else {
                    val transition = (gameOverColorTransition - 0.5f) * 2f
                    val red = Color.red(currentThemeColor)
                    val green = Color.green(currentThemeColor)
                    val blue = Color.blue(currentThemeColor)
                    Color.argb(
                        (150 * gameOverAlpha).toInt(),  // Increased opacity
                        (255 - (255-red) * transition).toInt(),
                        (green * transition).toInt(),  // Transition from 0 (red) to theme green
                        (blue * transition).toInt()   // Transition from 0 (red) to theme blue
                    )
                }
                color = transitionColor
                alpha = (150 * gameOverAlpha).toInt()  // Increased opacity
                isAntiAlias = true
                style = Paint.Style.FILL
                // Only apply blur if alpha is greater than 0
                if (gameOverAlpha > 0) {
                    maskFilter = BlurMaskFilter(48f * gameOverAlpha, BlurMaskFilter.Blur.OUTER)  // Increased blur
                }
            }
            canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), gameOverPaint2)
            
            // Draw "GAME OVER" text
            if (gameOverAlpha > 0.5f) {
                val textPaint = Paint().apply {
                    color = Color.WHITE
                    alpha = (255 * Math.min(1f, (gameOverAlpha - 0.5f) * 2)).toInt()
                    isAntiAlias = true
                    textSize = blockSize * 1.5f  // Reduced from 2f to 1.5f to fit on screen
                    textAlign = Paint.Align.CENTER
                    typeface = android.graphics.Typeface.DEFAULT_BOLD
                    style = Paint.Style.FILL_AND_STROKE
                    strokeWidth = blockSize * 0.08f  // Proportionally reduced from 0.1f
                }
                
                // Draw text with glow
                val glowPaint = Paint(textPaint).apply {
                    maskFilter = BlurMaskFilter(blockSize * 0.4f, BlurMaskFilter.Blur.NORMAL)  // Reduced from 0.5f
                    alpha = (200 * Math.min(1f, (gameOverAlpha - 0.5f) * 2)).toInt()
                    color = Color.RED
                    strokeWidth = blockSize * 0.15f  // Reduced from 0.2f
                }
                
                val xPos = width / 2f
                val yPos = height / 3f
                
                // Measure text width to check if it fits
                val textWidth = textPaint.measureText("GAME OVER")
                
                // If text would still be too wide, scale it down further
                if (textWidth > width * 0.9f) {
                    val scaleFactor = width * 0.9f / textWidth
                    textPaint.textSize *= scaleFactor
                    glowPaint.textSize *= scaleFactor
                }
                
                canvas.drawText("GAME OVER", xPos, yPos, glowPaint)
                canvas.drawText("GAME OVER", xPos, yPos, textPaint)
            }
            
            // Draw falling blocks
            if (gameOverBlocksY.isNotEmpty()) {
                for (i in gameOverBlocksY.indices) {
                    val x = gameOverBlocksX[i]
                    val y = gameOverBlocksY[i]
                    val rotation = gameOverBlocksRotation[i]
                    val size = gameOverBlocksSize[i] * blockSize
                    
                    // Skip blocks that have fallen off the screen
                    if (y > height) continue
                    
                    // Draw each falling block with rotation
                    canvas.save()
                    canvas.translate(x, y)
                    canvas.rotate(rotation)
                    
                    // Create a pulsing effect for the falling blocks
                    val blockPaint = Paint(blockPaint)
                    blockPaint.alpha = (255 * gameOverAlpha * (1.0f - y / height.toFloat() * 0.7f)).toInt()
                    
                    // Draw block with glow effect
                    val blockGlowPaint = Paint(blockGlowPaint)
                    blockGlowPaint.alpha = (200 * gameOverAlpha * (1.0f - y / height.toFloat() * 0.5f)).toInt()
                    canvas.drawRect(-size/2, -size/2, size/2, size/2, blockGlowPaint)
                    canvas.drawRect(-size/2, -size/2, size/2, size/2, blockPaint)
                    
                    canvas.restore()
                }
            }
            
            // Reset any transformations from screen shake
            if (gameOverShakeAmount > 0) {
                canvas.restore()
            }
        }
    }
    
    /**
     * Draw glowing border around the playable area
     */
    private fun drawBoardBorder(canvas: Canvas) {
        val left = boardLeft
        val top = boardTop
        val right = boardLeft + gameBoard.width * blockSize
        val bottom = boardTop + gameBoard.height * blockSize
        
        val rect = RectF(left, top, right, bottom)
        
        // Draw base border with updated glow properties from the paint object
        canvas.drawRect(rect, borderGlowPaint)

        // Draw a sharp inner line for a flush appearance
        val sharpBorderPaint = Paint().apply {
            color = Color.WHITE
            alpha = 90 // Slightly less intense than glow
            isAntiAlias = true
            style = Paint.Style.STROKE
            strokeWidth = 1f // Very thin line
            maskFilter = null // No blur
        }
        canvas.drawRect(rect, sharpBorderPaint)
        
        // Draw pulsing border if animation is active
        if (isPulsing) {
            val pulseBorderPaint = Paint().apply {
                color = Color.WHITE
                style = Paint.Style.STROKE
                strokeWidth = 6f + (16f * pulseAlpha)  // Increased from 4f+12f to 6f+16f
                alpha = (255 * pulseAlpha).toInt()
                isAntiAlias = true
                maskFilter = BlurMaskFilter(32f * (1f + pulseAlpha), BlurMaskFilter.Blur.OUTER)  // Increased from 24f to 32f
            }
            // Draw the border with a slight inset to prevent edge artifacts
            val inset = 1f
            canvas.drawRect(
                left + inset,
                top + inset,
                right - inset,
                bottom - inset,
                pulseBorderPaint
            )
            
            // Add an additional outer glow for more dramatic effect
            val outerGlowPaint = Paint().apply {
                color = Color.WHITE
                style = Paint.Style.STROKE
                strokeWidth = 2f
                alpha = (128 * pulseAlpha).toInt()
                isAntiAlias = true
                maskFilter = BlurMaskFilter(48f * (1f + pulseAlpha), BlurMaskFilter.Blur.OUTER)
            }
            canvas.drawRect(
                left - 4f,
                top - 4f,
                right + 4f,
                bottom + 4f,
                outerGlowPaint
            )

            // Add extra bright glow for side borders during line clear
            val sideGlowPaint = Paint().apply {
                color = Color.WHITE
                style = Paint.Style.STROKE
                strokeWidth = 8f + (24f * pulseAlpha)  // Thicker stroke for side borders
                alpha = (255 * pulseAlpha).toInt()
                isAntiAlias = true
                maskFilter = BlurMaskFilter(64f * (1f + pulseAlpha), BlurMaskFilter.Blur.OUTER)  // Larger blur for side borders
            }
            
            // Draw left border with extra glow
            canvas.drawLine(
                left + inset,
                top + inset,
                left + inset,
                bottom - inset,
                sideGlowPaint
            )
            
            // Draw right border with extra glow
            canvas.drawLine(
                right - inset,
                top + inset,
                right - inset,
                bottom - inset,
                sideGlowPaint
            )
        }
    }
    
    /**
     * Draw the grid lines (very subtle)
     */
    private fun drawGrid(canvas: Canvas) {
        // Save the canvas state to prevent any effects from affecting the grid
        canvas.save()
        
        // Draw vertical grid lines
        for (x in 0..gameBoard.width) {
            val xPos = boardLeft + x * blockSize
            canvas.drawLine(
                xPos, boardTop,
                xPos, boardTop + gameBoard.height * blockSize,
                gridPaint
            )
        }
        
        // Draw horizontal grid lines
        for (y in 0..gameBoard.height) {
            val yPos = boardTop + y * blockSize
            canvas.drawLine(
                boardLeft, yPos,
                boardLeft + gameBoard.width * blockSize, yPos,
                gridPaint
            )
        }
        
        // Restore the canvas state
        canvas.restore()
    }
    
    /**
     * Draw the locked blocks on the board
     */
    private fun drawLockedBlocks(canvas: Canvas) {
        for (y in 0 until gameBoard.height) {
            for (x in 0 until gameBoard.width) {
                if (gameBoard.isOccupied(x, y)) {
                    drawBlock(canvas, x, y, false, y in linesToPulse)
                }
            }
        }
    }
    
    /**
     * Draw the currently active tetromino
     */
    private fun drawActivePiece(canvas: Canvas) {
        val piece = gameBoard.getCurrentPiece() ?: return
        
        for (y in 0 until piece.getHeight()) {
            for (x in 0 until piece.getWidth()) {
                if (piece.isBlockAt(x, y)) {
                    val boardX = piece.x + x
                    val boardY = piece.y + y
                    
                    // Draw piece regardless of vertical position
                    if (boardX >= 0 && boardX < gameBoard.width) {
                        drawBlock(canvas, boardX, boardY, false, false)
                    }
                }
            }
        }
    }
    
    /**
     * Draw the ghost piece (landing preview)
     */
    private fun drawGhostPiece(canvas: Canvas) {
        val piece = gameBoard.getCurrentPiece() ?: return
        val ghostY = gameBoard.getGhostY()
        
        // Draw semi-transparent background for each block
        for (y in 0 until piece.getHeight()) {
            for (x in 0 until piece.getWidth()) {
                if (piece.isBlockAt(x, y)) {
                    val boardX = piece.x + x
                    val boardY = ghostY + y
                    
                    if (boardX >= 0 && boardX < gameBoard.width) {
                        val screenX = boardLeft + boardX * blockSize
                        val screenY = boardTop + boardY * blockSize
                        
                        // Draw background
                        canvas.drawRect(
                            screenX + 1f,
                            screenY + 1f,
                            screenX + blockSize - 1f,
                            screenY + blockSize - 1f,
                            ghostBackgroundPaint
                        )
                        
                        // Draw border
                        canvas.drawRect(
                            screenX + 1f,
                            screenY + 1f,
                            screenX + blockSize - 1f,
                            screenY + blockSize - 1f,
                            ghostBorderPaint
                        )
                        
                        // Draw outline
                        canvas.drawRect(
                            screenX + 1f,
                            screenY + 1f,
                            screenX + blockSize - 1f,
                            screenY + blockSize - 1f,
                            ghostPaint
                        )
                    }
                }
            }
        }
    }
    
    /**
     * Draw a single tetris block at the given grid position
     */
    private fun drawBlock(canvas: Canvas, x: Int, y: Int, isGhost: Boolean, isPulsingLine: Boolean) {
        val left = boardLeft + x * blockSize
        val top = boardTop + y * blockSize
        val right = left + blockSize
        val bottom = top + blockSize
        
        // Save canvas state before drawing block effects
        canvas.save()
        
        // Get the current block skin paint
        val paint = blockSkinPaints[currentBlockSkin] ?: blockSkinPaints["block_skin_1"]!!
        
        // Create a clone of the paint to avoid modifying the original
        val blockPaint = Paint(paint)
        
        // Draw block based on current skin
        when (currentBlockSkin) {
            "block_skin_1" -> {  // Classic
                // Draw outer glow
                blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
                canvas.drawRect(left - 2f, top - 2f, right + 2f, bottom + 2f, blockGlowPaint)
                
                // Draw block
                blockPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
                blockPaint.alpha = if (isGhost) 30 else 255
                canvas.drawRect(left, top, right, bottom, blockPaint)
                
                // Draw inner glow
                glowPaint.color = if (isGhost) Color.argb(30, 255, 255, 255) else Color.WHITE
                canvas.drawRect(left + 1f, top + 1f, right - 1f, bottom - 1f, glowPaint)
            }
            "block_skin_2" -> {  // Neon
                // Stronger outer glow for neon skin
                blockGlowPaint.color = if (isGhost) Color.argb(30, 255, 0, 255) else Color.parseColor("#FF00FF")
                blockGlowPaint.maskFilter = BlurMaskFilter(16f, BlurMaskFilter.Blur.OUTER)
                canvas.drawRect(left - 4f, top - 4f, right + 4f, bottom + 4f, blockGlowPaint)
                
                // For neon, use semi-translucent fill with strong glowing edges
                blockPaint.style = Paint.Style.FILL_AND_STROKE
                blockPaint.strokeWidth = 2f
                blockPaint.maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL)
                
                if (isGhost) {
                    blockPaint.color = Color.argb(30, 255, 0, 255)
                    blockPaint.alpha = 30
                } else {
                    blockPaint.color = Color.parseColor("#66004D") // Darker magenta fill
                    blockPaint.alpha = 170  // More opaque to be more visible
                }
                
                // Draw block with neon effect
                canvas.drawRect(left, top, right, bottom, blockPaint)
                
                // Draw a brighter border for better visibility
                val borderPaint = Paint().apply {
                    color = Color.parseColor("#FF00FF")
                    style = Paint.Style.STROKE
                    strokeWidth = 3f
                    alpha = 255
                    isAntiAlias = true
                    maskFilter = BlurMaskFilter(6f, BlurMaskFilter.Blur.NORMAL)
                }
                canvas.drawRect(left, top, right, bottom, borderPaint)
                
                // Inner glow for neon blocks
                glowPaint.color = if (isGhost) Color.argb(10, 255, 0, 255) else Color.parseColor("#FF00FF")
                glowPaint.alpha = if (isGhost) 10 else 100
                glowPaint.style = Paint.Style.STROKE
                glowPaint.strokeWidth = 2f
                glowPaint.maskFilter = BlurMaskFilter(4f, BlurMaskFilter.Blur.NORMAL)
                canvas.drawRect(left + 4f, top + 4f, right - 4f, bottom - 4f, glowPaint)
            }
            "block_skin_3" -> {  // Retro
                // Draw pixelated block with retro effect
                blockPaint.color = if (isGhost) Color.argb(30, 255, 90, 95) else Color.parseColor("#FF5A5F")
                blockPaint.alpha = if (isGhost) 30 else 255
                
                // Draw main block
                canvas.drawRect(left, top, right, bottom, blockPaint)
                
                // Draw pixelated highlights
                val highlightPaint = Paint().apply {
                    color = Color.parseColor("#FF8A8F")
                    isAntiAlias = false
                    style = Paint.Style.FILL
                }
                // Top and left highlights
                canvas.drawRect(left, top, right - 2f, top + 2f, highlightPaint)
                canvas.drawRect(left, top, left + 2f, bottom - 2f, highlightPaint)
                
                // Draw pixelated shadows
                val shadowPaint = Paint().apply {
                    color = Color.parseColor("#CC4A4F")
                    isAntiAlias = false
                    style = Paint.Style.FILL
                }
                // Bottom and right shadows
                canvas.drawRect(left + 2f, bottom - 2f, right, bottom, shadowPaint)
                canvas.drawRect(right - 2f, top + 2f, right, bottom - 2f, shadowPaint)
            }
            "block_skin_4" -> {  // Minimalist
                // Draw clean, simple block with subtle border
                blockPaint.color = if (isGhost) Color.argb(30, 0, 0, 0) else Color.BLACK
                blockPaint.alpha = if (isGhost) 30 else 255
                blockPaint.style = Paint.Style.FILL
                canvas.drawRect(left, top, right, bottom, blockPaint)
                
                // Draw subtle border
                val borderPaint = Paint().apply {
                    color = Color.parseColor("#333333")
                    style = Paint.Style.STROKE
                    strokeWidth = 1f
                    isAntiAlias = true
                }
                canvas.drawRect(left, top, right, bottom, borderPaint)
            }
            "block_skin_5" -> {  // Galaxy
                // Draw cosmic glow effect
                blockGlowPaint.color = if (isGhost) Color.argb(30, 102, 252, 241) else Color.parseColor("#66FCF1")
                blockGlowPaint.maskFilter = BlurMaskFilter(20f, BlurMaskFilter.Blur.OUTER)
                canvas.drawRect(left - 8f, top - 8f, right + 8f, bottom + 8f, blockGlowPaint)
                
                // Draw main block with gradient
                val gradient = LinearGradient(
                    left, top, right, bottom,
                    Color.parseColor("#66FCF1"),
                    Color.parseColor("#45B7AF"),
                    Shader.TileMode.CLAMP
                )
                blockPaint.shader = gradient
                blockPaint.color = if (isGhost) Color.argb(30, 102, 252, 241) else Color.parseColor("#66FCF1")
                blockPaint.alpha = if (isGhost) 30 else 255
                blockPaint.style = Paint.Style.FILL
                canvas.drawRect(left, top, right, bottom, blockPaint)
                
                // Draw star-like sparkles
                if (!isGhost) {
                    val sparklePaint = Paint().apply {
                        color = Color.WHITE
                        style = Paint.Style.FILL
                        isAntiAlias = true
                        maskFilter = BlurMaskFilter(4f, BlurMaskFilter.Blur.NORMAL)
                    }
                    // Add small white dots for sparkle effect
                    canvas.drawCircle(left + 4f, top + 4f, 1f, sparklePaint)
                    canvas.drawCircle(right - 4f, bottom - 4f, 1f, sparklePaint)
                }
            }
        }
        
        // Draw pulse effect if animation is active and this is a pulsing line
        if (isPulsing && isPulsingLine) {
            val pulseBlockPaint = Paint().apply {
                color = Color.WHITE
                alpha = (255 * pulseAlpha).toInt()
                isAntiAlias = true
                style = Paint.Style.FILL
                maskFilter = BlurMaskFilter(40f * (1f + pulseAlpha), BlurMaskFilter.Blur.OUTER)
            }
            canvas.drawRect(left - 16f, top - 16f, right + 16f, bottom + 16f, pulseBlockPaint)
        }
        
        // Restore canvas state after drawing block effects
        canvas.restore()
    }
    
    /**
     * Check if the given board position is part of the current piece
     */
    private fun isPositionInPiece(boardX: Int, boardY: Int, piece: Tetromino): Boolean {
        for (y in 0 until piece.getHeight()) {
            for (x in 0 until piece.getWidth()) {
                if (piece.isBlockAt(x, y)) {
                    val pieceX = piece.x + x
                    val pieceY = piece.y + y
                    if (pieceX == boardX && pieceY == boardY) {
                        return true
                    }
                }
            }
        }
        return false
    }
    
    /**
     * Get color for tetromino type
     */
    private fun getTetrominoColor(type: TetrominoType): Int {
        return when (type) {
            TetrominoType.I -> Color.CYAN
            TetrominoType.J -> Color.BLUE
            TetrominoType.L -> Color.rgb(255, 165, 0) // Orange
            TetrominoType.O -> Color.YELLOW
            TetrominoType.S -> Color.GREEN
            TetrominoType.T -> Color.MAGENTA
            TetrominoType.Z -> Color.RED
        }
    }
    
    // Custom touch event handling
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (!isRunning || isPaused || gameBoard.isGameOver) {
            return true
        }
        
        // Ignore touch events during the freeze period after a piece locks
        val currentTime = System.currentTimeMillis()
        if (currentTime < touchFreezeUntil) {
            Log.d(TAG, "Ignoring touch event - freeze active for ${touchFreezeUntil - currentTime}ms more")
            return true
        }
        
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                startX = event.x
                startY = event.y
                lastTouchX = event.x
                lastTouchY = event.y
                lastTapTime = currentTime  // Set the tap time when touch starts
                
                // Reset direction lock
                lockedDirection = null
            }
            
            MotionEvent.ACTION_MOVE -> {
                val deltaX = event.x - lastTouchX
                val deltaY = event.y - lastTouchY
                
                // Check if we should lock direction
                if (lockedDirection == null) {
                    val absDeltaX = abs(deltaX)
                    val absDeltaY = abs(deltaY)
                    
                    if (absDeltaX > blockSize * directionLockThreshold || 
                        absDeltaY > blockSize * directionLockThreshold) {
                        // Lock to the dominant direction
                        lockedDirection = if (absDeltaX > absDeltaY) {
                            Direction.HORIZONTAL
                        } else {
                            Direction.VERTICAL
                        }
                    }
                }
                
                // Handle movement based on locked direction
                when (lockedDirection) {
                    Direction.HORIZONTAL -> {
                        if (abs(deltaX) > blockSize * minMovementThreshold) {
                            if (deltaX > 0) {
                                gameBoard.moveRight()
                            } else {
                                gameBoard.moveLeft()
                            }
                            lastTouchX = event.x
                            if (currentTime - lastMoveTime >= moveCooldown) {
                                gameHaptics?.vibrateForPieceMove()
                                lastMoveTime = currentTime
                            }
                            invalidate()
                        }
                    }
                    Direction.VERTICAL -> {
                        if (deltaY > blockSize * minMovementThreshold) {
                            gameBoard.softDrop()
                            lastTouchY = event.y
                            if (currentTime - lastMoveTime >= moveCooldown) {
                                gameHaptics?.vibrateForPieceMove()
                                lastMoveTime = currentTime
                            }
                            invalidate()
                        }
                    }
                    null -> {
                        // No direction lock yet, don't process movement
                    }
                }
            }
            
            MotionEvent.ACTION_UP -> {
                val deltaX = event.x - startX
                val deltaY = event.y - startY
                val moveTime = currentTime - lastTapTime
                
                // Handle taps for rotation
                if (moveTime < minTapTime * 1.5 &&
                    abs(deltaY) < maxTapMovement * 1.5 &&
                    abs(deltaX) < maxTapMovement * 1.5) {
                    
                    if (currentTime - lastRotationTime >= rotationCooldown) {
                        gameBoard.rotate()
                        lastRotationTime = currentTime
                        gameHaptics?.vibrateForPieceMove()
                        invalidate()
                    }
                    return true
                }
                
                // Handle gestures
                // Check for hold gesture (swipe up)
                if (deltaY < -blockSize * minHoldDistance && 
                    abs(deltaX) / abs(deltaY) < 0.5f) {
                    if (currentTime - lastHoldTime >= holdCooldown) {
                        gameBoard.holdPiece()
                        lastHoldTime = currentTime
                        gameHaptics?.vibrateForPieceMove()
                        invalidate()
                    }
                }
                // Check for hard drop (must be faster and longer than soft drop)
                else if (deltaY > blockSize * minHardDropDistance && 
                    abs(deltaX) / abs(deltaY) < 0.5f &&
                    (deltaY / moveTime) * 1000 > minSwipeVelocity) {
                    if (currentTime - lastHardDropTime >= hardDropCooldown) {
                        gameBoard.hardDrop()
                        lastHardDropTime = currentTime
                        invalidate()
                    }
                }
                // Check for soft drop (slower and shorter than hard drop)
                else if (deltaY > blockSize * minMovementThreshold && 
                    deltaY < blockSize * maxSoftDropDistance &&
                    (deltaY / moveTime) * 1000 < minSwipeVelocity) {
                    gameBoard.softDrop()
                    invalidate()
                }
                
                // Reset direction lock
                lockedDirection = null
            }
        }
        
        return true
    }
    
    /**
     * Get the current score
     */
    fun getScore(): Int = gameBoard.score
    
    /**
     * Get the current level
     */
    fun getLevel(): Int = gameBoard.level
    
    /**
     * Get the number of lines cleared
     */
    fun getLines(): Int = gameBoard.lines
    
    /**
     * Check if the game is over
     */
    fun isGameOver(): Boolean = gameBoard.isGameOver
    
    /**
     * Get the next piece that will be spawned
     */
    fun getNextPiece(): Tetromino? {
        return gameBoard.getNextPiece()
    }
    
    /**
     * Get the game board instance
     */
    fun getGameBoard(): GameBoard = gameBoard
    
    /**
     * Clean up resources when view is detached
     */
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        handler.removeCallbacks(gameLoopRunnable)
    }
    
    /**
     * Set the game board for this view
     */
    fun setGameBoard(board: GameBoard) {
        gameBoard = board
        
        // Reconnect callbacks to the new board
        gameBoard.onPieceMove = { onPieceMove?.invoke() }
        gameBoard.onPieceLock = { onPieceLock?.invoke() }
        gameBoard.onLineClear = { lineCount, clearedLines -> 
            Log.d(TAG, "Received line clear from GameBoard: $lineCount lines")
            try {
                onLineClear?.invoke(lineCount)
                // Use the lines that were cleared directly
                linesToPulse.clear()
                linesToPulse.addAll(clearedLines)
                Log.d(TAG, "Found ${linesToPulse.size} lines to pulse")
                startPulseAnimation(lineCount)
                Log.d(TAG, "Forwarded line clear callback")
            } catch (e: Exception) {
                Log.e(TAG, "Error forwarding line clear callback", e)
            }
        }
        
        invalidate()
    }
    
    /**
     * Set the haptics handler for this view
     */
    fun setHaptics(haptics: GameHaptics) {
        gameHaptics = haptics
    }
    
    /**
     * Resume the game
     */
    fun resume() {
        if (!isRunning) {
            isRunning = true
        }
        isPaused = false
        
        // Restart the game loop immediately
        handler.removeCallbacks(gameLoopRunnable)
        handler.post(gameLoopRunnable)
        
        // Force an update to ensure pieces move immediately
        update()
        invalidate()
    }
    
    /**
     * Start the pulse animation for line clear
     */
    private fun startPulseAnimation(lineCount: Int) {
        Log.d(TAG, "Starting pulse animation for $lineCount lines")
        
        // Cancel any existing animation
        pulseAnimator?.cancel()
        
        // Create new animation
        pulseAnimator = ValueAnimator.ofFloat(0f, 1f, 0f).apply {
            duration = when (lineCount) {
                1 -> 500L   // Single
                2 -> 1000L  // Double
                3 -> 1500L  // Triple
                4 -> 2000L  // Quad - longer duration
                else -> 500L
            }
            interpolator = LinearInterpolator()
            addUpdateListener { animation ->
                pulseAlpha = animation.animatedValue as Float
                isPulsing = true
                invalidate()
                Log.d(TAG, "Pulse animation update: alpha = $pulseAlpha")
            }
            addListener(object : android.animation.AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: android.animation.Animator) {
                    isPulsing = false
                    pulseAlpha = 0f
                    linesToPulse.clear()
                    invalidate()
                    Log.d(TAG, "Pulse animation ended")
                }
            })
        }
        pulseAnimator?.start()
    }
    
    /**
     * Set the theme color for the game view
     */
    fun setThemeColor(color: Int) {
        currentThemeColor = color
        blockPaint.color = color
        ghostBlockPaint.color = color
        glowPaint.color = color
        blockGlowPaint.color = color
        borderGlowPaint.color = color
        pulsePaint.color = color
        invalidate()
    }
    
    /**
     * Set the background color for the game view
     */
    override fun setBackgroundColor(color: Int) {
        super.setBackgroundColor(color)
        invalidate()
    }
    
    /**
     * Start the game over animation
     */
    fun startGameOverAnimation() {
        Log.d(TAG, "Starting game over animation")
        
        // Check if game over already showing
        if (isGameOverAnimating && gameOverAlpha > 0.5f) {
            Log.d(TAG, "Game over animation already active - skipping")
            return
        }
        
        // Cancel any existing animations
        pulseAnimator?.cancel()
        gameOverAnimator?.cancel()
        
        // Trigger haptic feedback
        gameHaptics?.vibrateForGameOver()
        
        // Force immediate visual feedback
        isGameOverAnimating = true
        gameOverAlpha = 0.3f
        invalidate()
        
        // Generate falling blocks based on current board state
        generateGameOverBlocks()
        
        // Create new game over animation
        gameOverAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
            duration = 3000L  // Increased from 2000L (2 seconds) to 3000L (3 seconds)
            interpolator = LinearInterpolator()
            
            addUpdateListener { animation ->
                val progress = animation.animatedValue as Float
                
                // Main alpha transition for overlay
                gameOverAlpha = when {
                    progress < 0.2f -> progress * 5f  // Quick fade in (first 20% of animation)
                    else -> 1f  // Hold at full opacity
                }
                
                // Color transition effect (start after 40% of animation)
                gameOverColorTransition = when {
                    progress < 0.4f -> 0f
                    progress < 0.8f -> (progress - 0.4f) * 2.5f  // Transition during 40%-80%
                    else -> 1f
                }
                
                // Screen shake effect (strongest at beginning, fades out)
                gameOverShakeAmount = when {
                    progress < 0.3f -> progress * 3.33f  // Ramp up
                    progress < 0.6f -> 1f - (progress - 0.3f) * 3.33f  // Ramp down
                    else -> 0f  // No shake
                }
                
                // Update falling blocks
                updateGameOverBlocks()
                
                isGameOverAnimating = true
                invalidate()
                Log.d(TAG, "Game over animation update: alpha = $gameOverAlpha, progress = $progress")
            }
            
            addListener(object : android.animation.AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: android.animation.Animator) {
                    Log.d(TAG, "Game over animation ended - Final alpha: $gameOverFinalAlpha")
                    isGameOverAnimating = true  // Keep true to maintain visibility
                    gameOverAlpha = gameOverFinalAlpha  // Keep at 80% opacity
                    invalidate()
                }
            })
        }
        gameOverAnimator?.start()
    }
    
    /**
     * Generate falling blocks for the game over animation based on current board state
     */
    private fun generateGameOverBlocks() {
        // Clear existing blocks
        gameOverBlocksY.clear()
        gameOverBlocksX.clear()
        gameOverBlocksRotation.clear()
        gameOverBlocksSpeed.clear()
        gameOverBlocksSize.clear()
        
        // Generate 30-40 blocks across the board
        val numBlocks = (30 + Math.random() * 10).toInt()
        
        for (i in 0 until numBlocks) {
            // Start positions - distribute across the board width but clustered near the top
            gameOverBlocksX.add(boardLeft + (Math.random() * gameBoard.width * blockSize).toFloat())
            gameOverBlocksY.add((boardTop - blockSize * 2 + Math.random() * height * 0.3f).toFloat())
            
            // Random rotation
            gameOverBlocksRotation.add((Math.random() * 360).toFloat())
            
            // Random fall speed (some faster, some slower)
            gameOverBlocksSpeed.add((5 + Math.random() * 15).toFloat())
            
            // Slightly varied block sizes
            gameOverBlocksSize.add((0.8f + Math.random() * 0.4f).toFloat())
        }
    }
    
    /**
     * Update the position of falling blocks in the game over animation
     */
    private fun updateGameOverBlocks() {
        for (i in gameOverBlocksY.indices) {
            // Update Y position based on speed
            gameOverBlocksY[i] += gameOverBlocksSpeed[i]
            
            // Update rotation
            gameOverBlocksRotation[i] += gameOverBlocksSpeed[i] * 0.5f
            
            // Accelerate falling
            gameOverBlocksSpeed[i] *= 1.03f
        }
    }
    
    /**
     * Check if the game is active (running, not paused, not game over)
     */
    fun isActive(): Boolean {
        return isRunning && !isPaused && !gameBoard.isGameOver
    }
    
    /**
     * Move the current piece left (for gamepad/keyboard support)
     */
    fun moveLeft() {
        if (!isActive()) return
        gameBoard.moveLeft()
        gameHaptics?.vibrateForPieceMove()
        invalidate()
    }
    
    /**
     * Move the current piece right (for gamepad/keyboard support)
     */
    fun moveRight() {
        if (!isActive()) return
        gameBoard.moveRight()
        gameHaptics?.vibrateForPieceMove()
        invalidate()
    }
    
    /**
     * Rotate the current piece (for gamepad/keyboard support)
     */
    fun rotate() {
        if (!isActive()) return
        gameBoard.rotate()
        gameHaptics?.vibrateForPieceMove()
        invalidate()
    }

    /**
     * Rotate the current piece counterclockwise (for gamepad/keyboard support)
     */
    fun rotateCounterClockwise() {
        if (!isActive()) return
        gameBoard.rotateCounterClockwise()
        gameHaptics?.vibrateForPieceMove()
        invalidate()
    }
    
    /**
     * Perform a soft drop (move down faster) (for gamepad/keyboard support)
     */
    fun softDrop() {
        if (!isActive()) return
        gameBoard.softDrop()
        gameHaptics?.vibrateForPieceMove()
        invalidate()
    }
    
    /**
     * Perform a hard drop (instant drop) (for gamepad/keyboard support)
     */
    fun hardDrop() {
        if (!isActive()) return
        gameBoard.hardDrop()
        // Hard drop haptic feedback is handled by the game board via onPieceLock
        invalidate()
    }
    
    /**
     * Hold the current piece (for gamepad/keyboard support)
     */
    fun holdPiece() {
        if (!isActive()) return
        gameBoard.holdPiece()
        gameHaptics?.vibrateForPieceMove()
        invalidate()
    }
}
