#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <iostream>

#define WIDTH 720
#define HEIGHT 720
#define FONTSIZE 32
#define BALLSIZE 16
#define BALLSPEED 16
#define SPEED 9
#define PI 3.1415926535897323846

SDL_Renderer* renderer;
SDL_Window* window;
TTF_Font* font;
SDL_Color color;
bool running;
int frameCount, timerFPS, lastFrame, fps;

SDL_Rect player, npc, ball, scoreboard, leveldisplay;
float xvelocity, yvelocity, ballvelocity, ballsize;
std::string score, slevel;
int lscore, rscore, level, games;
bool turn, isgameover;
std::string version = "1.0.0";

void serve() {
  player.y = npc.y = (HEIGHT/2) - (player.h/2);
  if (turn) {
    ball.x = player.x + (player.w * 4);
    xvelocity = (BALLSPEED*ballvelocity)/2;
    turn = false;
  } else {
    ball.x = npc.x - (player.w * 4);
    xvelocity = -(BALLSPEED*ballvelocity)/2;
    turn = true;
  }

  yvelocity = 0;
  ball.y = HEIGHT / 2 - ((BALLSIZE-ballsize)/2);
}

void write(std::string text, int x, int y) {
  SDL_Surface *surface;
  SDL_Texture *texture;
  const char* t = text.c_str();

  surface = TTF_RenderText_Solid(font, t, color);
  texture = SDL_CreateTextureFromSurface(renderer, surface);

  // スコアボード
  scoreboard.w = surface->w;
  scoreboard.h = surface->h;
  scoreboard.x = x - scoreboard.w;
  scoreboard.y = y - scoreboard.h;

  // レベル表示
  leveldisplay.w = surface->w;
  leveldisplay.h = surface->h;
  leveldisplay.x = x - scoreboard.w + 8;
  leveldisplay.y = y - scoreboard.h + 8;

  // 掃除
  SDL_FreeSurface(surface);
  SDL_RenderCopy(renderer, texture, NULL, &scoreboard);
  SDL_DestroyTexture(texture);
}

void gameover() {
  xvelocity = yvelocity = ballvelocity = 0;
}

void update() {
  // ボールとコンピュータの衝突検出
  if (SDL_HasIntersection(&ball, &npc)) {
    double rel = (npc.y + (npc.h / 2)) - (ball.y + ((BALLSIZE-ballsize) / 2));
    double norm = rel / (npc.h / 2);
    double bounce = norm * (5 * PI / 12);
    xvelocity = -(BALLSPEED*ballvelocity) * cos(bounce);
    yvelocity = (BALLSPEED*ballvelocity) * -sin(bounce);
  }

  // ボールとプレイヤーの衝突検出
  if (SDL_HasIntersection(&ball, &player)) {
    double rel = (player.y + (player.h / 2)) - (ball.y + ((BALLSIZE-ballsize) / 2));
    double norm = rel / (player.h / 2);
    double bounce = norm * (5 * PI / 12);
    xvelocity = (BALLSPEED*ballvelocity) * cos(bounce);
    yvelocity = (BALLSPEED*ballvelocity) * -sin(bounce);
  }

  // コンピュータがボールの方向を付いてくる
  if (ball.y > npc.y + (npc.h/2)) npc.y += ((SPEED+level*0.5f)*ballvelocity);
  if (ball.y < npc.y + (npc.h/2)) npc.y -= ((SPEED+level*0.5f)*ballvelocity);

  // ボールが左右に逃げたら、スコアアップにして、最初フレームから再開
  if (ball.x <= 0) {
    rscore++;
    serve();
  } else if (ball.x + (BALLSIZE-ballsize) >= WIDTH) {
    lscore++;
    serve();
    games++;
  }

  // コンピュータさんを十分倒したら、レベルアップにする
  if (games == ((level + 1) * 2)) {
    level++;
    ballvelocity += 0.25f;
    ballsize -= 0.2f;
    games = 0;
  }

  // コンピュータのスコアが10になったら、ゲームオーバーだ
  if (rscore > 9) {
    isgameover = true;
  }

  // ボールが上下に逃げる事に対して守る
  if (ball.y <= 0 || ball.y + (BALLSIZE-ballsize) >= HEIGHT) yvelocity = -yvelocity;

  // ボールの移動
  ball.x += xvelocity;
  ball.y += yvelocity;

  // スコアやレベルの文字
  score = std::to_string(lscore) + "  " + std::to_string(rscore);
  slevel = "Lv" + std::to_string(level);

  // プレイヤーを上下に逃げられない為
  if (player.y < 0) player.y = 0;
  if (player.y + player.h > HEIGHT) player.y = HEIGHT-player.h;

  // コンピューターを上下に逃げられない為
  if (npc.y < 0) npc.y = 0;
  if (npc.y + npc.h > HEIGHT) npc.y = HEIGHT-npc.h;
}

