/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Tetromino class
var Tetromino = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.type = null;
	self.shape = null; // array of [x,y]
	self.rotation = 0;
	self.blocks = [];
	self.x = 0; // board col
	self.y = 0; // board row
	self.isPowerup = false;
	self.powerupType = null;
	// Initialize tetromino
	self.init = function (type, isPowerup) {
		self.type = type;
		self.isPowerup = !!isPowerup;
		self.rotation = 0;
		self.shape = [];
		// Copy shape
		var base = TETROMINO_SHAPES[type][0];
		for (var i = 0; i < base.length; i++) {
			self.shape.push([base[i][0], base[i][1]]);
		}
		self.blocks = [];
		self.removeChildren();
		for (var i = 0; i < self.shape.length; i++) {
			var block;
			if (self.isPowerup && i === 0) {
				block = self.attachAsset('powerup', {
					anchorX: 0.5,
					anchorY: 0.5,
					width: CELL_SIZE,
					height: CELL_SIZE
				});
				self.powerupType = 'clearLine'; // Only one powerup for MVP
			} else {
				block = self.attachAsset(self.type, {
					anchorX: 0.5,
					anchorY: 0.5,
					width: CELL_SIZE,
					height: CELL_SIZE
				});
			}
			self.blocks.push(block);
			// Overlay grid lines for each block
			var gridOverlay = new Container();
			var vlineL = LK.getAsset('O', {
				anchorX: 0.5,
				anchorY: 0,
				width: 4,
				height: CELL_SIZE,
				color: 0x222222 // match board grid color
			});
			vlineL.x = -CELL_SIZE / 2;
			vlineL.y = -CELL_SIZE / 2;
			vlineL.alpha = 0.25;
			gridOverlay.addChild(vlineL);
			var vlineR = LK.getAsset('O', {
				anchorX: 0.5,
				anchorY: 0,
				width: 4,
				height: CELL_SIZE,
				color: 0x222222 // match board grid color
			});
			vlineR.x = CELL_SIZE / 2;
			vlineR.y = -CELL_SIZE / 2;
			vlineR.alpha = 0.25;
			gridOverlay.addChild(vlineR);
			var hlineT = LK.getAsset('O', {
				anchorX: 0,
				anchorY: 0.5,
				width: CELL_SIZE,
				height: 4,
				color: 0x222222 // match board grid color
			});
			hlineT.x = -CELL_SIZE / 2;
			hlineT.y = -CELL_SIZE / 2;
			hlineT.alpha = 0.25;
			gridOverlay.addChild(hlineT);
			var hlineB = LK.getAsset('O', {
				anchorX: 0,
				anchorY: 0.5,
				width: CELL_SIZE,
				height: 4,
				color: 0x222222 // match board grid color
			});
			hlineB.x = -CELL_SIZE / 2;
			hlineB.y = CELL_SIZE / 2;
			hlineB.alpha = 0.25;
			gridOverlay.addChild(hlineB);
			block.addChild(gridOverlay);
		}
		self.x = Math.floor(BOARD_COLS / 2) - 2;
		self.y = 0;
		self.updateBlockPositions();
	};
	// Update block positions
	self.updateBlockPositions = function () {
		for (var i = 0; i < self.shape.length; i++) {
			var pos = self.shape[i];
			self.blocks[i].x = (self.x + pos[0]) * CELL_SIZE + CELL_SIZE / 2;
			self.blocks[i].y = (self.y + pos[1]) * CELL_SIZE + CELL_SIZE / 2;
		}
	};
	// Try move
	self.tryMove = function (dx, dy, board) {
		if (self.canMove(dx, dy, self.shape, board)) {
			self.x += dx;
			self.y += dy;
			self.updateBlockPositions();
			return true;
		}
		return false;
	};
	// Try rotate
	self.tryRotate = function (board) {
		var rotated = rotateShape(self.shape);
		if (self.canMove(0, 0, rotated, board)) {
			self.shape = rotated;
			self.updateBlockPositions();
			return true;
		}
		return false;
	};
	// Can move/rotate
	self.canMove = function (dx, dy, shape, board) {
		for (var i = 0; i < shape.length; i++) {
			var nx = self.x + shape[i][0] + dx;
			var ny = self.y + shape[i][1] + dy;
			if (nx < 0 || nx >= BOARD_COLS || ny < 0 || ny >= BOARD_ROWS) {
				return false;
			}
			if (board[ny][nx]) {
				return false;
			}
		}
		return true;
	};
	// Lock tetromino into board
	self.lockToBoard = function (board, blockRefs) {
		for (var i = 0; i < self.shape.length; i++) {
			var nx = self.x + self.shape[i][0];
			var ny = self.y + self.shape[i][1];
			board[ny][nx] = self.isPowerup && i === 0 ? 'powerup' : self.type;
			blockRefs[ny][nx] = self.blocks[i];
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	// No title, no description
	// Always backgroundColor is black
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// Board state: 2D array [row][col], 0 = empty, else type string
// Tetromino shapes (classic + new)
// Colors for each tetromino type
// New shapes
// Power-up block
// Power-up effect
// Tetromino definitions (relative coordinates for each block in the tetromino)
var TETROMINO_SHAPES = {
	O: [[[1, 0], [2, 0], [1, 1], [2, 1]]],
	T: [[[1, 0], [0, 1], [1, 1], [2, 1]]],
	S: [[[1, 0], [2, 0], [0, 1], [1, 1]]],
	Z: [[[0, 0], [1, 0], [1, 1], [2, 1]]],
	J: [[[0, 0], [0, 1], [1, 1], [2, 1]]],
	L: [[[2, 0], [0, 1], [1, 1], [2, 1]]],
	U: [[[0, 0], [2, 0], [0, 1], [1, 1], [2, 1]]],
	P: [[[0, 0], [1, 0], [0, 1], [1, 1], [0, 2]]],
	Dot: [[[1, 1]]]
};
var TETROMINO_TYPES = ['O', 'T', 'S', 'Z', 'J', 'L', 'U', 'P'];
var POWERUP_CHANCE = 0.08; // 8% chance for a powerup block
// Board settings
var BOARD_COLS = 8; // Reduced width for fewer columns, block size unchanged
var BOARD_ROWS = 20;
var CELL_SIZE = 90; // px, fits well on 2048x2732
var BOARD_OFFSET_X = Math.floor((2048 - BOARD_COLS * CELL_SIZE) / 2) - 80; // Shift left to make space for preview
var BOARD_OFFSET_Y = 300;
// Helper: rotate a shape (array of [x,y]) 90deg clockwise
function rotateShape(shape) {
	var maxX = 0,
		maxY = 0;
	for (var i = 0; i < shape.length; i++) {
		if (shape[i][0] > maxX) {
			maxX = shape[i][0];
		}
		if (shape[i][1] > maxY) {
			maxY = shape[i][1];
		}
	}
	var rotated = [];
	for (var i = 0; i < shape.length; i++) {
		var x = shape[i][0],
			y = shape[i][1];
		rotated.push([maxY - y, x]);
	}
	return rotated;
}
var board = [];
var blockRefs = []; // 2D array of block display objects
for (var r = 0; r < BOARD_ROWS; r++) {
	board[r] = [];
	blockRefs[r] = [];
	for (var c = 0; c < BOARD_COLS; c++) {
		board[r][c] = 0;
		blockRefs[r][c] = null;
	}
}
// Score and level
var score = 0;
var level = 1;
var linesCleared = 0;
var fallInterval = 1000; // ms, decreases with level
// GUI
var scoreTxt = new Text2('0', {
	size: 100,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Lv.1', {
	size: 60,
	fill: "#fff"
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 110;
// Next tetromino preview
var nextTetrominoType = null;
var nextTetrominoPreview = new Container();
game.addChild(nextTetrominoPreview);
// Current falling tetromino
var currentTetromino = null;
// Game state
var isGameOver = false;
var isDropping = false;
var dropTimer = null;
var moveTimer = null;
var moveDir = 0; // -1 left, 1 right, 0 none
// Helper: spawn new tetromino
function spawnTetromino() {
	var type = nextTetrominoType;
	if (!type) {
		type = TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)];
	}
	var isPowerup = Math.random() < POWERUP_CHANCE;
	var tetro = new Tetromino();
	tetro.init(type, isPowerup);
	game.addChild(tetro);
	currentTetromino = tetro;
	// Next
	nextTetrominoType = TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)];
	updateNextPreview();
	// Flash effect on next preview
	if (nextTetrominoPreview.children && nextTetrominoPreview.children.length > 0) {
		for (var i = 0; i < nextTetrominoPreview.children.length; i++) {
			LK.effects.flashObject(nextTetrominoPreview.children[i], 0xffffff, 180);
		}
	}
	// If cannot place, game over
	if (!tetro.canMove(0, 0, tetro.shape, board)) {
		isGameOver = true;
		LK.effects.flashScreen(0xff0000, 1000);
		LK.showGameOver();
	}
}
// Helper: update next preview
function updateNextPreview() {
	nextTetrominoPreview.removeChildren();
	var type = nextTetrominoType;
	if (!type) {
		return;
	}
	var shape = TETROMINO_SHAPES[type][0];
	// Find min/max for centering
	var minX = 99,
		maxX = -99,
		minY = 99,
		maxY = -99;
	for (var i = 0; i < shape.length; i++) {
		if (shape[i][0] < minX) {
			minX = shape[i][0];
		}
		if (shape[i][0] > maxX) {
			maxX = shape[i][0];
		}
		if (shape[i][1] < minY) {
			minY = shape[i][1];
		}
		if (shape[i][1] > maxY) {
			maxY = shape[i][1];
		}
	}
	var previewWidth = (maxX - minX + 1) * CELL_SIZE;
	var previewHeight = (maxY - minY + 1) * CELL_SIZE;
	for (var i = 0; i < shape.length; i++) {
		var block = LK.getAsset(type, {
			anchorX: 0.5,
			anchorY: 0.5,
			width: CELL_SIZE,
			height: CELL_SIZE
		});
		block.x = (shape[i][0] - minX) * CELL_SIZE + CELL_SIZE / 2;
		block.y = (shape[i][1] - minY) * CELL_SIZE + CELL_SIZE / 2;
		nextTetrominoPreview.addChild(block);
	}
	// Place preview to the right of the board, fully visible and inside the screen
	nextTetrominoPreview.x = BOARD_OFFSET_X + BOARD_COLS * CELL_SIZE + 120;
	nextTetrominoPreview.y = BOARD_OFFSET_Y + 200;
}
// Helper: clear full lines
function clearLines() {
	var lines = [];
	for (var r = 0; r < BOARD_ROWS; r++) {
		var full = true;
		for (var c = 0; c < BOARD_COLS; c++) {
			if (!board[r][c]) {
				full = false;
				break;
			}
		}
		if (full) {
			lines.push(r);
		}
	}
	if (lines.length === 0) {
		return 0;
	}
	// Animate and remove lines
	for (var i = 0; i < lines.length; i++) {
		var row = lines[i];
		for (var c = 0; c < BOARD_COLS; c++) {
			if (blockRefs[row][c]) {
				tween(blockRefs[row][c], {
					alpha: 0
				}, {
					duration: 200,
					onFinish: function (obj) {
						if (obj.parent) {
							obj.parent.removeChild(obj);
						}
					}.bind(null, blockRefs[row][c])
				});
			}
		}
	}
	// Remove from board
	for (var i = 0; i < lines.length; i++) {
		var row = lines[i];
		for (var c = 0; c < BOARD_COLS; c++) {
			board[row][c] = 0;
			blockRefs[row][c] = null;
		}
	}
	// Drop above lines
	for (var i = lines.length - 1; i >= 0; i--) {
		var row = lines[i];
		for (var r = row - 1; r >= 0; r--) {
			for (var c = 0; c < BOARD_COLS; c++) {
				board[r + 1][c] = board[r][c];
				blockRefs[r + 1][c] = blockRefs[r][c];
				if (blockRefs[r + 1][c]) {
					tween(blockRefs[r + 1][c], {
						y: blockRefs[r + 1][c].y + CELL_SIZE
					}, {
						duration: 100
					});
				}
			}
		}
		// Clear top row
		for (var c = 0; c < BOARD_COLS; c++) {
			board[0][c] = 0;
			blockRefs[0][c] = null;
		}
	}
	return lines.length;
}
// Helper: activate powerup
function activatePowerup(row) {
	// For MVP: clear the row where powerup landed
	for (var c = 0; c < BOARD_COLS; c++) {
		if (blockRefs[row][c]) {
			tween(blockRefs[row][c], {
				alpha: 0
			}, {
				duration: 200,
				onFinish: function (obj) {
					if (obj.parent) {
						obj.parent.removeChild(obj);
					}
				}.bind(null, blockRefs[row][c])
			});
			board[row][c] = 0;
			blockRefs[row][c] = null;
		}
	}
	// Drop above lines
	for (var r = row - 1; r >= 0; r--) {
		for (var c = 0; c < BOARD_COLS; c++) {
			board[r + 1][c] = board[r][c];
			blockRefs[r + 1][c] = blockRefs[r][c];
			if (blockRefs[r + 1][c]) {
				tween(blockRefs[r + 1][c], {
					y: blockRefs[r + 1][c].y + CELL_SIZE
				}, {
					duration: 100
				});
			}
		}
	}
	// Clear top row
	for (var c = 0; c < BOARD_COLS; c++) {
		board[0][c] = 0;
		blockRefs[0][c] = null;
	}
}
// Helper: update score/level
function updateScore(lines) {
	var points = [0, 100, 300, 500, 800];
	score += points[lines] || 0;
	linesCleared += lines;
	scoreTxt.setText(score);
	var newLevel = 1 + Math.floor(linesCleared / 10);
	if (newLevel !== level) {
		level = newLevel;
		levelTxt.setText('Lv.' + level);
		fallInterval = Math.max(150, 1000 - (level - 1) * 100);
	}
}
// Helper: draw board grid (for visual reference)
var gridLines = new Container();
game.addChild(gridLines);
// Draw faint cell backgrounds for subtle grid
for (var r = 0; r < BOARD_ROWS; r++) {
	for (var c = 0; c < BOARD_COLS; c++) {
		var cell = LK.getAsset('O', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: CELL_SIZE - 4,
			height: CELL_SIZE - 4,
			color: 0x222222
		});
		cell.x = c * CELL_SIZE + CELL_SIZE / 2;
		cell.y = r * CELL_SIZE + CELL_SIZE / 2;
		cell.alpha = 0.15;
		gridLines.addChild(cell);
	}
}
// Draw vertical grid lines
for (var c = 0; c <= BOARD_COLS; c++) {
	var vline = LK.getAsset('O', {
		anchorX: 0.5,
		anchorY: 0,
		width: 4,
		height: CELL_SIZE * BOARD_ROWS,
		color: 0x222222 // darker gray for better contrast
	});
	vline.x = c * CELL_SIZE;
	vline.y = 0;
	vline.alpha = 0.35;
	gridLines.addChild(vline);
}
// Draw horizontal grid lines
for (var r = 0; r <= BOARD_ROWS; r++) {
	var hline = LK.getAsset('O', {
		anchorX: 0,
		anchorY: 0.5,
		width: CELL_SIZE * BOARD_COLS,
		height: 4,
		color: 0x222222 // darker gray for better contrast
	});
	hline.x = 0;
	hline.y = r * CELL_SIZE;
	hline.alpha = 0.35;
	gridLines.addChild(hline);
}
gridLines.x = BOARD_OFFSET_X;
gridLines.y = BOARD_OFFSET_Y;
// Move all blocks/containers to board offset
function updateBoardDisplayOffsets() {
	gridLines.x = BOARD_OFFSET_X;
	gridLines.y = BOARD_OFFSET_Y;
	for (var r = 0; r < BOARD_ROWS; r++) {
		for (var c = 0; c < BOARD_COLS; c++) {
			if (blockRefs[r][c]) {
				blockRefs[r][c].x = c * CELL_SIZE + CELL_SIZE / 2 + BOARD_OFFSET_X;
				blockRefs[r][c].y = r * CELL_SIZE + CELL_SIZE / 2 + BOARD_OFFSET_Y;
			}
		}
	}
	if (currentTetromino) {
		for (var i = 0; i < currentTetromino.blocks.length; i++) {
			currentTetromino.blocks[i].x += BOARD_OFFSET_X;
			currentTetromino.blocks[i].y += BOARD_OFFSET_Y;
		}
	}
}
// Remove board offset from tetromino before logic
function removeTetrominoOffset() {
	if (currentTetromino) {
		for (var i = 0; i < currentTetromino.blocks.length; i++) {
			currentTetromino.blocks[i].x -= BOARD_OFFSET_X;
			currentTetromino.blocks[i].y -= BOARD_OFFSET_Y;
		}
	}
}
// Touch controls
var dragStartX = null;
var dragStartY = null;
var dragTetrominoX = null;
var dragTetrominoY = null;
var lastTouchMove = 0;
var touchDown = false;
var hardDrop = false;
// Convert screen x,y to board col,row
function screenToBoard(x, y) {
	var bx = Math.floor((x - BOARD_OFFSET_X) / CELL_SIZE);
	var by = Math.floor((y - BOARD_OFFSET_Y) / CELL_SIZE);
	return {
		col: bx,
		row: by
	};
}
// Touch/mouse events
game.down = function (x, y, obj) {
	if (isGameOver || !currentTetromino) {
		return;
	}
	dragStartX = x;
	dragStartY = y;
	dragTetrominoX = currentTetromino.x;
	dragTetrominoY = currentTetromino.y;
	touchDown = true;
	hardDrop = false;
	lastTouchMove = Date.now();
};
game.move = function (x, y, obj) {
	if (isGameOver || !currentTetromino || !touchDown) {
		return;
	}
	var dx = x - dragStartX;
	var dy = y - dragStartY;
	var moved = false;
	// Horizontal drag: move left/right
	if (Math.abs(dx) > CELL_SIZE / 2) {
		var dir = dx > 0 ? 1 : -1;
		if (currentTetromino.tryMove(dir, 0, board)) {
			dragStartX = x;
			dragTetrominoX = currentTetromino.x;
			moved = true;
		}
	}
	// Vertical drag: hard drop
	if (dy > CELL_SIZE * 2 && !hardDrop) {
		// Hard drop
		while (currentTetromino.tryMove(0, 1, board)) {}
		hardDrop = true;
		moved = true;
	}
	// Tap: rotate (handled in up)
	if (moved) {
		currentTetromino.updateBlockPositions();
		updateBoardDisplayOffsets();
	}
};
game.up = function (x, y, obj) {
	if (isGameOver || !currentTetromino) {
		return;
	}
	touchDown = false;
	var dt = Date.now() - lastTouchMove;
	var dx = x - dragStartX;
	var dy = y - dragStartY;
	// Tap: rotate
	if (Math.abs(dx) < 30 && Math.abs(dy) < 30 && dt < 400) {
		removeTetrominoOffset();
		currentTetromino.tryRotate(board);
		currentTetromino.updateBlockPositions();
		updateBoardDisplayOffsets();
	}
};
// Auto fall
function scheduleDrop() {
	if (dropTimer) {
		LK.clearTimeout(dropTimer);
	}
	if (isGameOver) {
		return;
	}
	dropTimer = LK.setTimeout(function () {
		if (isGameOver) {
			return;
		}
		removeTetrominoOffset();
		var moved = currentTetromino.tryMove(0, 1, board);
		currentTetromino.updateBlockPositions();
		updateBoardDisplayOffsets();
		if (!moved) {
			// Lock to board
			currentTetromino.lockToBoard(board, blockRefs);
			// Flash effect on lock
			for (var i = 0; i < currentTetromino.blocks.length; i++) {
				LK.effects.flashObject(currentTetromino.blocks[i], 0xffffff, 180);
			}
			// Powerup check
			var landedPowerup = false;
			for (var i = 0; i < currentTetromino.shape.length; i++) {
				var nx = currentTetromino.x + currentTetromino.shape[i][0];
				var ny = currentTetromino.y + currentTetromino.shape[i][1];
				if (board[ny][nx] === 'powerup') {
					activatePowerup(ny);
					landedPowerup = true;
				}
			}
			// Clear lines
			var lines = clearLines();
			updateScore(lines);
			// Do not remove tetromino blocks here; they are now part of the board and will be removed when lines are cleared
			currentTetromino = null;
			// Spawn next
			spawnTetromino();
			updateBoardDisplayOffsets();
		}
		scheduleDrop();
	}, fallInterval);
}
// Game update
game.update = function () {
	// No per-frame logic needed for MVP
};
// Start game
function startGame() {
	// Reset board
	for (var r = 0; r < BOARD_ROWS; r++) {
		for (var c = 0; c < BOARD_COLS; c++) {
			board[r][c] = 0;
			if (blockRefs[r][c]) {
				if (blockRefs[r][c].parent) {
					blockRefs[r][c].parent.removeChild(blockRefs[r][c]);
				}
				blockRefs[r][c] = null;
			}
		}
	}
	score = 0;
	level = 1;
	linesCleared = 0;
	fallInterval = 1000;
	scoreTxt.setText(score);
	levelTxt.setText('Lv.1');
	isGameOver = false;
	nextTetrominoType = TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)];
	updateNextPreview();
	if (currentTetromino) {
		for (var i = 0; i < currentTetromino.blocks.length; i++) {
			if (currentTetromino.blocks[i].parent) {
				currentTetromino.blocks[i].parent.removeChild(currentTetromino.blocks[i]);
			}
		}
		currentTetromino = null;
	}
	spawnTetromino();
	updateBoardDisplayOffsets();
	scheduleDrop();
}
// On game over, restart on new game
LK.on('gameStart', function () {
	startGame();
});
// Initial start
LK.playMusic('tetris_bgm');
startGame(); /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Tetromino class
var Tetromino = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.type = null;
	self.shape = null; // array of [x,y]
	self.rotation = 0;
	self.blocks = [];
	self.x = 0; // board col
	self.y = 0; // board row
	self.isPowerup = false;
	self.powerupType = null;
	// Initialize tetromino
	self.init = function (type, isPowerup) {
		self.type = type;
		self.isPowerup = !!isPowerup;
		self.rotation = 0;
		self.shape = [];
		// Copy shape
		var base = TETROMINO_SHAPES[type][0];
		for (var i = 0; i < base.length; i++) {
			self.shape.push([base[i][0], base[i][1]]);
		}
		self.blocks = [];
		self.removeChildren();
		for (var i = 0; i < self.shape.length; i++) {
			var block;
			if (self.isPowerup && i === 0) {
				block = self.attachAsset('powerup', {
					anchorX: 0.5,
					anchorY: 0.5,
					width: CELL_SIZE,
					height: CELL_SIZE
				});
				self.powerupType = 'clearLine'; // Only one powerup for MVP
			} else {
				block = self.attachAsset(self.type, {
					anchorX: 0.5,
					anchorY: 0.5,
					width: CELL_SIZE,
					height: CELL_SIZE
				});
			}
			self.blocks.push(block);
			// Overlay grid lines for each block
			var gridOverlay = new Container();
			var vlineL = LK.getAsset('O', {
				anchorX: 0.5,
				anchorY: 0,
				width: 4,
				height: CELL_SIZE,
				color: 0x222222 // match board grid color
			});
			vlineL.x = -CELL_SIZE / 2;
			vlineL.y = -CELL_SIZE / 2;
			vlineL.alpha = 0.25;
			gridOverlay.addChild(vlineL);
			var vlineR = LK.getAsset('O', {
				anchorX: 0.5,
				anchorY: 0,
				width: 4,
				height: CELL_SIZE,
				color: 0x222222 // match board grid color
			});
			vlineR.x = CELL_SIZE / 2;
			vlineR.y = -CELL_SIZE / 2;
			vlineR.alpha = 0.25;
			gridOverlay.addChild(vlineR);
			var hlineT = LK.getAsset('O', {
				anchorX: 0,
				anchorY: 0.5,
				width: CELL_SIZE,
				height: 4,
				color: 0x222222 // match board grid color
			});
			hlineT.x = -CELL_SIZE / 2;
			hlineT.y = -CELL_SIZE / 2;
			hlineT.alpha = 0.25;
			gridOverlay.addChild(hlineT);
			var hlineB = LK.getAsset('O', {
				anchorX: 0,
				anchorY: 0.5,
				width: CELL_SIZE,
				height: 4,
				color: 0x222222 // match board grid color
			});
			hlineB.x = -CELL_SIZE / 2;
			hlineB.y = CELL_SIZE / 2;
			hlineB.alpha = 0.25;
			gridOverlay.addChild(hlineB);
			block.addChild(gridOverlay);
		}
		self.x = Math.floor(BOARD_COLS / 2) - 2;
		self.y = 0;
		self.updateBlockPositions();
	};
	// Update block positions
	self.updateBlockPositions = function () {
		for (var i = 0; i < self.shape.length; i++) {
			var pos = self.shape[i];
			self.blocks[i].x = (self.x + pos[0]) * CELL_SIZE + CELL_SIZE / 2;
			self.blocks[i].y = (self.y + pos[1]) * CELL_SIZE + CELL_SIZE / 2;
		}
	};
	// Try move
	self.tryMove = function (dx, dy, board) {
		if (self.canMove(dx, dy, self.shape, board)) {
			self.x += dx;
			self.y += dy;
			self.updateBlockPositions();
			return true;
		}
		return false;
	};
	// Try rotate
	self.tryRotate = function (board) {
		var rotated = rotateShape(self.shape);
		if (self.canMove(0, 0, rotated, board)) {
			self.shape = rotated;
			self.updateBlockPositions();
			return true;
		}
		return false;
	};
	// Can move/rotate
	self.canMove = function (dx, dy, shape, board) {
		for (var i = 0; i < shape.length; i++) {
			var nx = self.x + shape[i][0] + dx;
			var ny = self.y + shape[i][1] + dy;
			if (nx < 0 || nx >= BOARD_COLS || ny < 0 || ny >= BOARD_ROWS) {
				return false;
			}
			if (board[ny][nx]) {
				return false;
			}
		}
		return true;
	};
	// Lock tetromino into board
	self.lockToBoard = function (board, blockRefs) {
		for (var i = 0; i < self.shape.length; i++) {
			var nx = self.x + self.shape[i][0];
			var ny = self.y + self.shape[i][1];
			board[ny][nx] = self.isPowerup && i === 0 ? 'powerup' : self.type;
			blockRefs[ny][nx] = self.blocks[i];
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	// No title, no description
	// Always backgroundColor is black
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// Board state: 2D array [row][col], 0 = empty, else type string
// Tetromino shapes (classic + new)
// Colors for each tetromino type
// New shapes
// Power-up block
// Power-up effect
// Tetromino definitions (relative coordinates for each block in the tetromino)
var TETROMINO_SHAPES = {
	O: [[[1, 0], [2, 0], [1, 1], [2, 1]]],
	T: [[[1, 0], [0, 1], [1, 1], [2, 1]]],
	S: [[[1, 0], [2, 0], [0, 1], [1, 1]]],
	Z: [[[0, 0], [1, 0], [1, 1], [2, 1]]],
	J: [[[0, 0], [0, 1], [1, 1], [2, 1]]],
	L: [[[2, 0], [0, 1], [1, 1], [2, 1]]],
	U: [[[0, 0], [2, 0], [0, 1], [1, 1], [2, 1]]],
	P: [[[0, 0], [1, 0], [0, 1], [1, 1], [0, 2]]],
	Dot: [[[1, 1]]]
};
var TETROMINO_TYPES = ['O', 'T', 'S', 'Z', 'J', 'L', 'U', 'P'];
var POWERUP_CHANCE = 0.08; // 8% chance for a powerup block
// Board settings
var BOARD_COLS = 8; // Reduced width for fewer columns, block size unchanged
var BOARD_ROWS = 20;
var CELL_SIZE = 90; // px, fits well on 2048x2732
var BOARD_OFFSET_X = Math.floor((2048 - BOARD_COLS * CELL_SIZE) / 2) - 80; // Shift left to make space for preview
var BOARD_OFFSET_Y = 300;
// Helper: rotate a shape (array of [x,y]) 90deg clockwise
function rotateShape(shape) {
	var maxX = 0,
		maxY = 0;
	for (var i = 0; i < shape.length; i++) {
		if (shape[i][0] > maxX) {
			maxX = shape[i][0];
		}
		if (shape[i][1] > maxY) {
			maxY = shape[i][1];
		}
	}
	var rotated = [];
	for (var i = 0; i < shape.length; i++) {
		var x = shape[i][0],
			y = shape[i][1];
		rotated.push([maxY - y, x]);
	}
	return rotated;
}
var board = [];
var blockRefs = []; // 2D array of block display objects
for (var r = 0; r < BOARD_ROWS; r++) {
	board[r] = [];
	blockRefs[r] = [];
	for (var c = 0; c < BOARD_COLS; c++) {
		board[r][c] = 0;
		blockRefs[r][c] = null;
	}
}
// Score and level
var score = 0;
var level = 1;
var linesCleared = 0;
var fallInterval = 1000; // ms, decreases with level
// GUI
var scoreTxt = new Text2('0', {
	size: 100,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Lv.1', {
	size: 60,
	fill: "#fff"
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 110;
// Next tetromino preview
var nextTetrominoType = null;
var nextTetrominoPreview = new Container();
game.addChild(nextTetrominoPreview);
// Current falling tetromino
var currentTetromino = null;
// Game state
var isGameOver = false;
var isDropping = false;
var dropTimer = null;
var moveTimer = null;
var moveDir = 0; // -1 left, 1 right, 0 none
// Helper: spawn new tetromino
function spawnTetromino() {
	var type = nextTetrominoType;
	if (!type) {
		type = TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)];
	}
	var isPowerup = Math.random() < POWERUP_CHANCE;
	var tetro = new Tetromino();
	tetro.init(type, isPowerup);
	game.addChild(tetro);
	currentTetromino = tetro;
	// Next
	nextTetrominoType = TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)];
	updateNextPreview();
	// Flash effect on next preview
	if (nextTetrominoPreview.children && nextTetrominoPreview.children.length > 0) {
		for (var i = 0; i < nextTetrominoPreview.children.length; i++) {
			LK.effects.flashObject(nextTetrominoPreview.children[i], 0xffffff, 180);
		}
	}
	// If cannot place, game over
	if (!tetro.canMove(0, 0, tetro.shape, board)) {
		isGameOver = true;
		LK.effects.flashScreen(0xff0000, 1000);
		LK.showGameOver();
	}
}
// Helper: update next preview
function updateNextPreview() {
	nextTetrominoPreview.removeChildren();
	var type = nextTetrominoType;
	if (!type) {
		return;
	}
	var shape = TETROMINO_SHAPES[type][0];
	// Find min/max for centering
	var minX = 99,
		maxX = -99,
		minY = 99,
		maxY = -99;
	for (var i = 0; i < shape.length; i++) {
		if (shape[i][0] < minX) {
			minX = shape[i][0];
		}
		if (shape[i][0] > maxX) {
			maxX = shape[i][0];
		}
		if (shape[i][1] < minY) {
			minY = shape[i][1];
		}
		if (shape[i][1] > maxY) {
			maxY = shape[i][1];
		}
	}
	var previewWidth = (maxX - minX + 1) * CELL_SIZE;
	var previewHeight = (maxY - minY + 1) * CELL_SIZE;
	for (var i = 0; i < shape.length; i++) {
		var block = LK.getAsset(type, {
			anchorX: 0.5,
			anchorY: 0.5,
			width: CELL_SIZE,
			height: CELL_SIZE
		});
		block.x = (shape[i][0] - minX) * CELL_SIZE + CELL_SIZE / 2;
		block.y = (shape[i][1] - minY) * CELL_SIZE + CELL_SIZE / 2;
		nextTetrominoPreview.addChild(block);
	}
	// Place preview to the right of the board, fully visible and inside the screen
	nextTetrominoPreview.x = BOARD_OFFSET_X + BOARD_COLS * CELL_SIZE + 120;
	nextTetrominoPreview.y = BOARD_OFFSET_Y + 200;
}
// Helper: clear full lines
function clearLines() {
	var lines = [];
	for (var r = 0; r < BOARD_ROWS; r++) {
		var full = true;
		for (var c = 0; c < BOARD_COLS; c++) {
			if (!board[r][c]) {
				full = false;
				break;
			}
		}
		if (full) {
			lines.push(r);
		}
	}
	if (lines.length === 0) {
		return 0;
	}
	// Animate and remove lines
	for (var i = 0; i < lines.length; i++) {
		var row = lines[i];
		for (var c = 0; c < BOARD_COLS; c++) {
			if (blockRefs[row][c]) {
				tween(blockRefs[row][c], {
					alpha: 0
				}, {
					duration: 200,
					onFinish: function (obj) {
						if (obj.parent) {
							obj.parent.removeChild(obj);
						}
					}.bind(null, blockRefs[row][c])
				});
			}
		}
	}
	// Remove from board
	for (var i = 0; i < lines.length; i++) {
		var row = lines[i];
		for (var c = 0; c < BOARD_COLS; c++) {
			board[row][c] = 0;
			blockRefs[row][c] = null;
		}
	}
	// Drop above lines
	for (var i = lines.length - 1; i >= 0; i--) {
		var row = lines[i];
		for (var r = row - 1; r >= 0; r--) {
			for (var c = 0; c < BOARD_COLS; c++) {
				board[r + 1][c] = board[r][c];
				blockRefs[r + 1][c] = blockRefs[r][c];
				if (blockRefs[r + 1][c]) {
					tween(blockRefs[r + 1][c], {
						y: blockRefs[r + 1][c].y + CELL_SIZE
					}, {
						duration: 100
					});
				}
			}
		}
		// Clear top row
		for (var c = 0; c < BOARD_COLS; c++) {
			board[0][c] = 0;
			blockRefs[0][c] = null;
		}
	}
	return lines.length;
}
// Helper: activate powerup
function activatePowerup(row) {
	// For MVP: clear the row where powerup landed
	for (var c = 0; c < BOARD_COLS; c++) {
		if (blockRefs[row][c]) {
			tween(blockRefs[row][c], {
				alpha: 0
			}, {
				duration: 200,
				onFinish: function (obj) {
					if (obj.parent) {
						obj.parent.removeChild(obj);
					}
				}.bind(null, blockRefs[row][c])
			});
			board[row][c] = 0;
			blockRefs[row][c] = null;
		}
	}
	// Drop above lines
	for (var r = row - 1; r >= 0; r--) {
		for (var c = 0; c < BOARD_COLS; c++) {
			board[r + 1][c] = board[r][c];
			blockRefs[r + 1][c] = blockRefs[r][c];
			if (blockRefs[r + 1][c]) {
				tween(blockRefs[r + 1][c], {
					y: blockRefs[r + 1][c].y + CELL_SIZE
				}, {
					duration: 100
				});
			}
		}
	}
	// Clear top row
	for (var c = 0; c < BOARD_COLS; c++) {
		board[0][c] = 0;
		blockRefs[0][c] = null;
	}
}
// Helper: update score/level
function updateScore(lines) {
	var points = [0, 100, 300, 500, 800];
	score += points[lines] || 0;
	linesCleared += lines;
	scoreTxt.setText(score);
	var newLevel = 1 + Math.floor(linesCleared / 10);
	if (newLevel !== level) {
		level = newLevel;
		levelTxt.setText('Lv.' + level);
		fallInterval = Math.max(150, 1000 - (level - 1) * 100);
	}
}
// Helper: draw board grid (for visual reference)
var gridLines = new Container();
game.addChild(gridLines);
// Draw faint cell backgrounds for subtle grid
for (var r = 0; r < BOARD_ROWS; r++) {
	for (var c = 0; c < BOARD_COLS; c++) {
		var cell = LK.getAsset('O', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: CELL_SIZE - 4,
			height: CELL_SIZE - 4,
			color: 0x222222
		});
		cell.x = c * CELL_SIZE + CELL_SIZE / 2;
		cell.y = r * CELL_SIZE + CELL_SIZE / 2;
		cell.alpha = 0.15;
		gridLines.addChild(cell);
	}
}
// Draw vertical grid lines
for (var c = 0; c <= BOARD_COLS; c++) {
	var vline = LK.getAsset('O', {
		anchorX: 0.5,
		anchorY: 0,
		width: 4,
		height: CELL_SIZE * BOARD_ROWS,
		color: 0x222222 // darker gray for better contrast
	});
	vline.x = c * CELL_SIZE;
	vline.y = 0;
	vline.alpha = 0.35;
	gridLines.addChild(vline);
}
// Draw horizontal grid lines
for (var r = 0; r <= BOARD_ROWS; r++) {
	var hline = LK.getAsset('O', {
		anchorX: 0,
		anchorY: 0.5,
		width: CELL_SIZE * BOARD_COLS,
		height: 4,
		color: 0x222222 // darker gray for better contrast
	});
	hline.x = 0;
	hline.y = r * CELL_SIZE;
	hline.alpha = 0.35;
	gridLines.addChild(hline);
}
gridLines.x = BOARD_OFFSET_X;
gridLines.y = BOARD_OFFSET_Y;
// Move all blocks/containers to board offset
function updateBoardDisplayOffsets() {
	gridLines.x = BOARD_OFFSET_X;
	gridLines.y = BOARD_OFFSET_Y;
	for (var r = 0; r < BOARD_ROWS; r++) {
		for (var c = 0; c < BOARD_COLS; c++) {
			if (blockRefs[r][c]) {
				blockRefs[r][c].x = c * CELL_SIZE + CELL_SIZE / 2 + BOARD_OFFSET_X;
				blockRefs[r][c].y = r * CELL_SIZE + CELL_SIZE / 2 + BOARD_OFFSET_Y;
			}
		}
	}
	if (currentTetromino) {
		for (var i = 0; i < currentTetromino.blocks.length; i++) {
			currentTetromino.blocks[i].x += BOARD_OFFSET_X;
			currentTetromino.blocks[i].y += BOARD_OFFSET_Y;
		}
	}
}
// Remove board offset from tetromino before logic
function removeTetrominoOffset() {
	if (currentTetromino) {
		for (var i = 0; i < currentTetromino.blocks.length; i++) {
			currentTetromino.blocks[i].x -= BOARD_OFFSET_X;
			currentTetromino.blocks[i].y -= BOARD_OFFSET_Y;
		}
	}
}
// Touch controls
var dragStartX = null;
var dragStartY = null;
var dragTetrominoX = null;
var dragTetrominoY = null;
var lastTouchMove = 0;
var touchDown = false;
var hardDrop = false;
// Convert screen x,y to board col,row
function screenToBoard(x, y) {
	var bx = Math.floor((x - BOARD_OFFSET_X) / CELL_SIZE);
	var by = Math.floor((y - BOARD_OFFSET_Y) / CELL_SIZE);
	return {
		col: bx,
		row: by
	};
}
// Touch/mouse events
game.down = function (x, y, obj) {
	if (isGameOver || !currentTetromino) {
		return;
	}
	dragStartX = x;
	dragStartY = y;
	dragTetrominoX = currentTetromino.x;
	dragTetrominoY = currentTetromino.y;
	touchDown = true;
	hardDrop = false;
	lastTouchMove = Date.now();
};
game.move = function (x, y, obj) {
	if (isGameOver || !currentTetromino || !touchDown) {
		return;
	}
	var dx = x - dragStartX;
	var dy = y - dragStartY;
	var moved = false;
	// Horizontal drag: move left/right
	if (Math.abs(dx) > CELL_SIZE / 2) {
		var dir = dx > 0 ? 1 : -1;
		if (currentTetromino.tryMove(dir, 0, board)) {
			dragStartX = x;
			dragTetrominoX = currentTetromino.x;
			moved = true;
		}
	}
	// Vertical drag: hard drop
	if (dy > CELL_SIZE * 2 && !hardDrop) {
		// Hard drop
		while (currentTetromino.tryMove(0, 1, board)) {}
		hardDrop = true;
		moved = true;
	}
	// Tap: rotate (handled in up)
	if (moved) {
		currentTetromino.updateBlockPositions();
		updateBoardDisplayOffsets();
	}
};
game.up = function (x, y, obj) {
	if (isGameOver || !currentTetromino) {
		return;
	}
	touchDown = false;
	var dt = Date.now() - lastTouchMove;
	var dx = x - dragStartX;
	var dy = y - dragStartY;
	// Tap: rotate
	if (Math.abs(dx) < 30 && Math.abs(dy) < 30 && dt < 400) {
		removeTetrominoOffset();
		currentTetromino.tryRotate(board);
		currentTetromino.updateBlockPositions();
		updateBoardDisplayOffsets();
	}
};
// Auto fall
function scheduleDrop() {
	if (dropTimer) {
		LK.clearTimeout(dropTimer);
	}
	if (isGameOver) {
		return;
	}
	dropTimer = LK.setTimeout(function () {
		if (isGameOver) {
			return;
		}
		removeTetrominoOffset();
		var moved = currentTetromino.tryMove(0, 1, board);
		currentTetromino.updateBlockPositions();
		updateBoardDisplayOffsets();
		if (!moved) {
			// Lock to board
			currentTetromino.lockToBoard(board, blockRefs);
			// Flash effect on lock
			for (var i = 0; i < currentTetromino.blocks.length; i++) {
				LK.effects.flashObject(currentTetromino.blocks[i], 0xffffff, 180);
			}
			// Powerup check
			var landedPowerup = false;
			for (var i = 0; i < currentTetromino.shape.length; i++) {
				var nx = currentTetromino.x + currentTetromino.shape[i][0];
				var ny = currentTetromino.y + currentTetromino.shape[i][1];
				if (board[ny][nx] === 'powerup') {
					activatePowerup(ny);
					landedPowerup = true;
				}
			}
			// Clear lines
			var lines = clearLines();
			updateScore(lines);
			// Do not remove tetromino blocks here; they are now part of the board and will be removed when lines are cleared
			currentTetromino = null;
			// Spawn next
			spawnTetromino();
			updateBoardDisplayOffsets();
		}
		scheduleDrop();
	}, fallInterval);
}
// Game update
game.update = function () {
	// No per-frame logic needed for MVP
};
// Start game
function startGame() {
	// Reset board
	for (var r = 0; r < BOARD_ROWS; r++) {
		for (var c = 0; c < BOARD_COLS; c++) {
			board[r][c] = 0;
			if (blockRefs[r][c]) {
				if (blockRefs[r][c].parent) {
					blockRefs[r][c].parent.removeChild(blockRefs[r][c]);
				}
				blockRefs[r][c] = null;
			}
		}
	}
	score = 0;
	level = 1;
	linesCleared = 0;
	fallInterval = 1000;
	scoreTxt.setText(score);
	levelTxt.setText('Lv.1');
	isGameOver = false;
	nextTetrominoType = TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)];
	updateNextPreview();
	if (currentTetromino) {
		for (var i = 0; i < currentTetromino.blocks.length; i++) {
			if (currentTetromino.blocks[i].parent) {
				currentTetromino.blocks[i].parent.removeChild(currentTetromino.blocks[i]);
			}
		}
		currentTetromino = null;
	}
	spawnTetromino();
	updateBoardDisplayOffsets();
	scheduleDrop();
}
// On game over, restart on new game
LK.on('gameStart', function () {
	startGame();
});
// Initial start
LK.playMusic('tetris_bgm');
startGame();