package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"
)

// Tetrimino types
const (
	EMPTY = iota
	I_PIECE
	O_PIECE
	T_PIECE
	S_PIECE
	Z_PIECE
	J_PIECE
	L_PIECE
)

// Tetrimino shapes (4 rotations each)
var tetriminoShapes = map[int][4][4][4]int{
	I_PIECE: {
		{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}},
		{{0, 0, 0, 0}, {0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}},
		{{0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}},
	},
	O_PIECE: {
		{{0, 1, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
	},
	T_PIECE: {
		{{0, 1, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 0, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
		{{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 0, 0}, {1, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
	},
	S_PIECE: {
		{{0, 1, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}},
		{{0, 0, 0, 0}, {0, 1, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}},
		{{1, 0, 0, 0}, {1, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
	},
	Z_PIECE: {
		{{1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 0, 1, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
		{{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}},
		{{0, 1, 0, 0}, {1, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}},
	},
	J_PIECE: {
		{{1, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 1, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
		{{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}},
		{{0, 1, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}},
	},
	L_PIECE: {
		{{0, 0, 1, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},
		{{0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}},
		{{0, 0, 0, 0}, {1, 1, 1, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}},
		{{1, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}},
	},
}

type Tetrimino struct {
	Type     int `json:"type"`
	X        int `json:"x"`
	Y        int `json:"y"`
	Rotation int `json:"rotation"`
}

type GameState struct {
	Board         [40][10]int `json:"board"`
	CurrentPiece  *Tetrimino  `json:"current_piece"`
	NextPieces    []int       `json:"next_pieces"`
	HoldPiece     *int        `json:"hold_piece"`
	Score         int         `json:"score"`
	Level         int         `json:"level"`
	LinesCleared  int         `json:"lines_cleared"`
	GameRunning   bool        `json:"game_running"`
	GameOver      bool        `json:"game_over"`
	CanHold       bool        `json:"can_hold"`
	LinesForLevel int         `json:"lines_for_level"`
}

type Game struct {
	state    *GameState
	bag      []int
	bagIndex int
	clients  map[chan string]bool
}

func NewGame() *Game {
	g := &Game{
		state: &GameState{
			NextPieces:    make([]int, 6),
			Level:         1,
			GameRunning:   false,
			GameOver:      false,
			CanHold:       true,
			LinesForLevel: 5,
		},
		clients: make(map[chan string]bool),
	}
	g.fillBag()
	g.fillNextPieces()
	return g
}

func (g *Game) fillBag() {
	g.bag = []int{I_PIECE, O_PIECE, T_PIECE, S_PIECE, Z_PIECE, J_PIECE, L_PIECE}
	// Simple shuffle
	for i := len(g.bag) - 1; i > 0; i-- {
		j := int(time.Now().UnixNano()) % (i + 1)
		g.bag[i], g.bag[j] = g.bag[j], g.bag[i]
	}
	g.bagIndex = 0
}

func (g *Game) getNextPiece() int {
	if g.bagIndex >= len(g.bag) {
		g.fillBag()
	}
	piece := g.bag[g.bagIndex]
	g.bagIndex++
	return piece
}

func (g *Game) fillNextPieces() {
	for i := 0; i < 6; i++ {
		g.state.NextPieces[i] = g.getNextPiece()
	}
}

func (g *Game) spawnPiece() {
	pieceType := g.state.NextPieces[0]
	// Shift next pieces
	copy(g.state.NextPieces[:5], g.state.NextPieces[1:])
	g.state.NextPieces[5] = g.getNextPiece()

	// Spawn position based on guidelines
	x := 3 // left-middle for most pieces
	if pieceType == I_PIECE || pieceType == O_PIECE {
		x = 4 // middle for I and O
	}

	g.state.CurrentPiece = &Tetrimino{
		Type:     pieceType,
		X:        x,
		Y:        21,
		Rotation: 0,
	}

	// Drop one space if possible
	if g.canMove(g.state.CurrentPiece.X, g.state.CurrentPiece.Y-1, g.state.CurrentPiece.Rotation) {
		g.state.CurrentPiece.Y--
	}

	// Check for game over
	if !g.canMove(g.state.CurrentPiece.X, g.state.CurrentPiece.Y, g.state.CurrentPiece.Rotation) {
		g.state.GameOver = true
		g.state.GameRunning = false
	}

	g.state.CanHold = true
}

func (g *Game) canMove(x, y, rotation int) bool {
	shape := tetriminoShapes[g.state.CurrentPiece.Type][rotation]
	for py := 0; py < 4; py++ {
		for px := 0; px < 4; px++ {
			if shape[py][px] == 0 {
				continue
			}
			boardX := x + px
			boardY := y + py
			if boardX < 0 || boardX >= 10 || boardY < 0 {
				return false
			}
			if boardY < 40 && g.state.Board[boardY][boardX] != EMPTY {
				return false
			}
		}
	}
	return true
}

func (g *Game) lockPiece() {
	shape := tetriminoShapes[g.state.CurrentPiece.Type][g.state.CurrentPiece.Rotation]
	for py := 0; py < 4; py++ {
		for px := 0; px < 4; px++ {
			if shape[py][px] == 0 {
				continue
			}
			boardX := g.state.CurrentPiece.X + px
			boardY := g.state.CurrentPiece.Y + py
			if boardY < 40 {
				g.state.Board[boardY][boardX] = g.state.CurrentPiece.Type
			}
		}
	}
	g.clearLines()
	g.spawnPiece()
}

func (g *Game) clearLines() {
	linesCleared := 0
	for y := 0; y < 40; y++ {
		full := true
		for x := 0; x < 10; x++ {
			if g.state.Board[y][x] == EMPTY {
				full = false
				break
			}
		}
		if full {
			// Remove line
			for moveY := y; moveY < 39; moveY++ {
				for x := 0; x < 10; x++ {
					g.state.Board[moveY][x] = g.state.Board[moveY+1][x]
				}
			}
			// Clear top line
			for x := 0; x < 10; x++ {
				g.state.Board[39][x] = EMPTY
			}
			linesCleared++
			y-- // Check same line again
		}
	}

	if linesCleared > 0 {
		g.state.LinesCleared += linesCleared
		g.updateScore(linesCleared)
		g.updateLevel()
	}
}

func (g *Game) updateScore(lines int) {
	baseScore := map[int]int{1: 100, 2: 300, 3: 500, 4: 800}
	if score, ok := baseScore[lines]; ok {
		g.state.Score += score * g.state.Level
	}
}

func (g *Game) updateLevel() {
	requiredLines := g.state.Level * g.state.LinesForLevel
	if g.state.LinesCleared >= requiredLines {
		g.state.Level++
	}
}

func (g *Game) move(direction string) {
	if !g.state.GameRunning || g.state.CurrentPiece == nil {
		return
	}

	newX := g.state.CurrentPiece.X
	newY := g.state.CurrentPiece.Y

	switch direction {
	case "left":
		newX--
	case "right":
		newX++
	case "down":
		newY--
	}

	if g.canMove(newX, newY, g.state.CurrentPiece.Rotation) {
		g.state.CurrentPiece.X = newX
		g.state.CurrentPiece.Y = newY
	}
}

func (g *Game) rotate(direction string) {
	if !g.state.GameRunning || g.state.CurrentPiece == nil {
		return
	}

	newRotation := g.state.CurrentPiece.Rotation
	if direction == "cw" {
		newRotation = (newRotation + 1) % 4
	} else {
		newRotation = (newRotation + 3) % 4
	}

	if g.canMove(g.state.CurrentPiece.X, g.state.CurrentPiece.Y, newRotation) {
		g.state.CurrentPiece.Rotation = newRotation
	}
}

func (g *Game) hardDrop() {
	if !g.state.GameRunning || g.state.CurrentPiece == nil {
		return
	}

	for g.canMove(g.state.CurrentPiece.X, g.state.CurrentPiece.Y-1, g.state.CurrentPiece.Rotation) {
		g.state.CurrentPiece.Y--
	}
	g.lockPiece()
}

func (g *Game) hold() {
	if !g.state.GameRunning || g.state.CurrentPiece == nil || !g.state.CanHold {
		return
	}

	if g.state.HoldPiece == nil {
		g.state.HoldPiece = &g.state.CurrentPiece.Type
		g.spawnPiece()
	} else {
		temp := *g.state.HoldPiece
		g.state.HoldPiece = &g.state.CurrentPiece.Type
		
		x := 3
		if temp == I_PIECE || temp == O_PIECE {
			x = 4
		}
		
		g.state.CurrentPiece = &Tetrimino{
			Type:     temp,
			X:        x,
			Y:        21,
			Rotation: 0,
		}
		
		if g.canMove(g.state.CurrentPiece.X, g.state.CurrentPiece.Y-1, g.state.CurrentPiece.Rotation) {
			g.state.CurrentPiece.Y--
		}
	}
	g.state.CanHold = false
}

func (g *Game) startGame() {
	g.state = &GameState{
		NextPieces:    make([]int, 6),
		Level:         1,
		GameRunning:   true,
		GameOver:      false,
		CanHold:       true,
		LinesForLevel: 5,
	}
	g.fillBag()
	g.fillNextPieces()
	g.spawnPiece()
}

func (g *Game) tick() {
	if !g.state.GameRunning || g.state.CurrentPiece == nil {
		return
	}

	if g.canMove(g.state.CurrentPiece.X, g.state.CurrentPiece.Y-1, g.state.CurrentPiece.Rotation) {
		g.state.CurrentPiece.Y--
	} else {
		g.lockPiece()
	}
}

func (g *Game) broadcast() {
	data, _ := json.Marshal(g.state)
	message := fmt.Sprintf("data: %s\n\n", string(data))
	
	for client := range g.clients {
		select {
		case client <- message:
		default:
			close(client)
			delete(g.clients, client)
		}
	}
}

var game = NewGame()

func main() {
	http.HandleFunc("/", serveHTML)
	http.HandleFunc("/events", handleSSE)
	http.HandleFunc("/action", handleAction)

	// Game loop
	go func() {
		ticker := time.NewTicker(time.Second)
		defer ticker.Stop()
		
		for {
			select {
			case <-ticker.C:
				if game.state.GameRunning {
					game.tick()
					game.broadcast()
				}
			}
		}
	}()

	fmt.Println("Server starting on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func handleSSE(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	client := make(chan string)
	game.clients[client] = true

	// Send initial state
	data, _ := json.Marshal(game.state)
	fmt.Fprintf(w, "data: %s\n\n", string(data))
	w.(http.Flusher).Flush()

	for {
		select {
		case message := <-client:
			fmt.Fprint(w, message)
			w.(http.Flusher).Flush()
		case <-r.Context().Done():
			delete(game.clients, client)
			close(client)
			return
		}
	}
}

func handleAction(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "POST")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

	if r.Method == "OPTIONS" {
		return
	}

	action := r.URL.Query().Get("action")
	direction := r.URL.Query().Get("direction")

	switch action {
	case "move":
		game.move(direction)
	case "rotate":
		game.rotate(direction)
	case "hard_drop":
		game.hardDrop()
	case "hold":
		game.hold()
	case "start":
		game.startGame()
	}

	game.broadcast()
	w.WriteHeader(http.StatusOK)
}

func serveHTML(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprint(w, htmlContent)
}

const htmlContent = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tetris</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #000;
            color: #fff;
            font-family: 'Courier New', monospace;
            display: flex;
            justify-content: center;
            align-items: flex-start;
            min-height: 100vh;
        }
        
        .game-container {
            display: flex;
            gap: 20px;
            align-items: flex-start;
        }
        
        .main-game {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        
        canvas {
            border: 2px solid #333;
            background: #111;
        }
        
        .info-panel {
            width: 200px;
            background: #222;
            padding: 15px;
            border-radius: 8px;
        }
        
        .info-section {
            margin-bottom: 20px;
        }
        
        .info-section h3 {
            margin: 0 0 10px 0;
            color: #ccc;
            font-size: 14px;
        }
        
        .next-canvas, .hold-canvas {
            border: 1px solid #444;
            background: #111;
        }
        
        .controls {
            margin-top: 15px;
            text-align: center;
        }
        
        .controls h3 {
            margin-bottom: 10px;
        }
        
        .controls p {
            margin: 5px 0;
            font-size: 12px;
            color: #aaa;
        }
        
        button {
            background: #333;
            color: #fff;
            border: 1px solid #555;
            padding: 8px 16px;
            margin: 5px;
            cursor: pointer;
            border-radius: 4px;
        }
        
        button:hover {
            background: #555;
        }
        
        .game-over {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.9);
            padding: 30px;
            border-radius: 10px;
            text-align: center;
            border: 2px solid #f00;
        }
        
        .score-info {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 10px;
            font-size: 12px;
        }
        
        .score-info div {
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <div class="info-panel">
            <div class="info-section">
                <h3>Hold</h3>
                <canvas id="holdCanvas" class="hold-canvas" width="80" height="80"></canvas>
            </div>
            
            <div class="info-section">
                <h3>Next</h3>
                <canvas id="nextCanvas" class="next-canvas" width="80" height="320"></canvas>
            </div>
            
            <div class="info-section">
                <div class="score-info">
                    <div>Score<br><span id="score">0</span></div>
                    <div>Level<br><span id="level">1</span></div>
                    <div>Lines<br><span id="lines">0</span></div>
                    <div>Goal<br><span id="goal">5</span></div>
                </div>
            </div>
        </div>
        
        <div class="main-game">
            <canvas id="gameCanvas" width="300" height="600"></canvas>
            
            <div class="controls">
                <button onclick="startGame()">Start Game</button>
                
                <h3>Controls</h3>
                <p>← → Move</p>
                <p>↓ Soft Drop</p>
                <p>Space Hard Drop</p>
                <p>Z Rotate Left</p>
                <p>X Rotate Right</p>
                <p>C Hold</p>
            </div>
        </div>
    </div>
    
    <div id="gameOver" class="game-over" style="display: none;">
        <h2>Game Over</h2>
        <p>Final Score: <span id="finalScore">0</span></p>
        <button onclick="startGame()">Play Again</button>
    </div>

    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const nextCanvas = document.getElementById('nextCanvas');
        const nextCtx = nextCanvas.getContext('2d');
        const holdCanvas = document.getElementById('holdCanvas');
        const holdCtx = holdCanvas.getContext('2d');
        
        const CELL_SIZE = 30;
        const COLORS = [
            '#000000', // Empty
            '#00f5ff', // I - Cyan
            '#ffff00', // O - Yellow  
            '#800080', // T - Purple
            '#00ff00', // S - Green
            '#ff0000', // Z - Red
            '#0000ff', // J - Blue
            '#ffa500'  // L - Orange
        ];
        
        let gameState = null;
        let eventSource = null;
        
        // Tetrimino shapes for rendering
        const shapes = {
            1: [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], // I
            2: [[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]], // O
            3: [[0,1,0,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]], // T
            4: [[0,1,1,0],[1,1,0,0],[0,0,0,0],[0,0,0,0]], // S
            5: [[1,1,0,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]], // Z
            6: [[1,0,0,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]], // J
            7: [[0,0,1,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]]  // L
        };
        
        function connectEventSource() {
            eventSource = new EventSource('/events');
            eventSource.onmessage = function(event) {
                gameState = JSON.parse(event.data);
                render();
                updateUI();
            };
            
            eventSource.onerror = function(event) {
                console.log('EventSource failed:', event);
                setTimeout(connectEventSource, 1000);
            };
        }
        
        function sendAction(action, direction = '') {
            const url = '/action?action=' + action + (direction ? '&direction=' + direction : '');
            fetch(url, { method: 'POST' }).catch(console.error);
        }
        
        function startGame() {
            document.getElementById('gameOver').style.display = 'none';
            sendAction('start');
        }
        
        function render() {
            if (!gameState) return;
            
            // Clear main canvas
            ctx.fillStyle = '#111';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // Draw board (only visible part: rows 0-19, but our array is 0-39)
            for (let y = 0; y < 20; y++) {
                for (let x = 0; x < 10; x++) {
                    const cellValue = gameState.board[y][x];
                    if (cellValue > 0) {
                        ctx.fillStyle = COLORS[cellValue];
                        ctx.fillRect(x * CELL_SIZE, (19 - y) * CELL_SIZE, CELL_SIZE, CELL_SIZE);
                        ctx.strokeStyle = '#333';
                        ctx.strokeRect(x * CELL_SIZE, (19 - y) * CELL_SIZE, CELL_SIZE, CELL_SIZE);
                    }
                }
            }
            
            // Draw current piece
            if (gameState.current_piece && gameState.game_running) {
                const piece = gameState.current_piece;
                const shape = shapes[piece.type];
                if (shape) {
                    ctx.fillStyle = COLORS[piece.type];
                    for (let py = 0; py < 4; py++) {
                        for (let px = 0; px < 4; px++) {
                            if (shape[py][px]) {
                                nextCtx.fillRect(px * 15 + 10, py * 15 + offsetY, 15, 15);
                            }
                        }
                    }
                }
            }
        }
        
        function renderHoldPiece() {
            holdCtx.fillStyle = '#111';
            holdCtx.fillRect(0, 0, holdCanvas.width, holdCanvas.height);
            
            if (!gameState || !gameState.hold_piece) return;
            
            const pieceType = gameState.hold_piece;
            const shape = shapes[pieceType];
            if (shape) {
                holdCtx.fillStyle = COLORS[pieceType];
                for (let py = 0; py < 4; py++) {
                    for (let px = 0; px < 4; px++) {
                        if (shape[py][px]) {
                            holdCtx.fillRect(px * 15 + 10, py * 15 + 10, 15, 15);
                        }
                    }
                }
            }
        }
        
        function updateUI() {
            if (!gameState) return;
            
            document.getElementById('score').textContent = gameState.score;
            document.getElementById('level').textContent = gameState.level;
            document.getElementById('lines').textContent = gameState.lines_cleared;
            document.getElementById('goal').textContent = gameState.level * gameState.lines_for_level;
            
            if (gameState.game_over) {
                document.getElementById('finalScore').textContent = gameState.score;
                document.getElementById('gameOver').style.display = 'block';
            }
        }
        
        // Keyboard controls
        document.addEventListener('keydown', function(event) {
            if (!gameState || !gameState.game_running) return;
            
            switch(event.code) {
                case 'ArrowLeft':
                    event.preventDefault();
                    sendAction('move', 'left');
                    break;
                case 'ArrowRight':
                    event.preventDefault();
                    sendAction('move', 'right');
                    break;
                case 'ArrowDown':
                    event.preventDefault();
                    sendAction('move', 'down');
                    break;
                case 'Space':
                    event.preventDefault();
                    sendAction('hard_drop');
                    break;
                case 'KeyZ':
                    event.preventDefault();
                    sendAction('rotate', 'ccw');
                    break;
                case 'KeyX':
                case 'ArrowUp':
                    event.preventDefault();
                    sendAction('rotate', 'cw');
                    break;
                case 'KeyC':
                    event.preventDefault();
                    sendAction('hold');
                    break;
            }
        });
        
        // Initialize
        connectEventSource();
    </script>
</body>
</html>`
                            if (shape[py][px]) {
                                const boardX = piece.x + px;
                                const boardY = piece.y + py;
                                if (boardY < 20) { // Only draw visible part
                                    ctx.fillRect(boardX * CELL_SIZE, (19 - boardY) * CELL_SIZE, CELL_SIZE, CELL_SIZE);
                                    ctx.strokeStyle = '#666';
                                    ctx.strokeRect(boardX * CELL_SIZE, (19 - boardY) * CELL_SIZE, CELL_SIZE, CELL_SIZE);
                                }
                            }
                        }
                    }
                }
            }
            
            // Draw grid
            ctx.strokeStyle = '#333';
            for (let x = 0; x <= 10; x++) {
                ctx.beginPath();
                ctx.moveTo(x * CELL_SIZE, 0);
                ctx.lineTo(x * CELL_SIZE, canvas.height);
                ctx.stroke();
            }
            for (let y = 0; y <= 20; y++) {
                ctx.beginPath();
                ctx.moveTo(0, y * CELL_SIZE);
                ctx.lineTo(canvas.width, y * CELL_SIZE);
                ctx.stroke();
            }
            
            renderNextPieces();
            renderHoldPiece();
        }
        
        function renderNextPieces() {
            nextCtx.fillStyle = '#111';
            nextCtx.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
            
            if (!gameState || !gameState.next_pieces) return;
            
            for (let i = 0; i < Math.min(6, gameState.next_pieces.length); i++) {
                const pieceType = gameState.next_pieces[i];
                const shape = shapes[pieceType];
                if (shape) {
                    nextCtx.fillStyle = COLORS[pieceType];
                    const offsetY = i * 50 + 5;
                    for (let py = 0; py < 4; py++) {
                        for (let px = 0; px < 4; px++) {