class Game {
    field int hbricks, vbricks;
    field int gameOver;

    field Rect paddle;
    field Ball ball;
    field Array bricks;

    constructor Game new() {
        var int i, j;
        var Rect curBrick;

        let gameOver = 0;
        let hbricks = 8;
        let vbricks = 3;
        
        let paddle = Rect.new(250,220,50,10);
        let ball = Ball.new(250, 200, 6, 1, 1);
        let bricks = Array.new(hbricks * vbricks);

        while (i < hbricks) {
            while (j < vbricks) {
                let bricks[i * 3 + j] = Rect.new(i * 60 + 20, j * 30 + 20, 50, 15);
                let curBrick = bricks[i * 3 + j];
                do curBrick.draw();
                let j = j + 1;
            }
            let j = 0;
            let i = i + 1;
        }

        return this;
    }

    method void loop() {
        var int keyPressed;

        while(true) {
            // Approximately 60 updates per second
            do Sys.wait(16);

            if (gameOver = 0) {
                do movePaddle();
                do ball.update();
                do chackBallPaddleCol();
                do checkBallBricksCol();

                do checkGameOver();
            }  else {
                let keyPressed = Keyboard.keyPressed();
                if (keyPressed = 82) {
                    do resetGame();
                }
            }

        }
        return;
    }

    method void resetGame() {
        var int i, end;
        var Rect brick;

        do Screen.clearScreen();

        do paddle.setPos(250, 220);
        do ball.setPos(250, 200);
        do ball.setSpeed(1, 1);
        
        let end = hbricks * vbricks;
        while (i < end) {
            let brick = bricks[i];

            do brick.setHidden(0);
            do brick.draw();

            let i = i + 1;
        }

        let gameOver = 0;

        return;
    }

    method void checkGameOver() {
        var int bBottom;

        let bBottom = ball.getBottom();

        if (bBottom > 240) {
            let gameOver = 1;

            do Output.moveCursor(0,0);
            do Output.println();
            do Output.printString("game over");
            do Output.println();
            do Output.printString("press r to restart");
        }

        return;
    }

    method void movePaddle() {
        var int keyPressed, paddleLeft, paddleRight;

        let keyPressed = Keyboard.keyPressed();
        let paddleLeft = paddle.getLeft();
        let paddleRight = paddle.getRight();
        // Move left
        if((keyPressed = 130) & (paddleLeft > 0)) {
            do paddle.moveLeft(2);
        }
        // Move right
        if((keyPressed = 132) & (paddleRight < 510)) {
            do paddle.moveRight(2);
        }
        do paddle.draw();

        return;
    }

    method void chackBallPaddleCol() {
        var int paddleLeft, paddleRight, paddleTop, paddleBottom, paddleMiddle; 
        var int bLeft, bRight, bTop, bBottom, bMiddle, bVspeed; 

        let paddleLeft = paddle.getLeft();
        let paddleRight = paddle.getRight();
        let paddleTop = paddle.getTop();
        let paddleBottom = paddle.getBottom();
        let paddleMiddle = (paddleRight + paddleLeft) / 2;

        let bLeft = ball.getLeft();
        let bRight = ball.getRight();
        let bTop = ball.getTop();
        let bBottom = ball.getBottom();
        let bMiddle = (bRight + bLeft) / 2;
        let bVspeed = ball.getVspeed();

        if((bLeft > paddleLeft) & (bRight < paddleRight)) {
            if((bBottom > paddleTop) & (bVspeed > 0)) {
                if (bMiddle > paddleMiddle) {
                    do ball.setSpeed(1, -1);
                } else {
                    do ball.setSpeed(-1, -1);
                }
            }
        }

        return;
    }

    method void checkBallBricksCol() {
        var int brLeft, brRight, brTop, brBottom, brHidden; 
        var int bLeft, bRight, bTop, bBottom, bVspeed;
        var int overlapLeft, overlapRight, overlapTop, overlapBottom, fromLeft, fromTop;
        var int minOverlapX, minOverlapY;
        var int i, end;
        var Rect brick;

        let bLeft = ball.getLeft();
        let bRight = ball.getRight();
        let bTop = ball.getTop();
        let bBottom = ball.getBottom();
        let bVspeed = ball.getVspeed();
        
        let end = hbricks * vbricks;
        while (i < end) {
            let brick = bricks[i];

            let brLeft = brick.getLeft();
            let brRight = brick.getRight();
            let brTop = brick.getTop();
            let brBottom = brick.getBottom();
            let brHidden = brick.getHidden();

            if(brHidden < 1) {
                do brick.draw();

                // Check if there's intersection at all first
                if((bLeft > brLeft) & (bRight < brRight)) {
                    if((bBottom > brTop) & (bTop < brBottom)) {
                        do brick.setHidden(1);
                        do brick.draw();

                        // Now calculate the collision side
                        // Algorithm basically copied from this video
                        // https://youtu.be/_4K3tsKa1Uc?t=1820

                        let overlapLeft = bRight - brLeft;
                        let overlapRight = brRight - bLeft;
                        let overlapTop = bBottom - brTop;
                        let overlapBottom = brBottom - bTop;

                        if(Util.abs(overlapLeft) < Util.abs(overlapRight)) {
                            let fromLeft = 1;
                            let minOverlapX = overlapLeft;
                        } else {
                            let minOverlapX = overlapRight;
                        }

                        if(Util.abs(overlapTop) < Util.abs(overlapBottom)) {
                            let fromTop = 1;
                            let minOverlapY = overlapTop;
                        } else {
                            let minOverlapY = overlapBottom;
                        }

                        if(Util.abs(minOverlapX) < Util.abs(minOverlapY)) {
                            do ball.invertHspeed();
                        } else {
                            do ball.invertVspeed();
                        }

                    }
                }
            }

            let i = i + 1;
        }

        return;
    }

}