#include <SDL.h>
#include <iostream>

#define SDL_WINDOW_TITLE "SDL2"
#define SDL_WINDOW_WIDTH (640)
#define SDL_WINDOW_HEIGHT (360)

/** NOTE: Windows on KVM cannot call direct3D.
    Then SDL_RENDERER_SOFTWARE will be needed. */
#ifdef NEED_RENDERER_SOFTWARE
#define SDL_RENDERER_FLAGS (SDL_RENDERER_SOFTWARE)
#else
#define SDL_RENDERER_FLAGS (0)
#endif

#define SDL_PrintError(name)                                    \
  do {                                                          \
    std::cerr << #name << ": " << SDL_GetError() << std::endl;  \
  } while (0)

/** NOTE: SDL_Renderer is referered with draw function like
    SDL_RenderDrawRect, SDL_RenderCopy and etc. for drawing.
    These draw functions and SDL_CreateRenderer must be called
    by same thread. */
static SDL_Window *gWindow;
static SDL_Renderer *gRenderer;

/** NOTE: Draw rect position. */
static int gRectX = 0;
static int gRectY = 0;

static bool initialize()
{
  if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
    SDL_PrintError(SDL_Init);
    return false;
  }

  gWindow = SDL_CreateWindow(SDL_WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED,
                             SDL_WINDOWPOS_UNDEFINED, SDL_WINDOW_WIDTH,
                             SDL_WINDOW_HEIGHT, 0);
  if (gWindow == nullptr) {
    SDL_PrintError(SDL_CreateWindow);
    goto err1;
  }

  gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_FLAGS);
  if (gRenderer == nullptr) {
    SDL_PrintError(SDL_CreateRenderer);
    goto err2;
  }

  return true;

 err2:
  SDL_DestroyWindow(gWindow);
 err1:
  SDL_Quit();
  return false;
}

static void finalize()
{
  SDL_DestroyRenderer(gRenderer);
  SDL_DestroyWindow(gWindow);
  SDL_Quit();
}

static void render()
{
  /** NOTE: Clear render with Color (0, 0, 0, 0) for erasing
      current drawing. */
  SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
  SDL_RenderClear(gRenderer);

  /** NOTE: Draw rect (0, 0, 100, 100) with
      Color (254, 254, 254, 254) */
  SDL_Rect rect = { gRectX, gRectY, 100, 100 };
  SDL_SetRenderDrawColor(gRenderer, 254, 254, 254, 254);
  SDL_RenderFillRect(gRenderer, &rect);

  /** NOTE: Update drawing (switch double buffer). */
  SDL_RenderPresent(gRenderer);

  /** NOTE: Sleep 10 msec. */
  SDL_Delay(10);
}

static bool input()
{
  SDL_Event event;

  /** NOTE: SDL_PollEvent does not sleep while SDL_WaitEvent sleep
      till event comes. SDL_WaitEvent is more relaxible than
      SDL_PollEvent. If input is combined with rendering,
      SDL_WaitEvent cannot be used. */
  while (SDL_PollEvent(&event)) {
    switch (event.type) {
    case SDL_QUIT:
      return true;
      break;
    case SDL_KEYDOWN:
      switch (event.key.keysym.sym) {
      case SDLK_UP:
        gRectY--;
        break;
      case SDLK_RIGHT:
        gRectX++;
        break;
      case SDLK_DOWN:
        gRectY++;
        break;
      case SDLK_LEFT:
        gRectX--;
        break;
      default:
        break;
      }
      break;
    default:
      break;
    }
  }

  return false;
}

int main(int argc, char *argv[])
{
  if (!initialize())
    return 1;

  while (1) {
    if (input())
      break;
    render();
  }

  finalize();
  return 0;
}
