// Core Tetris Game Logic

const BOARD_WIDTH = 10;
const BOARD_HEIGHT = 20;
const CELL_SIZE = 24;

// Tetromino definitions with colors
const TETROMINOS = {
  I: { shape: [[0,0,0,0], [1,1,1,1], [0,0,0,0], [0,0,0,0]], color: '#00ffff' },
  O: { shape: [[1,1], [1,1]], color: '#ffff00' },
  T: { shape: [[0,1,0], [1,1,1], [0,0,0]], color: '#800080' },
  S: { shape: [[0,1,1], [1,1,0], [0,0,0]], color: '#00ff00' },
  Z: { shape: [[1,1,0], [0,1,1], [0,0,0]], color: '#ff0000' },
  J: { shape: [[1,0,0], [1,1,1], [0,0,0]], color: '#0000ff' },
  L: { shape: [[0,0,1], [1,1,1], [0,0,0]], color: '#ffa500' }
};

const TETROMINO_KEYS = Object.keys(TETROMINOS);
const GARbage_COLOR = '#666666';

class TetrisGame {
  constructor(playerId) {
    this.playerId = playerId;
    this.board = this.createEmptyBoard();
    this.currentPiece = null;
    this.nextPiece = null;
    this.holdPiece = null;
    this.canHold = true;
    this.score = 0;
    this.lines = 0;
    this.level = 1;
    this.gameOver = false;
    this.eliminated = false;

    // Game speed (milliseconds per drop)
    this.dropInterval = 1000;
    this.lastDrop = 0;

    // Input lock for delay hits
    this.inputLocked = false;
    this.lockUntil = 0;
  }

  createEmptyBoard() {
    return Array(BOARD_HEIGHT).fill(null).map(() => Array(BOARD_WIDTH).fill(0));
  }

  initialize() {
    this.board = this.createEmptyBoard();
    this.score = 0;
    this.lines = 0;
    this.level = 1;
    this.gameOver = false;
    this.eliminated = false;
    this.dropInterval = 1000;
    this.holdPiece = null;
    this.canHold = true;
    this.currentPiece = this.spawnPiece();
    this.nextPiece = this.getRandomPiece();

    if (!this.currentPiece) {
      this.gameOver = true;
    }
  }

  getRandomPiece() {
    const key = TETROMINO_KEYS[Math.floor(Math.random() * TETROMINO_KEYS.length)];
    const tetromino = TETROMINOS[key];
    return {
      type: key,
      shape: JSON.parse(JSON.stringify(tetromino.shape)),
      color: tetromino.color,
      x: Math.floor(BOARD_WIDTH / 2) - Math.floor(tetromino.shape[0].length / 2),
      y: 0
    };
  }

  spawnPiece() {
    const piece = this.nextPiece;
    this.nextPiece = this.getRandomPiece();

    // Check if spawn position is valid
    if (!this.isValidPosition(piece.x, piece.y, piece.shape)) {
      return null;
    }

    return piece;
  }

  isValidPosition(x, y, shape) {
    for (let row = 0; row < shape.length; row++) {
      for (let col = 0; col < shape[row].length; col++) {
        if (shape[row][col]) {
          const newX = x + col;
          const newY = y + row;

          // Check bounds
          if (newX < 0 || newX >= BOARD_WIDTH || newY >= BOARD_HEIGHT) {
            return false;
          }

          // Check collision with locked pieces (only if on board)
          if (newY >= 0 && this.board[newY][newX]) {
            return false;
          }
        }
      }
    }
    return true;
  }

  move(direction) {
    if (this.gameOver || this.inputLocked) return false;

    const newX = this.currentPiece.x + (direction === 'left' ? -1 : 1);

    if (this.isValidPosition(newX, this.currentPiece.y, this.currentPiece.shape)) {
      this.currentPiece.x = newX;
      return true;
    }
    return false;
  }

