class GameSession {
	gameStatus {
		Idle {
			entry / {
				initializeBlocks(); // Add blocks that admin has created
				initializeBall(); // Add a SpecificBall
				initializePaddle(); // Add a SpecificPaddle & position it
			}
			startGame() [isGameAdmin() && ofTestMode() && hasEnoughBlocks()] / {
			} -> Play;
			startGame() [!ofTestMode() && hasEnoughBlocks()] / {
			} -> Play;
			startGame() [isGameAdmin() && ofTestMode() && !hasEnoughBlocks()] / {
				addRandomBlocks(); // Must reach required number of blocks
			} -> Play;
			startGame() [!ofTestMode() && !hasEnoughBlocks()] / {
				addRandomBlocks();
			} -> Play;
		}
		Play {
			entry / {
				move(SpecificBall aSpecificBall);
			}
			move(SpecificBall aSpecificBall) [isBlockHit() && !isLastBlock()] / { // move() updates SpecificBall position
				doBlockHit(SpecificBall aSpecificBall);
				deleteBlock(SpecificBlock aBlock); 
				increaseScore();
			} -> Play;
			move(SpecificBall aSpecificBall) [isBlockHit() && isLastBlock() && !isLastLevel()] / {
				doBlockHit(SpecificBall aSpecificBall);
				deleteBlock(SpecificBlock aBlock);
				resetPaddlePosition(SpecificPaddle aPaddle);
				resetBallPosition(SpecificBall aSpecificBall);
				increaseScore();
				currentLevelNr++;
			} -> Paused;
			move(SpecificBall aSpecificBall) [isBlockHit() && isLastBlock() && isLastLevel()] / {
				doBlockHit(SpecificBall aSpecificBall);
				deleteBlock(SpecificBlock aBlock);
				increaseScore();
			} -> Complete;
			move(SpecificBall aSpecificBall) [isWallHit()] / {
				doWallHit(SpecificBall aSpecificBall);
			} -> Play;
			move(SpecificBall aSpecificBall) [isPaddleHit()] / {
				doPaddleHit(SpecificBall aSpecificBall);
			} -> Play;
			move(SpecificBall aSpecificBall) [isOutOfBounds() && !isLastLife()] / {
				doOutOfBounds(SpecificBall aSpecificBall);
				resetPaddlePosition(SpecificPaddle aPaddle);
				resetBallPosition(SpecificBall aBall);
				currentLife--;
			} -> Paused;
			move(SpecificBall aSpecificBall) [isOutOfBounds() && isLastLife()] / {
				doOutOfBounds(SpecificBall aSpecificBall);
				currentLife--;
			} -> Complete;
			pauseGame() [!isOutOfBounds()] / {
				stopBall(SpecificBall aSpecificBall); // Stop SpecificBall in its current location
				stopPaddle(SpecificPaddle aSpecificPaddle); // Stop SpecificPaddle in its current location
			} -> Paused;
			exit / {
				stopBall(SpecificBall aSpecificBall);
			}
		}
		Paused { // Make sure to call save to persistence when exiting game loop
			resumeGame() [hasNoBlocks()] / { // Represents completing a level
				initializeBlocks(); // initializeBlocks() checks if there are blocks left (if no, start from scratch)
				addRandomBlocks();
				initializeBall(); 
				initializePaddle();
			} -> Play;
			resumeGame() [!hasNoBlocks()] / { // Level not yet complete
				initializeBlocks(); // Add blocks that admin has created from save, those not deleted
				initializeBall(); // Add a SpecificBall from save
				initializePaddle(); // Add a SpecificPaddle & position it from save
			} -> Play;
		}
		Complete {
			updateHallOfFame() [isLastLevel()] {
				addHallOfFameEntry(HallOfFameEntry aHallOfFameEntry);
			} -> Final;
			gameOver() [!isLastLevel()] {
				displayGameOver();
			} -> Final;
		}
	}
	
        // Action methods
        private void initializeBlocks();
        private void initializeBall();
        private void initializePaddle();
        private void addRandomBlocks();
        private void doBlockHit(SpecificBall aSpecificBall);
        private void deleteBlock(SpecificBlock aBlock);
        private void increaseScore();
        private void resetPaddlePosition(SpecificPaddle aPaddle);
        private void resetBallPosition(SpecificBall aSpecificBall);
        private void doWallHit(SpecificBall aSpecificBall);
        private void doPaddleHit(SpecificBall aSpecificBall);
        private void doOutOfBounds(SpecificBall aSpecificBall);
        private void stopBall(SpecificBall aSpecificBall);
        private void stopPaddle(SpecificPaddle aSpecificPaddle);
        private void addHallOfFameEntry();
        private void displayGameOver();
        
        
	// Helper methods
	private boolean isGameAdmin(); // Checks if the person testing the game created it
	private boolean ofTestMode(); // Checks if GameSession is of test mode
	private boolean hasEnoughBlocks(); // Checks that # blocks is at maximum
	private boolean hasNoBlocks();
	private boolean isBlockHit();
	private boolean isWallHit();
	private boolean isPaddleHit();
	private boolean isOutOfBounds();
	private boolean isLastBlock();
	private boolean isLastLife();
	private boolean hasLifeLeft();
	private boolean isLastLevel();
}