void input() {
  SDL_Event e;
  const Uint8 *keystates = SDL_GetKeyboardState(NULL);

  while (SDL_PollEvent(&e)) {
    if (e.type == SDL_QUIT) {
      running = false;
    }
  }
  
  if (keystates[SDL_SCANCODE_Q]) running = false; // Qでゲームを終了
  if (!isgameover) {
    if (keystates[SDL_SCANCODE_UP]) player.y -= (SPEED*ballvelocity);   // ↑でプレイヤーを上がる
    if (keystates[SDL_SCANCODE_DOWN]) player.y += (SPEED*ballvelocity); // ↓でプレイヤーを下げる
  }
}

void render() {
  // 黒色背景
  SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 255);
  SDL_RenderClear(renderer);

  frameCount++;
  timerFPS = SDL_GetTicks() - lastFrame;
  if (timerFPS < (1000/60)) {
    SDL_Delay((1000/60) - timerFPS);
  }

  // 背景以外もレンダーして
  SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 255);

  // プレイヤー、コンピュータ、とボールを創作
  if (!isgameover) {
    SDL_RenderFillRect(renderer, &player);
    SDL_RenderFillRect(renderer, &npc);
    SDL_RenderFillRect(renderer, &ball);
  }

  write(score, WIDTH/2+FONTSIZE, FONTSIZE*2);
  write(slevel, WIDTH/2+FONTSIZE, FONTSIZE*3);
  if (isgameover) {
    write("GAME OVER", WIDTH/2+84, HEIGHT/2);
  }
  write("076.moe SUWAKO NO PONG v" + version, WIDTH-150, HEIGHT-20);
  SDL_RenderPresent(renderer);
}

int main(void) {
  // SDLを初期化
  if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
    std::cout << "SDL_Init()に失敗" << '\n';
    return -1;
  }

  // ウィンドウとレンダーを創作
  if (SDL_CreateWindowAndRenderer(WIDTH, HEIGHT, 0, &window, &renderer) < 0) {
    std::cout << "SDL_CreateWindowAndRenderer()に失敗" << '\n';
    SDL_Quit();
    return -1;
  }

  // フォントを読込
  TTF_Init();
  font = TTF_OpenFont("Peepo.ttf", FONTSIZE);
  if (!font) {
    std::cout << "フォントを読込に失敗" << '\n';
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return -1;
  }

  // 初期設定
  running = true;
  static int lastTime = 0;

  color.r = color.g = color.b = 255;
  lscore = rscore = games = 0;
  ballvelocity = ballsize = 1.0f;
  level = 1;
  player.x = 32;
  player.h = (HEIGHT/4)/(ballsize*1.15f);
  player.y = (HEIGHT/2) - (player.h/2);
  player.w = 12;
  npc = player;
  npc.x = WIDTH - npc.w - 32;
  ball.w = ball.h = (BALLSIZE-ballsize);

  serve();

  // ゲームループ
  while (running) {
    lastFrame = SDL_GetTicks();
    if (lastFrame >= (lastTime + 1000)) {
      lastTime = lastFrame;
      fps = frameCount;
      frameCount = 0;
    }

    if (!isgameover) {
      update();
      input();
      render();
    } else {
      gameover();
      input();
      render();
    }
  }

  // 掃除
  TTF_CloseFont(font);
  SDL_DestroyRenderer(renderer);
  SDL_DestroyWindow(window);
  SDL_Quit();

  return 0;
}