  rotate() {
    if (this.gameOver || this.inputLocked) return false;

    const originalShape = this.currentPiece.shape;
    const rows = originalShape.length;
    const cols = originalShape[0].length;

    // Rotate 90 degrees clockwise
    const rotated = Array(cols).fill(null).map(() => Array(rows).fill(0));
    for (let row = 0; row < rows; row++) {
      for (let col = 0; col < cols; col++) {
        rotated[col][rows - 1 - row] = originalShape[row][col];
      }
    }

    // Try rotation with wall kicks
    const kicks = [0, -1, 1, -2, 2];
    for (const kick of kicks) {
      if (this.isValidPosition(this.currentPiece.x + kick, this.currentPiece.y, rotated)) {
        this.currentPiece.shape = rotated;
        this.currentPiece.x += kick;
        return true;
      }
    }

    return false;
  }

  drop() {
    if (this.gameOver || this.inputLocked) return false;

    const newY = this.currentPiece.y + 1;

    if (this.isValidPosition(this.currentPiece.x, newY, this.currentPiece.shape)) {
      this.currentPiece.y = newY;
      return true;
    }

    // Lock the piece
    return this.lockPiece();
  }

  hardDrop() {
    if (this.gameOver || this.inputLocked) return 0;

    let dropped = 0;
    while (this.isValidPosition(this.currentPiece.x, this.currentPiece.y + 1, this.currentPiece.shape)) {
      this.currentPiece.y++;
      dropped++;
    }

    this.lockPiece();
    return dropped;
  }

  hold() {
    if (this.gameOver || this.inputLocked || !this.canHold) return false;

    if (this.holdPiece === null) {
      // First hold - store current piece and spawn next
      this.holdPiece = {
        type: this.currentPiece.type,
        shape: JSON.parse(JSON.stringify(this.currentPiece.shape)),
        color: this.currentPiece.color
      };
      this.currentPiece = this.nextPiece;
      this.nextPiece = this.getRandomPiece();
    } else {
      // Swap with held piece
      const temp = {
        type: this.currentPiece.type,
        shape: JSON.parse(JSON.stringify(this.currentPiece.shape)),
        color: this.currentPiece.color
      };
      this.currentPiece = {
        ...this.holdPiece,
        shape: JSON.parse(JSON.stringify(this.holdPiece.shape)),
        x: Math.floor(BOARD_WIDTH / 2) - Math.floor(this.holdPiece.shape[0].length / 2),
        y: 0
      };
      this.holdPiece = temp;
    }

    this.canHold = false;
    return true;
  }

  lockPiece() {
    if (!this.currentPiece) return false;

    // Lock piece into board
    for (let row = 0; row < this.currentPiece.shape.length; row++) {
      for (let col = 0; col < this.currentPiece.shape[row].length; col++) {
        if (this.currentPiece.shape[row][col]) {
          const boardY = this.currentPiece.y + row;
          const boardX = this.currentPiece.x + col;

          if (boardY >= 0 && boardY < BOARD_HEIGHT && boardX >= 0 && boardX < BOARD_WIDTH) {
            this.board[boardY][boardX] = this.currentPiece.color;
          }
        }
      }
    }

    // Check for game over (piece locked above visible area)
    // Only eliminate if any part of the piece is above y=0 (not visible)
    let pieceAboveBoard = false;
    for (let row = 0; row < this.currentPiece.shape.length; row++) {
      for (let col = 0; col < this.currentPiece.shape[row].length; col++) {
        if (this.currentPiece.shape[row][col]) {
          const boardY = this.currentPiece.y + row;
          if (boardY < 0) {
            pieceAboveBoard = true;
            break;
          }
        }
      }
      if (pieceAboveBoard) break;
    }
    if (pieceAboveBoard) {
      this.gameOver = true;
      this.eliminated = true;
    }

    // Clear completed rows
    const cleared = this.clearRows();

    // Spawn new piece
    this.currentPiece = this.spawnPiece();
    if (!this.currentPiece) {
      this.gameOver = true;
      this.eliminated = true;
    }

    // Reset canHold for the new piece
    this.canHold = true;

    return cleared;
  }

