// Breakout: hold L / R = move the paddle, M = launch the ball. Break every brick; 3 balls.
#pragma once

const uint8_t BRICK_ROWS = 4;
const uint8_t BRICK_COLS = 8;
const uint8_t BRICK_W = 16;
const uint8_t BRICK_H = 5;
const uint8_t BRICK_TOP = 10;
const uint8_t PADDLE_W = 22;
const uint8_t PADDLE_Y = 60;

struct BreakoutState {
  uint8_t bricks[BRICK_ROWS];                // one bit per brick
  int16_t ball_x16, ball_y16, vx16, vy16;
  int16_t paddle_x;
  uint8_t balls, level;
  bool launched;
};

void breakout_level(BreakoutState &g) {
  for (uint8_t r = 0; r < BRICK_ROWS; r++) {
    g.bricks[r] = 0xFF;
  }

  g.launched = false;
}

void breakout_init(BreakoutState &g) {
  memset(&g, 0, sizeof g);
  g.paddle_x = 64 - PADDLE_W / 2;
  g.balls = 3;
  g.level = 1;
  breakout_level(g);
}

void breakout_update(BreakoutState &g) {
  if (key_down[KEY_L]) {
    g.paddle_x -= 3;
  }

  if (key_down[KEY_R]) {
    g.paddle_x += 3;
  }

  g.paddle_x = constrain(g.paddle_x, 0, 128 - PADDLE_W);

  if (!g.launched) {
    g.ball_x16 = (g.paddle_x + PADDLE_W / 2) * 16;
    g.ball_y16 = (PADDLE_Y - 3) * 16;

    if (key_tap[KEY_M]) {
      int8_t speed = 22 + g.level * 3;
      g.launched = true;
      g.vx16 = random(2) ? speed : -speed;
      g.vy16 = -speed;
    }

    return;
  }

  g.ball_x16 += g.vx16;
  g.ball_y16 += g.vy16;
  int16_t x = g.ball_x16 / 16;
  int16_t y = g.ball_y16 / 16;

  if (x <= 0 || x >= 125) {
    g.vx16 = -g.vx16;
    g.ball_x16 = constrain(x, 1, 124) * 16;
  }

  if (y <= 0) {
    g.vy16 = -g.vy16;
  }

  if (g.vy16 > 0 && y + 3 >= PADDLE_Y && y + 3 <= PADDLE_Y + 2 && x + 3 >= g.paddle_x && x <= g.paddle_x + PADDLE_W) {
    int16_t offset = x + 1 - (g.paddle_x + PADDLE_W / 2);    // where the ball hits sets the angle
    int16_t speed = abs(g.vy16);
    g.vy16 = -speed;
    g.vx16 = offset * speed / 8;

    if (abs(g.vx16) < speed / 3) {           // never straight up: the ball would bounce in one column forever
      g.vx16 = g.vx16 < 0 ? -speed / 3 : speed / 3;
    }
  }

  if (y > 64) {
    g.launched = false;

    if (--g.balls == 0) {
      end_game();
    }

    return;
  }

  int16_t row = (y + 1 - BRICK_TOP) / BRICK_H;
  int16_t col = (x + 1) / BRICK_W;

  if (y + 1 >= BRICK_TOP && row < BRICK_ROWS && col >= 0 && col < BRICK_COLS && (g.bricks[row] & (1 << col))) {
    g.bricks[row] &= ~(1 << col);
    g.vy16 = -g.vy16;
    score += BRICK_ROWS - row;
    bool left = false;

    for (uint8_t r = 0; r < BRICK_ROWS; r++) {
      left |= g.bricks[r] != 0;
    }

    if (!left) {
      g.level++;
      breakout_level(g);
    }
  }
}

void breakout_draw(BreakoutState &g) {
  for (uint8_t r = 0; r < BRICK_ROWS; r++) {
    for (uint8_t c = 0; c < BRICK_COLS; c++) {
      if (g.bricks[r] & (1 << c)) {
        box(c * BRICK_W + 1, BRICK_TOP + r * BRICK_H, BRICK_W - 2, BRICK_H - 1);
      }
    }
  }

  box(g.paddle_x, PADDLE_Y, PADDLE_W, 3);
  box(g.ball_x16 / 16, g.ball_y16 / 16, 3, 3);
  oled.setFont(u8g2_font_5x7_tr);
  draw_number(score, 0, 7, false);

  for (uint8_t i = 0; i < g.balls; i++) {
    box(124 - i * 6, 2, 3, 3);
  }

  if (!g.launched && !game_over) {
    text_center("M: launch", 45);
  }
}
