// Racer: L / R = change lane. Dodge the cars coming down the road; it gets faster.
#pragma once

const uint8_t LANES = 3;
const uint8_t LANE_W = 16;
const uint8_t ROAD_X = 64 - LANES * LANE_W / 2;
const uint8_t CAR_W = 10;
const uint8_t CAR_H = 14;
const int16_t PLAYER_Y = 48;
const int16_t NO_CAR = -1000;               // a free slot in the car list

struct RacerState {
  int16_t car_y16[4];
  uint8_t car_lane[4];
  uint32_t dist16;                           // road travelled, 1/16 px (uint16 would wrap after 255 points)
  uint8_t lane, speed16, gap;
};

uint8_t lane_x(uint8_t lane) {
  return ROAD_X + lane * LANE_W + (LANE_W - CAR_W) / 2;
}

void racer_init(RacerState &g) {
  memset(&g, 0, sizeof g);
  g.lane = 1;
  g.speed16 = 24;

  for (uint8_t i = 0; i < 4; i++) {
    g.car_y16[i] = NO_CAR;
  }
}

void racer_update(RacerState &g) {
  if (key_tap[KEY_L] && g.lane > 0) {
    g.lane--;
  }

  if (key_tap[KEY_R] && g.lane < LANES - 1) {
    g.lane++;
  }

  g.dist16 += g.speed16;
  score = g.dist16 / 256;
  g.speed16 = 24 + score / 4;

  if (g.speed16 > 64) {
    g.speed16 = 64;
  }

  int16_t top = 64 * 16;

  for (uint8_t i = 0; i < 4; i++) {
    if (g.car_y16[i] == NO_CAR) {
      continue;
    }

    g.car_y16[i] += g.speed16;

    if (g.car_y16[i] > 64 * 16) {
      g.car_y16[i] = NO_CAR;
      continue;
    }

    if (g.car_y16[i] < top) {
      top = g.car_y16[i];
    }

    if (g.car_lane[i] == g.lane && hit(0, PLAYER_Y + 1, 1, CAR_H - 2, 0, g.car_y16[i] / 16, 1, CAR_H)) {
      end_game();
    }
  }

  if (top / 16 > g.gap) {
    for (uint8_t i = 0; i < 4; i++) {
      if (g.car_y16[i] == NO_CAR) {
        g.car_y16[i] = -CAR_H * 16;
        g.car_lane[i] = random(LANES);
        g.gap = 22 + random(22);
        break;
      }
    }
  }
}

void racer_car(uint8_t x, int16_t y, bool player) {
  if (y < 0) {
    box(x, y, CAR_W, CAR_H);
    return;
  }

  oled.drawRBox(x, y, CAR_W, CAR_H, 2);
  oled.setDrawColor(0);
  box(x + 2, y + (player ? 3 : 8), CAR_W - 4, 3);
  oled.setDrawColor(1);
  box(x - 1, y + 2, 1, 3);
  box(x + CAR_W, y + 2, 1, 3);
  box(x - 1, y + CAR_H - 5, 1, 3);
  box(x + CAR_W, y + CAR_H - 5, 1, 3);
}

void racer_draw(RacerState &g) {
  oled.drawVLine(ROAD_X - 2, 0, 64);
  oled.drawVLine(ROAD_X + LANES * LANE_W + 1, 0, 64);
  uint8_t shift = (g.dist16 / 16) % 12;

  for (uint8_t l = 1; l < LANES; l++) {
    for (int16_t y = -12 + shift; y < 64; y += 12) {
      box(ROAD_X + l * LANE_W, y, 1, 6);
    }
  }

  for (uint8_t i = 0; i < 4; i++) {
    if (g.car_y16[i] != NO_CAR) {
      racer_car(lane_x(g.car_lane[i]), g.car_y16[i] / 16, false);
    }
  }

  racer_car(lane_x(g.lane), PLAYER_Y, true);
  oled.setFont(u8g2_font_5x7_tr);
  oled.drawStr(0, 7, "SCORE");
  draw_number(score, 0, 16, false);
  oled.drawStr(100, 7, "BEST");
  draw_number(best, 127, 16, true);
}
