
#include <stdio.h>
#include <altera_avalon_pio_regs.h>
#include <system.h>
#include <math.h>
#include "Display.h"
#include "structs.h"

#define ACCELERATION (0.001f)		//increase of the ball speed over time
#define TICK         (0.001f)   //movement per tick

/*
 * button input is 8 bit word.
 * variables define which of the 8 buttons is assigned to the given functionality
 * Exp.:
 * 	#define LEFT_UP_BUTTON 1 -> the first button will move the left pannel up
*/
#define LEFT_UP_BUTTON    (1)
#define LEFT_DOWN_BUTTON  (0)
#define RIGHT_UP_BUTTON   (3)
#define RIGHT_DOWN_BUTTON (2)

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

struct Ball ball;
struct Paddle left_paddle;
struct Paddle right_paddle;

/*
 * restes everything to default.
 * both paddles on highes position, with a length of 3.
 * ball in the middle, start moving up-right.
*/
void reset_pos() {
  ball.x = COLS/2 - 1;
  ball.y = ROWS/2 - 1;
  ball.vel_x = -1;
  ball.vel_y = -1;
  left_paddle.y = 0;
  left_paddle.length = 3;
  right_paddle.y = 0;
  right_paddle.length = 3;
}

/*
 * definition of the game PONG itself.
 * first read input registers and calculate new paddelpositions depending on the pressed buttons.
 * 
 * second calculate the new position of the ball depending on its current position and velocity.
 * If Ball hits left or right border calculate if the ball hits a paddle or strike a goal.
 * Goal 	  -> restart the game
 * Paddle 	-> bounce ball back
 * 
 * Third reset the screen to 0 and draw the new positions on it
*/
void game() {
  int i,k;
  unsigned buttons;
  k=0;

  reset_pos();

  while (1){

    buttons = ~IORD_ALTERA_AVALON_PIO_DATA(PIO_BUTTON_BASE);

    // calculate new paddle positions from button input
    if (buttons & (1<<LEFT_UP_BUTTON)) {
      left_paddle.y = MAX(left_paddle.y - (10.0f * TICK), 0.0f);
      printf("%f\n",left_paddle.y);
    }

    if (buttons & (1<<LEFT_DOWN_BUTTON)){
      left_paddle.y = MIN(left_paddle.y + (10.0f * TICK), ROWS - (left_paddle.length));
      printf("%f\n",left_paddle.y);
    }

    if (buttons & (1<<RIGHT_UP_BUTTON)) {
      right_paddle.y = MAX(right_paddle.y - (10.0f * TICK), 0.0f);
    }

    if (buttons & (1<<RIGHT_DOWN_BUTTON)) {
      right_paddle.y = MIN(right_paddle.y + (10.0f * TICK), ROWS - (right_paddle.length));
    }

    // calculate new ball position from old position and velocity
    ball.y = MAX(MIN(ball.y + ball.vel_y * TICK, ROWS - 1.0f), 0.0f);

    //check for up and down borders
    if (ball.y <= 0.0f || ball.y >= ROWS - 1.0f) {
      ball.vel_y = -ball.vel_y;
    }

    ball.x = MAX(MIN(ball.x + ball.vel_x * TICK, COLS - 1.0f), 0.0f);

    // check for left border
    if (ball.x <= 1.0f) {
      printf("ballx: %f, ball y: %f\n", ball.x, ball.y);
      printf("left_paddlex: %f\n", left_paddle.y);

      if (ball.y >= left_paddle.y && ball.y <= (left_paddle.y + left_paddle.length - 1.0f)) {
        ball.vel_x = -ball.vel_x;
      } else {
        printf("ballx: %f, ball y: %f\n", ball.x, ball.y);
        printf("right player lost\n");
        reset_pos();
      }
    }

    // check for right border
    if (ball.x >= COLS - 2.0f) {
      if (ball.y >= right_paddle.y && ball.y <= (right_paddle.y + right_paddle.length - 1.0f)) {
        ball.vel_x = -ball.vel_x;
      } else {
        printf("ballx: %f, ball y: %f\n", ball.x, ball.y);
        printf("left player lost\n");
        reset_pos();
      }
    }
    //increase balls velocity
    if (ball.vel_x < 0.0f) {
      ball.vel_x = ball.vel_x - ACCELERATION;
    } else {
      ball.vel_x = ball.vel_x + ACCELERATION;
    }
    if (ball.vel_y < 0.0f) {
      ball.vel_y = ball.vel_y - ACCELERATION;
    } else {
      ball.vel_y = ball.vel_y + ACCELERATION;
    }

    // draw new positions on screen
    reset_screen();

    for (i=0; i<left_paddle.length; i++) {
      set_pixel( 0, floor(left_paddle.y) + i);
    }
    for (i=0; i<right_paddle.length; i++) {
      set_pixel(COLS - 1, floor(right_paddle.y) + i);
    }
    set_pixel(floor(ball.x), floor(ball.y));

    for (k=0; k<1000; k++) {}

    draw();
  }
}

/*
 * function to output a test picture. used to verify the VHDL implementation.
 * Will turn on one pixel after another starting with row 1 on the right side
 * Will also print on console if one of the game buttons is pressed.
*/
void test_picture() {
  int i,j,k;
  unsigned buttons;
  while (1){
    for (i=0; i<ROWS; i++) {
      for (j=0; j<COLS; j++) {
        buttons = ~IORD_ALTERA_AVALON_PIO_DATA(PIO_BUTTON_BASE);

        if (buttons & (1<<LEFT_UP_BUTTON)){
          printf("left button up");
        }
        if (buttons & (1<<LEFT_DOWN_BUTTON)){
          printf("left button down");
        }

        if (buttons & (1<<RIGHT_UP_BUTTON)){
          printf("right button up");
        }
        if (buttons & (1<<RIGHT_DOWN_BUTTON)){
          printf("right button down");
        }
        printf("\n");
        set_pixel(j, i);
        draw();
        // sleep 
        for(k=0; k<100000; k++) {}
      }
    }
    reset_screen();
  }
}

int main() {
  //test_picture();
  game();
}
