#!/usr/bin/env python3
"""
Tetris Clone (Web App) for BlackBerry QNX - ES5 Compatible
- Compatible with TaskApp2.py (PORT declared, simple HTTP server)
- Classic Tetris game with keyboard controls
- ES5-only frontend (no external libraries), zero external web libs
- Optimized for BlackBerry Passport browser
"""

import http.server
import socketserver
import urllib.parse
import json
import os
import sys
import signal

# ---- App identity & TaskApp2 integration ----
PORT = 8040  # TaskApp2 scans for this exact pattern
APP_NAME = "Tetris Clone"

# ---- Utility: JSON response helper ----
def send_json(handler, status_code, payload):
    body = json.dumps(payload).encode("utf-8")
    handler.send_response(status_code)
    handler.send_header("Content-Type", "application/json")
    handler.send_header("Cache-Control", "no-store")
    handler.send_header("Content-Length", str(len(body)))
    handler.end_headers()
    handler.wfile.write(body)

# ---- HTTP Handler ----
class AppHandler(http.server.BaseHTTPRequestHandler):
    # Minimal logging (easier for TaskApp2 / pidin parsing)
    def log_message(self, fmt, *args):
        sys.stdout.write("%s - - [%s] %s\n" % (self.address_string(),
                                               self.log_date_time_string(),
                                               fmt % args))

    def do_GET(self):
        parsed = urllib.parse.urlparse(self.path)
        path = parsed.path

        if path == "/" or path == "/index.html":
            self.serve_html()
            return

        if path == "/api/status":
            self.handle_status()
            return

        # Everything else: 404
        self.send_error(404, "Not Found")

    def handle_status(self):
        """Status endpoint to check app availability"""
        status = {
            "app": APP_NAME,
            "port": PORT,
            "game": "Tetris Clone",
            "controls": "WASD + Space + P",
            "compatibility": "ES5 BlackBerry Passport"
        }
        send_json(self, 200, {"status": "ok", "info": status})

    def serve_html(self):
        html = """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Tetris Clone - BlackBerry QNX</title>
<style>
  body { 
    font-family: Arial, sans-serif; 
    background: #1a1a2e; 
    color: #fff; 
    margin: 0; 
    padding: 2px; 
    overflow-x: hidden;
    overflow-y: auto;
  }
  .game-container { 
    background: #222; 
    border: 1px solid #444; 
    border-radius: 4px; 
    padding: 5px; 
    text-align: center;
    width: 100%;
    max-width: 100vw;
  }
  h1 { 
    margin-top: 0; 
    font-size: 14px; 
    color: #4caf50; 
    margin-bottom: 4px;
  }
  .game-info {
    display: flex;
    justify-content: space-between;
    margin-bottom: 6px;
    font-size: 10px;
  }
  .score-info {
    color: #4caf50;
    font-weight: bold;
  }
  .level-info {
    color: #ffa500;
    font-weight: bold;
  }
  .lines-info {
    color: #ff6b6b;
    font-weight: bold;
  }
  #gameCanvas { 
    border: 1px solid #555; 
    background: #000; 
    display: block;
    margin: 0 auto 6px;
    max-width: 100%;
    height: auto;
  }
  .controls { 
    background: #2b2b2b; 
    padding: 4px; 
    border-radius: 4px; 
    margin-bottom: 6px;
    font-size: 9px;
  }
  .control-row {
    margin: 2px 0;
  }
  .control-key {
    background: #444;
    padding: 1px 3px;
    border-radius: 2px;
    font-family: monospace;
    margin: 0 1px;
  }
  button { 
    background: #4caf50; 
    border: 1px solid #388e3c; 
    color: #fff; 
    padding: 4px 8px; 
    border-radius: 4px; 
    cursor: pointer; 
    margin: 2px;
    font-size: 10px;
  }
  button:active { 
    background: #388e3c; 
  }
  button:disabled { 
    background: #666; 
    border-color: #555; 
    cursor: not-allowed; 
  }
  .status { 
    margin-top: 4px; 
    background: #2b2b2b; 
    padding: 4px; 
    border-radius: 3px; 
    min-height: 12px;
    font-size: 10px;
  }
  .ok { color: #7CFC00; }
  .err { color: #ff6b6b; }
  .warn { color: #ffa500; }
  .info { 
    background: #1e3a5f; 
    padding: 4px; 
    border-radius: 3px; 
    margin: 4px 0; 
    font-size: 8px; 
  }
  .game-over {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: rgba(0,0,0,0.9);
    padding: 20px;
    border-radius: 8px;
    text-align: center;
    display: none;
  }
  .next-piece {
    margin-top: 6px;
    font-size: 9px;
  }
  #nextCanvas {
    border: 1px solid #555;
    background: #000;
    margin-top: 3px;
  }
  .fullscreen {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    width: 100vw !important;
    height: 100vh !important;
    z-index: 9999 !important;
    background: #1a1a2e !important;
    padding: 5px !important;
    margin: 0 !important;
    border-radius: 0 !important;
    border: none !important;
  }
  .fullscreen #gameCanvas {
    width: auto !important;
    height: auto !important;
    max-width: 100vw !important;
    max-height: calc(100vh - 150px) !important;
    margin: 10px auto !important;
  }
  .fullscreen .game-info {
    font-size: 14px !important;
    margin-bottom: 15px !important;
  }
  .fullscreen .controls {
    font-size: 12px !important;
    padding: 10px !important;
    margin-bottom: 15px !important;
  }
  .fullscreen button {
    font-size: 14px !important;
    padding: 10px 15px !important;
    margin: 5px !important;
  }
</style>
</head>
<body>
  <div class="game-container">
    <h1>🎮 Tetris Clone</h1>
    
    <div class="info">
      <strong>Status:</strong> <span id="appStatus">Loading...</span>
    </div>

    <div class="game-info">
      <div class="score-info">Score: <span id="score">0</span></div>
      <div class="level-info">Level: <span id="level">1</span></div>
      <div class="lines-info">Lines: <span id="lines">0</span></div>
    </div>

    <canvas id="gameCanvas" width="160" height="320"></canvas>
    
    <div class="next-piece">
      Next Piece:<br>
      <canvas id="nextCanvas" width="50" height="50"></canvas>
    </div>

    <div class="controls">
      <div class="control-row">
        <span class="control-key">A</span> Move Left
        <span class="control-key">D</span> Move Right
        <span class="control-key">S</span> Soft Drop
      </div>
      <div class="control-row">
        <span class="control-key">W</span> Rotate
        <span class="control-key">Space</span> Hard Drop
        <span class="control-key">P</span> Pause
      </div>
    </div>

    <div>
      <button id="startBtn" onclick="startGame()">Start Game</button>
      <button id="pauseBtn" onclick="pauseGame()" disabled>Pause</button>
      <button id="fullscreenBtn" onclick="toggleFullscreen()">Fullscreen</button>
      <button onclick="checkStatus()">Check Status</button>
    </div>

    <div id="status" class="status">Ready to play! Press Start Game to begin.</div>
    
    <div class="game-over" id="gameOver">
      <h2>Game Over!</h2>
      <p>Final Score: <span id="finalScore">0</span></p>
      <p>Lines Cleared: <span id="finalLines">0</span></p>
      <p>Level Reached: <span id="finalLevel">1</span></p>
      <button onclick="startGame()">Play Again</button>
    </div>
    
    <p class="info">
      <strong>Controls:</strong> WASD for movement, Space for hard drop, P to pause.<br>
      <strong>TaskApp2:</strong> Detected via <code>PORT = 8040</code>. ES5-compatible game engine.
    </p>
  </div>

<script>
  // ES5 Tetris Game Engine
  var canvas, ctx, nextCanvas, nextCtx;
  var board = [];
  var currentPiece = null;
  var nextPiece = null;
  var score = 0;
  var level = 1;
  var lines = 0;
  var gameRunning = false;
  var gamePaused = false;
  var dropTime = 0;
  var dropInterval = 1000; // 1 second initially
  
  // Tetris pieces (tetrominoes)
  var pieces = [
    {
      shape: [
        [1, 1, 1, 1]
      ],
      color: '#00f5ff' // I piece - cyan
    },
    {
      shape: [
        [1, 1],
        [1, 1]
      ],
      color: '#ffff00' // O piece - yellow
    },
    {
      shape: [
        [0, 1, 0],
        [1, 1, 1]
      ],
      color: '#a000f0' // T piece - purple
    },
    {
      shape: [
        [0, 1, 1],
        [1, 1, 0]
      ],
      color: '#00f000' // S piece - green
    },
    {
      shape: [
        [1, 1, 0],
        [0, 1, 1]
      ],
      color: '#f00000' // Z piece - red
    },
    {
      shape: [
        [1, 0, 0],
        [1, 1, 1]
      ],
      color: '#f0a000' // J piece - orange
    },
    {
      shape: [
        [0, 0, 1],
        [1, 1, 1]
      ],
      color: '#0000f0' // L piece - blue
    }
  ];
  
  // Board dimensions
  var BOARD_WIDTH = 10;
  var BOARD_HEIGHT = 20;
  var CELL_SIZE = 16;
  
  // Initialize game
  function initGame() {
    canvas = document.getElementById('gameCanvas');
    ctx = canvas.getContext('2d');
    nextCanvas = document.getElementById('nextCanvas');
    nextCtx = nextCanvas.getContext('2d');
    
    // Initialize empty board (ES5 compatible)
    board = [];
    for (var y = 0; y < BOARD_HEIGHT; y++) {
      board[y] = [];
      for (var x = 0; x < BOARD_WIDTH; x++) {
        board[y][x] = 0;
      }
    }
    
    // Set up keyboard controls
    document.addEventListener('keydown', handleKeyPress);
    
    updateDisplay();
    drawBoard();
  }
  
  // Handle keyboard input
  function handleKeyPress(e) {
    if (!gameRunning || gamePaused) return;
    
    switch(e.keyCode) {
      case 65: // A key
        movePiece(-1, 0);
        break;
      case 68: // D key
        movePiece(1, 0);
        break;
      case 83: // S key
        movePiece(0, 1);
        break;
      case 87: // W key
        rotatePiece();
        break;
      case 32: // Space
        hardDrop();
        break;
      case 80: // P key
        pauseGame();
        break;
    }
    e.preventDefault();
  }
  
  // Create a new piece
  function createPiece() {
    var pieceIndex = Math.floor(Math.random() * pieces.length);
    var piece = pieces[pieceIndex];
    
    return {
      shape: piece.shape,
      color: piece.color,
      x: Math.floor(BOARD_WIDTH / 2) - Math.floor(piece.shape[0].length / 2),
      y: 0
    };
  }
  
  // Check if piece can be placed at position
  function isValidPosition(piece, dx, dy) {
    for (var y = 0; y < piece.shape.length; y++) {
      for (var x = 0; x < piece.shape[y].length; x++) {
        if (piece.shape[y][x]) {
          var newX = piece.x + x + dx;
          var newY = piece.y + y + dy;
          
          // Check boundaries
          if (newX < 0 || newX >= BOARD_WIDTH || newY >= BOARD_HEIGHT) {
            return false;
          }
          
          // Check collision with existing pieces
          if (newY >= 0 && board[newY][newX]) {
            return false;
          }
        }
      }
    }
    return true;
  }
  
  // Move piece
  function movePiece(dx, dy) {
    if (isValidPosition(currentPiece, dx, dy)) {
      currentPiece.x += dx;
      currentPiece.y += dy;
      drawBoard();
      return true;
    }
    return false;
  }
  
  // Rotate piece
  function rotatePiece() {
    var rotated = [];
    var shape = currentPiece.shape;
    
    // Create rotated shape
    for (var x = 0; x < shape[0].length; x++) {
      rotated[x] = [];
      for (var y = shape.length - 1; y >= 0; y--) {
        rotated[x][shape.length - 1 - y] = shape[y][x];
      }
    }
    
    var originalShape = currentPiece.shape;
    currentPiece.shape = rotated;
    
    if (!isValidPosition(currentPiece, 0, 0)) {
      currentPiece.shape = originalShape;
    } else {
      drawBoard();
    }
  }
  
  // Hard drop piece
  function hardDrop() {
    while (movePiece(0, 1)) {
      score += 2; // Bonus points for hard drop
    }
    placePiece();
  }
  
  // Place piece on board
  function placePiece() {
    try {
      for (var y = 0; y < currentPiece.shape.length; y++) {
        for (var x = 0; x < currentPiece.shape[y].length; x++) {
          if (currentPiece.shape[y][x]) {
            var boardX = currentPiece.x + x;
            var boardY = currentPiece.y + y;
            if (boardY >= 0 && boardY < BOARD_HEIGHT && boardX >= 0 && boardX < BOARD_WIDTH) {
              board[boardY][boardX] = currentPiece.color;
            }
          }
        }
      }
      
      // Check for completed lines
      clearLines();
      
      // Create next piece
      currentPiece = nextPiece;
      nextPiece = createPiece();
      
      // Check game over
      if (!isValidPosition(currentPiece, 0, 0)) {
        gameOver();
        return;
      }
      
      updateDisplay();
      drawBoard();
      drawNextPiece();
    } catch (e) {
      console.error('Place piece error:', e);
      setStatus('Place piece error: ' + e.message, 'err');
      gameRunning = false;
    }
  }
  
  // Clear completed lines
  function clearLines() {
    var linesCleared = 0;
    var newBoard = [];
    
    // Create new board without completed lines
    for (var y = 0; y < BOARD_HEIGHT; y++) {
      var lineComplete = true;
      for (var x = 0; x < BOARD_WIDTH; x++) {
        if (!board[y][x]) {
          lineComplete = false;
          break;
        }
      }
      
      if (!lineComplete) {
        newBoard.push(board[y]);
      } else {
        linesCleared++;
      }
    }
    
    // Add empty lines at the top (ES5 compatible)
    while (newBoard.length < BOARD_HEIGHT) {
      var emptyLine = [];
      for (var i = 0; i < BOARD_WIDTH; i++) {
        emptyLine[i] = 0;
      }
      newBoard.unshift(emptyLine);
    }
    
    // Update board
    board = newBoard;
    
    if (linesCleared > 0) {
      lines += linesCleared;
      score += linesCleared * 100 * level;
      level = Math.floor(lines / 10) + 1;
      dropInterval = Math.max(100, 1000 - (level - 1) * 100);
      updateDisplay();
    }
  }
  
  // Game loop
  function gameLoop(timestamp) {
    if (!gameRunning || gamePaused) return;
    
    try {
      if (timestamp - dropTime > dropInterval) {
        if (!movePiece(0, 1)) {
          placePiece();
        }
        dropTime = timestamp;
      }
      
      requestAnimationFrame(gameLoop);
    } catch (e) {
      console.error('Game loop error:', e);
      setStatus('Game error: ' + e.message, 'err');
      gameRunning = false;
    }
  }
  
  // Draw the game board
  function drawBoard() {
    // Clear canvas
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    // Draw board
    for (var y = 0; y < BOARD_HEIGHT; y++) {
      for (var x = 0; x < BOARD_WIDTH; x++) {
        if (board[y][x]) {
          ctx.fillStyle = board[y][x];
          ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
          ctx.strokeStyle = '#333';
          ctx.strokeRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
        }
      }
    }
    
    // Draw current piece
    if (currentPiece) {
      ctx.fillStyle = currentPiece.color;
      for (var y = 0; y < currentPiece.shape.length; y++) {
        for (var x = 0; x < currentPiece.shape[y].length; x++) {
          if (currentPiece.shape[y][x]) {
            var drawX = (currentPiece.x + x) * CELL_SIZE;
            var drawY = (currentPiece.y + y) * CELL_SIZE;
            ctx.fillRect(drawX, drawY, CELL_SIZE, CELL_SIZE);
            ctx.strokeStyle = '#333';
            ctx.strokeRect(drawX, drawY, CELL_SIZE, CELL_SIZE);
          }
        }
      }
    }
  }
  
  // Draw next piece
  function drawNextPiece() {
    nextCtx.fillStyle = '#000';
    nextCtx.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
    
    if (nextPiece) {
      nextCtx.fillStyle = nextPiece.color;
      var cellSize = 12;
      var offsetX = (nextCanvas.width - nextPiece.shape[0].length * cellSize) / 2;
      var offsetY = (nextCanvas.height - nextPiece.shape.length * cellSize) / 2;
      
      for (var y = 0; y < nextPiece.shape.length; y++) {
        for (var x = 0; x < nextPiece.shape[y].length; x++) {
          if (nextPiece.shape[y][x]) {
            nextCtx.fillRect(offsetX + x * cellSize, offsetY + y * cellSize, cellSize, cellSize);
          }
        }
      }
    }
  }
  
  // Update display
  function updateDisplay() {
    document.getElementById('score').textContent = score;
    document.getElementById('level').textContent = level;
    document.getElementById('lines').textContent = lines;
  }
  
  // Start game
  function startGame() {
    if (gameRunning) return;
    
    // Reset game state
    score = 0;
    level = 1;
    lines = 0;
    dropInterval = 1000;
    
    // Clear board (ES5 compatible)
    board = [];
    for (var y = 0; y < BOARD_HEIGHT; y++) {
      board[y] = [];
      for (var x = 0; x < BOARD_WIDTH; x++) {
        board[y][x] = 0;
      }
    }
    
    // Create pieces
    currentPiece = createPiece();
    nextPiece = createPiece();
    
    gameRunning = true;
    gamePaused = false;
    
    document.getElementById('startBtn').disabled = true;
    document.getElementById('pauseBtn').disabled = false;
    document.getElementById('gameOver').style.display = 'none';
    
    updateDisplay();
    drawBoard();
    drawNextPiece();
    
    setStatus('Game started! Use arrow keys to control pieces.', 'ok');
    
    dropTime = performance.now();
    requestAnimationFrame(gameLoop);
  }
  
  // Pause game
  function pauseGame() {
    if (!gameRunning) return;
    
    gamePaused = !gamePaused;
    
    if (gamePaused) {
      setStatus('Game paused. Press P to resume.', 'warn');
    } else {
      setStatus('Game resumed!', 'ok');
      dropTime = performance.now();
      requestAnimationFrame(gameLoop);
    }
  }
  
  // Game over
  function gameOver() {
    gameRunning = false;
    gamePaused = false;
    
    document.getElementById('startBtn').disabled = false;
    document.getElementById('pauseBtn').disabled = true;
    
    document.getElementById('finalScore').textContent = score;
    document.getElementById('finalLines').textContent = lines;
    document.getElementById('finalLevel').textContent = level;
    document.getElementById('gameOver').style.display = 'block';
    
    setStatus('Game Over! Press "Play Again" to restart.', 'err');
  }
  
  // Status display
  function setStatus(message, className) {
    var el = document.getElementById('status');
    el.innerHTML = message;
    el.className = 'status ' + (className || 'ok');
  }
  
  // Toggle fullscreen using WebKit API
  function toggleFullscreen() {
    var container = document.querySelector('.game-container');
    var fullscreenBtn = document.getElementById('fullscreenBtn');
    
    // Check if we're currently in fullscreen
    if (document.webkitIsFullScreen || document.webkitFullscreenElement) {
      // Exit fullscreen
      if (document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
      } else if (document.webkitCancelFullScreen) {
        document.webkitCancelFullScreen();
      }
      fullscreenBtn.textContent = 'Fullscreen';
      setStatus('Exited fullscreen mode.', 'ok');
    } else {
      // Enter fullscreen
      if (container.webkitRequestFullscreen) {
        container.webkitRequestFullscreen();
      } else if (container.webkitRequestFullScreen) {
        container.webkitRequestFullScreen();
      } else if (container.requestFullscreen) {
        container.requestFullscreen();
      } else {
        // Fallback to CSS fullscreen if browser doesn't support it
        container.classList.add('fullscreen');
        fullscreenBtn.textContent = 'Exit Fullscreen';
        setStatus('Entered CSS fullscreen mode! Press ESC or button to exit.', 'ok');
        return;
      }
      fullscreenBtn.textContent = 'Exit Fullscreen';
      setStatus('Entered fullscreen mode! Press ESC or button to exit.', 'ok');
    }
  }
  
  // Handle fullscreen change events
  document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
  document.addEventListener('fullscreenchange', handleFullscreenChange);
  
  function handleFullscreenChange() {
    var container = document.querySelector('.game-container');
    var fullscreenBtn = document.getElementById('fullscreenBtn');
    
    if (document.webkitIsFullScreen || document.webkitFullscreenElement || document.fullscreenElement) {
      fullscreenBtn.textContent = 'Exit Fullscreen';
      container.classList.add('fullscreen');
    } else {
      fullscreenBtn.textContent = 'Fullscreen';
      container.classList.remove('fullscreen');
    }
  }
  
  // Handle ESC key to exit fullscreen
  document.addEventListener('keydown', function(e) {
    if (e.keyCode === 27) { // ESC key
      if (document.webkitIsFullScreen || document.webkitFullscreenElement || document.fullscreenElement) {
        toggleFullscreen();
      } else {
        var container = document.querySelector('.game-container');
        if (container.classList.contains('fullscreen')) {
          container.classList.remove('fullscreen');
          document.getElementById('fullscreenBtn').textContent = 'Fullscreen';
          setStatus('Exited CSS fullscreen mode.', 'ok');
        }
      }
    }
  });
  
  // Check status
  function checkStatus() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/api/status', true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        try {
          var res = JSON.parse(xhr.responseText);
          if (xhr.status === 200 && res.status === 'ok') {
            var info = res.info;
            var statusText = 'App: ' + info.app + ', Port: ' + info.port + 
                           ', Game: ' + info.game + ', Controls: ' + info.controls;
            document.getElementById('appStatus').textContent = statusText;
            document.getElementById('appStatus').className = 'ok';
          } else {
            document.getElementById('appStatus').textContent = 'Error checking status';
            document.getElementById('appStatus').className = 'err';
          }
        } catch (e) {
          document.getElementById('appStatus').textContent = 'Status check failed';
          document.getElementById('appStatus').className = 'err';
        }
      }
    };
    xhr.send();
  }
  
  // Initialize game when page loads
  window.onload = function() {
    initGame();
    checkStatus();
  };
</script>
</body>
</html>"""
        body = html.encode("utf-8")
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

# ---- Graceful shutdown (SIGTERM) ----
def _signal_handler(sig, frame):
    print("Shutting down %s..." % APP_NAME)
    sys.exit(0)

signal.signal(signal.SIGTERM, _signal_handler)

# ---- Threaded server (keeps UI responsive) ----
class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    daemon_threads = True
    allow_reuse_address = True

def run_server(port=PORT):
    try:
        with ThreadingTCPServer(("", port), AppHandler) as httpd:
            print("Starting %s on port %d..." % (APP_NAME, port))
            print("Access at: http://localhost:%d" % port)
            print("Game: Classic Tetris with keyboard controls")
            print("Controls: WASD (move/rotate), Space (hard drop), P (pause)")
            try:
                httpd.serve_forever()
            except KeyboardInterrupt:
                print("\nShutting down %s..." % APP_NAME)
    except OSError as e:
        if e.errno == 48:  # Address already in use
            print(f"Error: Port {port} is already in use.")
        else:
            print(f"Error starting server: {e}")
        sys.exit(1)

if __name__ == "__main__":
    run_server()