  clearRows() {
    let rowsCleared = 0;

    for (let row = BOARD_HEIGHT - 1; row >= 0; row--) {
      if (this.board[row].every(cell => cell !== 0)) {
        // Remove the row
        this.board.splice(row, 1);
        // Add empty row at top
        this.board.unshift(Array(BOARD_WIDTH).fill(0));
        rowsCleared++;
        row++; // Check same row again
      }
    }

    if (rowsCleared > 0) {
      this.lines += rowsCleared;
      this.updateScore(rowsCleared);
      this.updateLevel();
    }

    return rowsCleared;
  }

  updateScore(rowsCleared) {
    const points = [0, 100, 300, 500, 800];
    this.score += points[rowsCleared] * this.level;
  }

  updateLevel() {
    this.level = Math.floor(this.lines / 10) + 1;
    this.dropInterval = Math.max(100, 1000 - (this.level - 1) * 100);
  }

  getGhostY() {
    if (!this.currentPiece) return null;

    let ghostY = this.currentPiece.y;
    while (this.isValidPosition(this.currentPiece.x, ghostY + 1, this.currentPiece.shape)) {
      ghostY++;
    }
    return ghostY;
  }

  receiveGarbage(rows) {
    if (this.gameOver) return;

    // Add garbage rows at bottom
    for (let i = 0; i < rows; i++) {
      // Remove top row (simulating overflow)
      this.board.pop();

      // Add garbage row with random gap
      const garbageRow = Array(BOARD_WIDTH).fill(GARbage_COLOR);
      const gap = Math.floor(Math.random() * BOARD_WIDTH);
      garbageRow[gap] = 0;
      this.board.unshift(garbageRow);
    }

    // Check if garbage caused game over
    for (let row = 0; row < 2; row++) {
      for (let col = 0; col < BOARD_WIDTH; col++) {
        if (this.board[row][col] !== 0) {
          this.gameOver = true;
          this.eliminated = true;
          return;
        }
      }
    }
  }

  applyDelayHit(duration) {
    this.inputLocked = true;
    this.lockUntil = Date.now() + duration;
  }

  update(deltaTime) {
    if (this.gameOver) return;

    // Check if input lock has expired
    if (this.inputLocked && Date.now() >= this.lockUntil) {
      this.inputLocked = false;
    }

    // Auto drop
    if (!this.inputLocked && Date.now() - this.lastDrop >= this.dropInterval) {
      this.drop();
      this.lastDrop = Date.now();
    }
  }

  getState() {
    const ghostY = this.currentPiece ? this.getGhostY() : null;
    return {
      playerId: this.playerId,
      board: JSON.parse(JSON.stringify(this.board)),
      currentPiece: this.currentPiece ? JSON.parse(JSON.stringify(this.currentPiece)) : null,
      ghostY: ghostY,
      nextPiece: this.nextPiece ? JSON.parse(JSON.stringify(this.nextPiece)) : null,
      holdPiece: this.holdPiece ? JSON.parse(JSON.stringify(this.holdPiece)) : null,
      canHold: this.canHold,
      score: this.score,
      lines: this.lines,
      level: this.level,
      gameOver: this.gameOver,
      eliminated: this.eliminated
    };
  }

  loadState(state) {
    this.board = JSON.parse(JSON.stringify(state.board));
    this.currentPiece = state.currentPiece ? JSON.parse(JSON.stringify(state.currentPiece)) : null;
    this.nextPiece = state.nextPiece ? JSON.parse(JSON.stringify(state.nextPiece)) : null;
    this.holdPiece = state.holdPiece ? JSON.parse(JSON.stringify(state.holdPiece)) : null;
    this.canHold = state.canHold !== undefined ? state.canHold : true;
    this.score = state.score;
    this.lines = state.lines;
    this.level = state.level;
    this.gameOver = state.gameOver;
    this.eliminated = state.eliminated;
    this.sequenceIndex = state.sequenceIndex || 0;
    this.dropInterval = Math.max(100, 1000 - (this.level - 1) * 100);
  }
}

// Export for use in other modules
window.TetrisGame = TetrisGame;
window.TETROMINOS = TETROMINOS;
window.BOARD_WIDTH = BOARD_WIDTH;
window.BOARD_HEIGHT = BOARD_HEIGHT;
window.CELL_SIZE = CELL_SIZE;
window.GARBAGE_COLOR = GARbage_COLOR;
