//
//  PacManGameViewController.swift
//  WebKitBrowser
//
//  Created by Developer on 2/15/26.
//

import UIKit
import SpriteKit

class PacManGameViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        
        let skView = SKView(frame: view.bounds)
        skView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(skView)

        let scene = PacManGameScene(size: view.bounds.size)
        scene.scaleMode = .resizeFill
        skView.presentScene(scene)
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }
}

class PacManGameScene: SKScene {
    // Map legend:
    // 0 = empty space
    // 1 = wall
    // 2 = pellet
    // 3 = power pellet
    
    // Using a simple maze map approx 21x21 for classic Pac-Man style maze
    private let tileSize: CGFloat = 24
    private var map: [[Int]] = [
        [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
        [1,3,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,3,1],
        [1,2,1,1,1,1,2,1,2,1,1,2,1,2,1,1,1,1,2,2,1],
        [1,2,1,0,0,1,2,1,2,1,1,2,1,2,1,0,0,1,2,2,1],
        [1,2,1,0,0,1,2,1,2,2,2,2,1,2,1,0,0,1,2,2,1],
        [1,2,1,1,0,1,2,1,1,1,1,1,1,2,1,0,1,1,2,2,1],
        [1,2,2,2,0,2,2,2,2,1,1,2,2,2,2,0,2,2,2,2,1],
        [1,2,1,1,0,1,1,1,2,1,1,2,1,1,1,0,1,1,1,2,1],
        [1,2,1,0,0,0,0,1,2,2,2,2,1,0,0,0,0,0,1,2,1],
        [1,2,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,2,1],
        [1,2,2,2,1,1,0,0,0,0,0,0,0,0,1,1,2,2,2,2,1],
        [1,2,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,2,1],
        [1,2,1,0,0,0,0,1,2,2,2,2,1,0,0,0,0,0,1,2,1],
        [1,2,1,1,1,1,2,1,2,1,1,2,1,2,1,1,1,1,2,2,1],
        [1,3,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,3,1],
        [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    ]
    
    private var rows: Int { map.count }
    private var cols: Int { map.first?.count ?? 0 }

    private var pacMan: SKShapeNode!
    private var pacManGridPos: CGPoint = .zero
    private var moveDirection: CGVector = .zero
    private var nextMoveDirection: CGVector = .zero
    
    private var pelletNodes: [CGPoint: SKShapeNode] = [:]
    private var powerPelletNodes: [CGPoint: SKShapeNode] = [:]

    private let pacManRadius: CGFloat = 10
    
    private var score: Int = 0 {
        didSet {
            scoreLabel.text = "Score: \(score)"
        }
    }
    private var scoreLabel: SKLabelNode!
    
    private var lives: Int = 3 {
        didSet {
            updateLivesDisplay()
        }
    }
    private var livesNodes: [SKShapeNode] = []
    
    private var youWinLabel: SKLabelNode!
    private var gameOverLabel: SKLabelNode!
    private var swipeToStartLabel: SKLabelNode!

    private var powerPelletActive: Bool = false
    private var powerPelletTimer: Timer?
    private let powerPelletDuration: TimeInterval = 8
    
    private var isGameOver = false
    private var hasStarted = false
    
    // MARK: - Ghost related
    
    private enum GhostState {
        case normal
        case vulnerable
        case eaten
    }
    
    private struct Ghost {
        let node: SKShapeNode
        var gridPosition: CGPoint
        let homePosition: CGPoint
        let color: UIColor
        var state: GhostState = .normal
        var vulnerableTimer: Timer? = nil
    }
    
    private var ghosts: [Ghost] = []
    
    // MARK: - Scene Setup
    
    override func didMove(to view: SKView) {
        backgroundColor = .black
        setupMaze()
        setupPacMan()
        setupScoreLabel()
        setupLivesDisplay()
        setupGhosts()
        addSwipeGestureRecognizers(to: view)
        addSwipeToStartPrompt()
    }
    
    // MARK: - Maze Setup
    
    private func setupMaze() {
        // Remove any previous nodes
        removeAllChildren()
        pelletNodes.removeAll()
        powerPelletNodes.removeAll()
        ghosts.removeAll()
        livesNodes.removeAll()
        youWinLabel = nil
        gameOverLabel = nil
        swipeToStartLabel = nil
        
        // Draw maze elements
        for row in 0..<rows {
            for col in 0..<cols {
                let tileValue = map[row][col]
                let pos = pointForGrid(row: row, col: col)
                
                switch tileValue {
                case 1: // wall
                    let wallNode = SKShapeNode(rectOf: CGSize(width: tileSize, height: tileSize))
                    wallNode.fillColor = .blue
                    wallNode.strokeColor = .blue
                    wallNode.position = pos
                    addChild(wallNode)
                case 2: // pellet
                    let pelletNode = SKShapeNode(circleOfRadius: tileSize * 0.15)
                    pelletNode.fillColor = .white
                    pelletNode.strokeColor = .clear
                    pelletNode.position = pos
                    addChild(pelletNode)
                    pelletNodes[CGPoint(x: col, y: row)] = pelletNode
                case 3: // power pellet
                    let powerPelletNode = SKShapeNode(circleOfRadius: tileSize * 0.4)
                    powerPelletNode.fillColor = .white
                    powerPelletNode.strokeColor = .yellow
                    powerPelletNode.lineWidth = 2
                    powerPelletNode.position = pos
                    addChild(powerPelletNode)
                    powerPelletNodes[CGPoint(x: col, y: row)] = powerPelletNode
                default:
                    // empty space - do nothing
                    break
                }
            }
        }
    }
    
    // MARK: - Pac-Man Setup
    
    private func setupPacMan() {
        // Place Pac-Man initially at a valid starting position.
        // Try to find close to (col: 10, row: 13), if not found, fallback to first empty/pellet/power pellet.

        let defaultStart = CGPoint(x: 10, y: 13)
        var foundStart = false
        
        if map[Int(defaultStart.y)][Int(defaultStart.x)] != 1 {
            pacManGridPos = defaultStart
            foundStart = true
        }
        
        if !foundStart {
            for row in (0..<rows).reversed() {
                for col in 0..<cols {
                    if map[row][col] == 0 || map[row][col] == 2 || map[row][col] == 3 {
                        pacManGridPos = CGPoint(x: col, y: row)
                        foundStart = true
                        break
                    }
                }
                if foundStart {
                    break
                }
            }
        }
        
        pacMan = SKShapeNode(circleOfRadius: pacManRadius)
        pacMan.fillColor = .yellow
        pacMan.strokeColor = .yellow
        pacMan.position = pointForGrid(row: Int(pacManGridPos.y), col: Int(pacManGridPos.x))
        addChild(pacMan)
        
        moveDirection = .zero
        nextMoveDirection = .zero
    }
    
    // MARK: - Score & Lives Setup
    
    private func setupScoreLabel() {
        scoreLabel = SKLabelNode(text: "Score: 0")
        scoreLabel.fontName = "Arial-BoldMT"
        scoreLabel.fontSize = 20
        scoreLabel.fontColor = .white
        scoreLabel.horizontalAlignmentMode = .left
        scoreLabel.verticalAlignmentMode = .top
        scoreLabel.position = CGPoint(x: frame.minX + 16, y: frame.maxY - 16)
        scoreLabel.zPosition = 1000
        addChild(scoreLabel)
    }
    
    private func setupLivesDisplay() {
        lives = 3
        for node in livesNodes {
            node.removeFromParent()
        }
        livesNodes.removeAll()
        
        let radius: CGFloat = 8
        let spacing: CGFloat = 20
        let startX = frame.minX + 16 + radius
        let y = frame.maxY - 48
        
        for i in 0..<lives {
            let lifeNode = SKShapeNode(circleOfRadius: radius)
            lifeNode.fillColor = .yellow
            lifeNode.strokeColor = .clear
            lifeNode.position = CGPoint(x: startX + CGFloat(i) * spacing, y: y)
            lifeNode.zPosition = 1000
            addChild(lifeNode)
            livesNodes.append(lifeNode)
        }
    }
    
    private func updateLivesDisplay() {
        // Remove all and re-add
        for node in livesNodes {
            node.removeFromParent()
        }
        livesNodes.removeAll()
        
        let radius: CGFloat = 8
        let spacing: CGFloat = 20
        let startX = frame.minX + 16 + radius
        let y = frame.maxY - 48
        
        for i in 0..<lives {
            let lifeNode = SKShapeNode(circleOfRadius: radius)
            lifeNode.fillColor = .yellow
            lifeNode.strokeColor = .clear
            lifeNode.position = CGPoint(x: startX + CGFloat(i) * spacing, y: y)
            lifeNode.zPosition = 1000
            addChild(lifeNode)
            livesNodes.append(lifeNode)
        }
    }
    
    // MARK: - Ghosts Setup
    
    private func setupGhosts() {
        // Classic ghost starting positions roughly center top of maze open area
        // We'll pick 4 classic colors: red, pink, cyan, orange
        // Positions chosen so they do not start inside walls.
        let ghostColors: [UIColor] = [.red, .systemPink, .cyan, .orange]
        
        // Classic ghost home positions (relative to grid):
        // We'll pick positions from the map that are empty, near center top:
        // For simplicity, use fixed positions known to be open in the maze:
        // (9,6), (10,6), (11,6), (12,6)
        
        let homePositions: [CGPoint] = [
            CGPoint(x: 9, y: 6),
            CGPoint(x: 10, y: 6),
            CGPoint(x: 11, y: 6),
            CGPoint(x: 12, y: 6)
        ]
        
        ghosts.removeAll()
        
        for (index, color) in ghostColors.enumerated() {
            if index >= homePositions.count { break }
            let homePos = homePositions[index]
            let ghostNode = SKShapeNode(circleOfRadius: pacManRadius)
            ghostNode.fillColor = color
            ghostNode.strokeColor = color
            ghostNode.position = pointForGrid(row: Int(homePos.y), col: Int(homePos.x))
            ghostNode.zPosition = 500
            addChild(ghostNode)
            
            let ghost = Ghost(node: ghostNode, gridPosition: homePos, homePosition: homePos, color: color, state: .normal)
            ghosts.append(ghost)
        }
    }
    
    // MARK: - Gestures
    
    private func addSwipeGestureRecognizers(to view: SKView) {
        let directions: [UISwipeGestureRecognizer.Direction] = [.up, .down, .left, .right]
        for direction in directions {
            let recognizer = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
            recognizer.direction = direction
            view.addGestureRecognizer(recognizer)
        }
    }
    
    @objc private func handleSwipe(_ gesture: UISwipeGestureRecognizer) {
        guard !isGameOver else { return }
        
        var desiredDirection: CGVector = .zero
        switch gesture.direction {
        case .up:
            desiredDirection = CGVector(dx: 0, dy: 1)
        case .down:
            desiredDirection = CGVector(dx: 0, dy: -1)
        case .left:
            desiredDirection = CGVector(dx: -1, dy: 0)
        case .right:
            desiredDirection = CGVector(dx: 1, dy: 0)
        default:
            break
        }
        
        nextMoveDirection = desiredDirection
        
        // If the desired direction is immediately valid from current position, update moveDirection to it
        if let _ = nextGridPositionIfValid(direction: nextMoveDirection) {
            moveDirection = nextMoveDirection
        }
        
        // If game hasn't started yet, start it now and remove the prompt
        if !hasStarted {
            hasStarted = true
            swipeToStartLabel?.removeFromParent()
            swipeToStartLabel = nil
        }
    }
    
    private func addSwipeToStartPrompt() {
        swipeToStartLabel = SKLabelNode(text: "Swipe to Start")
        swipeToStartLabel?.fontName = "Arial-BoldMT"
        swipeToStartLabel?.fontSize = 28
        swipeToStartLabel?.fontColor = .white
        swipeToStartLabel?.position = CGPoint(x: frame.midX, y: frame.minY + 60)
        swipeToStartLabel?.zPosition = 1002
        if let label = swipeToStartLabel {
            addChild(label)
        }
    }
    
    // MARK: - Game Update Loop
    
    override func update(_ currentTime: TimeInterval) {
        guard !isGameOver else { return }
        
        // Pac-Man movement logic:
        // Classic Pac-Man continuous movement:
        // 1. If Pac-Man is stopped (moveDirection == .zero), check if nextMoveDirection is valid and start moving.
        // 2. If Pac-Man is moving (moveDirection != .zero), try to turn in nextMoveDirection if valid (cornering).
        // 3. Otherwise, continue in current direction if possible.
        // 4. Stop if no valid moves.
        
        if moveDirection == .zero {
            // Not moving, try to start moving in nextMoveDirection if possible
            if let newGridPos = nextGridPositionIfValid(direction: nextMoveDirection) {
                moveDirection = nextMoveDirection
                movePacMan(to: newGridPos)
                // Remove "Swipe to Start" prompt if present
                if !hasStarted {
                    hasStarted = true
                    swipeToStartLabel?.removeFromParent()
                    swipeToStartLabel = nil
                }
            }
            // else remain stopped
        } else {
            // Moving: try to turn in nextMoveDirection if different and valid
            if nextMoveDirection != moveDirection {
                if let turnPos = nextGridPositionIfValid(direction: nextMoveDirection) {
                    moveDirection = nextMoveDirection
                    movePacMan(to: turnPos)
                    if !hasStarted {
                        hasStarted = true
                        swipeToStartLabel?.removeFromParent()
                        swipeToStartLabel = nil
                    }
                    return // move done this frame
                }
            }
            // Else try continue moving in current direction
            if let continuePos = nextGridPositionIfValid(direction: moveDirection) {
                movePacMan(to: continuePos)
            } else {
                // Can't move, stop
                moveDirection = .zero
            }
        }
        
        // Ghost movement
        updateGhosts()
        
        // Check collisions with ghosts
        checkGhostCollisions()
        
        // Check win condition
        if pelletNodes.isEmpty && powerPelletNodes.isEmpty {
            showWinMessage()
        }
    }
    
    // MARK: - Movement Helpers
    
    // Calculate next grid position if move is valid (not a wall)
    private func nextGridPositionIfValid(direction: CGVector) -> CGPoint? {
        guard direction != .zero else { return nil }
        let newX = Int(pacManGridPos.x) + Int(direction.dx)
        let newY = Int(pacManGridPos.y) + Int(direction.dy)
        
        guard newX >= 0, newX < cols, newY >= 0, newY < rows else {
            return nil
        }
        let tileValue = map[newY][newX]
        if tileValue == 1 {
            // Wall
            return nil
        }
        return CGPoint(x: newX, y: newY)
    }
    
    private func movePacMan(to newGridPos: CGPoint) {
        guard newGridPos != pacManGridPos else { return }
        pacManGridPos = newGridPos
        
        // Animate movement to new position
        let newPosition = pointForGrid(row: Int(newGridPos.y), col: Int(newGridPos.x))
        let moveAction = SKAction.move(to: newPosition, duration: 0.12)
        pacMan.run(moveAction)
        
        // Check for pellet consumption
        eatPelletIfNeeded(at: newGridPos)
    }
    
    private func eatPelletIfNeeded(at gridPos: CGPoint) {
        if let pelletNode = pelletNodes[gridPos] {
            pelletNode.removeFromParent()
            pelletNodes.removeValue(forKey: gridPos)
            score += 10
            map[Int(gridPos.y)][Int(gridPos.x)] = 0
        } else if let powerPelletNode = powerPelletNodes[gridPos] {
            powerPelletNode.removeFromParent()
            powerPelletNodes.removeValue(forKey: gridPos)
            score += 50
            map[Int(gridPos.y)][Int(gridPos.x)] = 0
            activatePowerPellet()
        }
    }
    
    // MARK: - Power Pellet Logic
    
    private func activatePowerPellet() {
        powerPelletTimer?.invalidate()
        powerPelletActive = true
        setGhostsVulnerable(true)
        powerPelletTimer = Timer.scheduledTimer(withTimeInterval: powerPelletDuration, repeats: false) { [weak self] _ in
            self?.deactivatePowerPellet()
        }
    }
    
    private func deactivatePowerPellet() {
        powerPelletActive = false
        setGhostsVulnerable(false)
        powerPelletTimer?.invalidate()
        powerPelletTimer = nil
    }
    
    private func setGhostsVulnerable(_ vulnerable: Bool) {
        for i in ghosts.indices {
            if vulnerable {
                if ghosts[i].state == .normal {
                    ghosts[i].state = .vulnerable
                    ghosts[i].node.fillColor = .blue
                    ghosts[i].node.strokeColor = .blue
                }
            } else {
                if ghosts[i].state == .vulnerable {
                    ghosts[i].state = .normal
                    ghosts[i].node.fillColor = ghosts[i].color
                    ghosts[i].node.strokeColor = ghosts[i].color
                }
            }
        }
    }
    
    // MARK: - Ghost Movement
    
    private func updateGhosts() {
        // Ghosts move regardless of game start, if not gameOver
        guard !isGameOver else { return }
        for i in ghosts.indices {
            var ghost = ghosts[i]
            // If eaten, move ghost back to home position
            switch ghost.state {
            case .eaten:
                if ghost.gridPosition == ghost.homePosition {
                    // Respawn ghost as normal
                    ghost.state = .normal
                    ghost.node.fillColor = ghost.color
                    ghost.node.strokeColor = ghost.color
                    ghosts[i] = ghost
                    continue
                } else {
                    // Move towards home position
                    let nextPos = nextStepTowards(from: ghost.gridPosition, to: ghost.homePosition)
                    moveGhost(&ghost, to: nextPos)
                    ghosts[i] = ghost
                }
            case .vulnerable:
                // Scatter randomly to avoid Pac-Man
                let directions = validDirections(from: ghost.gridPosition)
                if directions.isEmpty {
                    // No moves, stay put
                    continue
                }
                // Pick a random direction
                let randomDir = directions.randomElement()!
                let nextPos = CGPoint(x: ghost.gridPosition.x + CGFloat(randomDir.dx), y: ghost.gridPosition.y + CGFloat(randomDir.dy))
                moveGhost(&ghost, to: nextPos)
                ghosts[i] = ghost
            case .normal:
                // Basic chase: move one step closer to Pac-Man, prefer shortest path by trying all directions
                let directions = validDirections(from: ghost.gridPosition)
                if directions.isEmpty {
                    continue
                }
                var bestDirection = directions[0]
                var minDistance = distance(from: CGPoint(x: ghost.gridPosition.x + CGFloat(bestDirection.dx), y: ghost.gridPosition.y + CGFloat(bestDirection.dy)), to: pacManGridPos)
                for dir in directions {
                    let newPoint = CGPoint(x: ghost.gridPosition.x + CGFloat(dir.dx), y: ghost.gridPosition.y + CGFloat(dir.dy))
                    let dist = distance(from: newPoint, to: pacManGridPos)
                    if dist < minDistance {
                        minDistance = dist
                        bestDirection = dir
                    }
                }
                let nextPos = CGPoint(x: ghost.gridPosition.x + CGFloat(bestDirection.dx), y: ghost.gridPosition.y + CGFloat(bestDirection.dy))
                moveGhost(&ghost, to: nextPos)
                ghosts[i] = ghost
            }
        }
    }
    
    private func validDirections(from position: CGPoint) -> [CGVector] {
        let directions = [CGVector(dx: 0, dy: 1), CGVector(dx: 0, dy: -1), CGVector(dx: -1, dy: 0), CGVector(dx: 1, dy: 0)]
        var validDirs: [CGVector] = []
        for dir in directions {
            let newX = Int(position.x) + Int(dir.dx)
            let newY = Int(position.y) + Int(dir.dy)
            if newX >= 0, newX < cols, newY >= 0, newY < rows {
                if map[newY][newX] != 1 {
                    validDirs.append(dir)
                }
            }
        }
        return validDirs
    }
    
    private func moveGhost(_ ghost: inout Ghost, to newGridPos: CGPoint) {
        guard newGridPos != ghost.gridPosition else { return }
        ghost.gridPosition = newGridPos
        
        let newPosition = pointForGrid(row: Int(newGridPos.y), col: Int(newGridPos.x))
        let moveAction = SKAction.move(to: newPosition, duration: 0.15)
        ghost.node.run(moveAction)
    }
    
    private func nextStepTowards(from start: CGPoint, to target: CGPoint) -> CGPoint {
        // Simple greedy approach to move closer on x or y axis
        let directions = validDirections(from: start)
        if directions.isEmpty {
            return start
        }
        var bestDirection = directions[0]
        var minDistance = distance(from: CGPoint(x: start.x + CGFloat(bestDirection.dx), y: start.y + CGFloat(bestDirection.dy)), to: target)
        for dir in directions {
            let newPoint = CGPoint(x: start.x + CGFloat(dir.dx), y: start.y + CGFloat(dir.dy))
            let dist = distance(from: newPoint, to: target)
            if dist < minDistance {
                minDistance = dist
                bestDirection = dir
            }
        }
        return CGPoint(x: start.x + CGFloat(bestDirection.dx), y: start.y + CGFloat(bestDirection.dy))
    }
    
    private func distance(from a: CGPoint, to b: CGPoint) -> CGFloat {
        let dx = a.x - b.x
        let dy = a.y - b.y
        return sqrt(dx * dx + dy * dy)
    }
    
    // MARK: - Collision Handling
    
    private func checkGhostCollisions() {
        for i in ghosts.indices {
            var ghost = ghosts[i]
            guard ghost.gridPosition == pacManGridPos else { continue }
            switch ghost.state {
            case .normal:
                // Pac-Man loses a life, reset positions or game over
                loseLife()
                return
            case .vulnerable:
                // Eat ghost
                eatGhost(&ghost)
                ghosts[i] = ghost
            case .eaten:
                // No effect
                break
            }
        }
    }
    
    private func loseLife() {
        lives -= 1
        if lives <= 0 {
            showGameOver()
        } else {
            resetPositionsAfterDeath()
        }
    }
    
    private func resetPositionsAfterDeath() {
        // Stop movement
        moveDirection = .zero
        nextMoveDirection = .zero
        
        // Reset Pac-Man position to start
        let defaultStart = CGPoint(x: 10, y: 13)
        if map[Int(defaultStart.y)][Int(defaultStart.x)] != 1 {
            pacManGridPos = defaultStart
        } else {
            pacManGridPos = .zero
            for row in (0..<rows).reversed() {
                for col in 0..<cols {
                    if map[row][col] == 0 || map[row][col] == 2 || map[row][col] == 3 {
                        pacManGridPos = CGPoint(x: col, y: row)
                        break
                    }
                }
                if pacManGridPos != .zero {
                    break
                }
            }
        }
        let pacManPos = pointForGrid(row: Int(pacManGridPos.y), col: Int(pacManGridPos.x))
        pacMan.position = pacManPos
        pacMan.removeAllActions()
        
        // Reset ghosts to home positions and normal state
        for i in ghosts.indices {
            ghosts[i].gridPosition = ghosts[i].homePosition
            ghosts[i].state = .normal
            ghosts[i].node.fillColor = ghosts[i].color
            ghosts[i].node.strokeColor = ghosts[i].color
            ghosts[i].node.position = pointForGrid(row: Int(ghosts[i].homePosition.y), col: Int(ghosts[i].homePosition.x))
            ghosts[i].node.removeAllActions()
        }
        
        deactivatePowerPellet()
    }
    
    private func eatGhost(_ ghost: inout Ghost) {
        ghost.state = .eaten
        ghost.node.fillColor = .white
        ghost.node.strokeColor = .white
        score += 200
        // Send ghost home, it will move automatically in updateGhosts()
    }
    
    // MARK: - Win / Game Over
    
    private func showWinMessage() {
        if youWinLabel != nil || isGameOver { return }
        
        youWinLabel = SKLabelNode(text: "You Win!")
        youWinLabel.fontName = "Arial-BoldMT"
        youWinLabel.fontSize = 48
        youWinLabel.fontColor = .yellow
        youWinLabel.position = CGPoint(x: frame.midX, y: frame.midY)
        youWinLabel.zPosition = 1001
        addChild(youWinLabel)
        
        // Disable movement on win
        moveDirection = .zero
        nextMoveDirection = .zero
        isGameOver = true
        
        powerPelletTimer?.invalidate()
        powerPelletTimer = nil
    }
    
    private func showGameOver() {
        if gameOverLabel != nil || isGameOver { return }
        
        gameOverLabel = SKLabelNode(text: "Game Over")
        gameOverLabel.fontName = "Arial-BoldMT"
        gameOverLabel.fontSize = 48
        gameOverLabel.fontColor = .red
        gameOverLabel.position = CGPoint(x: frame.midX, y: frame.midY)
        gameOverLabel.zPosition = 1001
        addChild(gameOverLabel)
        
        // Disable movement on game over
        moveDirection = .zero
        nextMoveDirection = .zero
        isGameOver = true
        
        powerPelletTimer?.invalidate()
        powerPelletTimer = nil
    }
    
    // MARK: - Coordinate Conversion
    
    // Convert grid coordinates to scene point centered on tile
    private func pointForGrid(row: Int, col: Int) -> CGPoint {
        // Center the maze in the scene
        let mazeWidth = CGFloat(cols) * tileSize
        let mazeHeight = CGFloat(rows) * tileSize
        
        let offsetX = (size.width - mazeWidth) / 2
        let offsetY = (size.height - mazeHeight) / 2
        
        // Y axis inverted: row 0 at top of maze, but SpriteKit Y=0 is bottom
        let x = offsetX + CGFloat(col) * tileSize + tileSize/2
        let y = offsetY + CGFloat(rows - 1 - row) * tileSize + tileSize/2
        
        return CGPoint(x: x, y: y)
    }
    
    // MARK: - Future additions:
    // - More advanced ghost AI and pathfinding
    // - Lives and death animation
    // - Sound effects and animations
}

