import allegro4;

//game states
enum STATE {
    GAME_OVER,
    INIT,
    PLAY
}

//point in game
class Point {
public:
    //members
    var x = 0;
    var y = 0;
    
    //constructor
    func constructor (x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
    
    //add point
    func operator += (p : *const Point) {
        x += p.x;
        y += p.y;
    }
}

//a game entity
class Entity {
public:
    //position of entity
    var position = Point();
}

//a paddle
class Paddle : Entity {
public:
    //speed
}

//paddle size
const PADDLE_WIDTH = 8;
const PADDLE_HEIGHT = 64;

//player 
class Player {
public:
    //paddle
    var paddle = Paddle();
    
    //score
    var score = 0;
    
    //controls
    var keyUp = 0;
    var keyDown = 0;
    
    //constructor
    func constructor(keyUp : int, keyDown : int) {
        this.keyUp = keyUp;
        this.keyDown = keyDown;
    }
    
    //init
    func init(x : int) {
        paddle.position.x = x;
        paddle.position.y = SCREEN_W - (PADDLE_HEIGHT / 2);
    }
    
    //collides with
    pure func collidesWith(ball : *Ball) {
        return 
            ball.position.x >= position.x and 
            ball.position.x < position.x + PADDLE_WIDTH and
            ball.position.y >= position.y and 
            ball.position.y < position.y + PADDLE_HEIGHT;
    }
    
    //update function
    func update() {
        //handle key up
        if (key(keyUp)) {
            if (paddle.position.y > 0) {
                paddle.position.y--;
            }
        }
        
        //handle key down
        if (key(keyDown)) {
            if (paddle.position.y < SCREEN_H - PADDLE_HEIGHT) {
                paddle.position.y++;
            }
        }
    }
    
    //draw
    func draw(buffer : *BITMAP) {
        rectfill(buffer, position.x, position.y, position.x + PADDLE_WIDTH - 1, position.y + PADDLE_HEIGHT - 1, makecol(255, 255, 255));
    }
};

const BALL_RADIUS = 16;

//the ball
class Ball : Entity {
public:     
    //speed
    var speed = Point(1, 1);
    
    //init
    func init() {
        position = Point(SCREEN_W / 2, SCREEN_H / 2);
        speed = Point(1, 1);        
    }
    
    //update ball
    func update() {
        //update position
        position += speed;
        
        //invert vertical speed if screen limits are reached
        if (position.y < BALL_RADIUS / 2 or position.y > SCREEN_H - BALL_RADIUS / 2) {
            speed.y = -speed_y;
        }            
    }
    
    //draw
    func draw(buffer : *BITMAP) {
        circlefill(buffer, position.x, position.y, BALL_RADIUS, makecol(255, 255, 255));
    }
}

//game state
var state = STATE.GAME_OVER;

//player 1
var player1 = Player(KEY_Q, KEY_A);

//player 2 
var player2 = Player(KEY_UP, KEY_DOWN);

//ball
var ball = Ball();

//speed counter
var speed_counter = 0;

//speed proc
func speed_proc() {
    speed_counter++;
}

//init all
func init() {
    player1.init(0);
    player2.init(SCREEN_W - PADDLE_WIDTH);
    ball.init();
}

//draw all
func draw(buffer : *BITMAP) {
    clear(buffer);
    player1.draw(buffer);
    player2.draw(buffer);
    ball.draw(buffer);
    blit(buffer, screen, 0, 0, 0, 0, SCREE_W, SCREEN_H);
}

//main
func main() {
    //init allegro
    allegro_init();
    install_keyboard();
    install_timer();
    
    //set video mode
    set_color_depth(32);
    if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) < 0) {
        return -1;
    }
    
    //create a buffer
    var buffer = create_bitmap(SCREEN_W, SCREEN_H);
    
    //set up a timer for speed
    install_int(speed_proc, 60);
    
    //init all
    init();
    
    //game loop
    while (1) {
        //handle state
        switch (state) {
            //start from game over state
            case STATE.GAME_OVER:
                clear(screen);
                textout_center(screen, font, "GAME OVER", SCREEN_W / 2, SCREEN_H / 2 - 16, makecol(255, 255, 255));
                textout_center(screen, font, "PRESS ANY KEY TO START", SCREEN_W / 2, SCREEN_H / 2 + 16, makecol(255, 255, 255));
                textout_center(screen, font, "PRESS ESC TO EXIT", SCREEN_W / 2, SCREEN_H / 2 + 32, makecol(255, 255, 255));
                var k = readkey();
                if (k == KEY_ESC) {
                    goto END_GAME;
                }
                state = STATE_PLAY;

            //init state; wait 2 seconds
            case STATE.INIT:
                if (speed_counter > 60 * 2) {
                    speed_counter = 0;
                    state = STATE.PLAY;
                }

            case STATE.PLAY:
                //game logic
                while (speed_counter > 0) {
                    //if esc is pressed, end game
                    if (key(KEY_ESC)) {
                        goto END_GAME;
                    }

                    //update players
                    player1.update();
                    player2.update();

                    //update the ball
                    ball.update();

                    //check collision with players
                    if (player1.collidesWidth(&ball) or player2.collidesWidth(&ball)) {
                        ball.speed.x = -ball.speed.x;
                    }

                    //otherwise find who scored
                    else if (ball.position.x < player1.position.x + PADDLE_WIDTH) {
                        player2.score++;
                        init();
                        if (player2.score < 21) {                        
                            state = STATE.INIT;
                        }
                        else {
                            state = STATE.GAME_OVER;
                        }
                    }
                    else if (ball.position.x > player2.position.x) {
                        player1.score++;
                        init();
                        if (player1.score < 21) {                        
                            state = STATE.INIT;
                        }
                        else {
                            state = STATE.GAME_OVER;
                        }
                    }

                    speed_counter--;
                } //while (speed_counter > 0)
        
                //draw
                draw(buffer);
                
        } //switch(state)               
        
    } // while(1)
    
    //end game
    END_GAME:
    
    //destroy buffer
    destroy_bitmap(buffer);
    
    return 0;
}
