/*!
  \example glScreen.cpp
  \brief SDL+OpenGL な画面の生成サンプル

  SDL-1.2.12/test/testgl.c のソースコードを元に実装した

  \author Satofumi KAMIMURA

  $Id: glScreen.cpp 673 2009-03-28 00:47:31Z satofumi $
*/

#include <SDL.h>
#include <SDL_opengl.h>
#include <cstdlib>
#if defined _MSC_VER
#define _USE_MATH_DEFINES
#endif
#include <cmath>


namespace
{
  enum {
    ScreenWidth = 640,
    ScreenHeight = 480,
  };


  void exitAndError(void)
  {
    printf("error: %s\n", SDL_GetError());
    exit(1);
  }


  bool initializeSdl(void)
  {
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
      return false;
    }
    atexit(SDL_Quit);

    return true;
  }


  void initializeOpenGl(void)
  {
    int bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;

    // Initialize the display
    int rgb_size[3];
    switch (bpp) {
    case 8:
      rgb_size[0] = 3;
      rgb_size[1] = 3;
      rgb_size[2] = 2;
      break;

    case 15:
    case 16:
      rgb_size[0] = 5;
      rgb_size[1] = 5;
      rgb_size[2] = 5;
      break;

    default:
      rgb_size[0] = 8;
      rgb_size[1] = 8;
      rgb_size[2] = 8;
      break;
    }
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, rgb_size[0]);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, rgb_size[1]);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, rgb_size[2]);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  }


  void setupOpenGlView(void)
  {
    glPushAttrib(GL_ENABLE_BIT);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glViewport(-1, -1, +1, +1);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(-1.0, +1.0, -1.0, +1.0, -10.0, +10.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glShadeModel(GL_SMOOTH);
  }


  // SDL と同じ座標系設定にする
  void enter2D(void)
  {
    glPushAttrib(GL_ENABLE_BIT);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glViewport(0, 0, ScreenWidth, ScreenHeight);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    glOrtho(0.0, ScreenWidth - 1.0, ScreenHeight - 1.0, 0.0, 0.0, 1.0);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
  }


  // OpenGL 風の座標系設定にする
  void leave2D(void)
  {
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    glPopAttrib();
  }


  bool screen(Uint32 flags)
  {
    initializeOpenGl();

    int bpp = 0;
    SDL_Surface* screen =
      SDL_SetVideoMode(ScreenWidth, ScreenHeight, bpp, flags | SDL_OPENGL);
    if (! screen) {
      return false;
    }

    setupOpenGlView();
    enter2D();

    return true;
  }


  bool isKeyPressed(void)
  {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
      switch (event.type) {

      case SDL_QUIT:
      case SDL_KEYDOWN:
        // 終了イベントが発生しても処理を戻す
        return true;
      }
    }
    return false;
  }


  void clearScreen(void)
  {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  }


  void drawLine(int degree)
  {
    // !!! この時点では、(0, 0)-(640, 480) の座標系に設定して
    // !!! OpenGL 関数を用いた描画をしている。
    // !!! 座標系の設定は enter2D(), leave2D() を参考のこと

    // 画面を中心に、指定角度の向きの線分を描く
    enum { Length = 100 };
    double base_x = ScreenWidth / 2.0;
    double base_y = ScreenHeight / 2.0;
    glColor3f(0.0, 1.0, 0.0);
    glLineWidth(2.0);

    glBegin(GL_LINES);
    glVertex2d(base_x, base_y);

    double radian = degree * M_PI / 180.0;

    glVertex2d(base_x + (Length * cos(radian)),
               base_y + (Length * sin(radian)));
    glEnd();
  }


  void swapScreenBuffers(void)
  {
    SDL_GL_SwapBuffers();
  }
}


int main(int argc, char *argv[])
{
  Uint32 flags = 0;
  for (int i = 1; i < argc; ++i) {
    if (! strcmp(argv[i], "-f")) {
      flags |= SDL_FULLSCREEN;
    }
  }

  // 初期化
  if (! initializeSdl()) {
    exitAndError();
  }

  // 画面の作成
  if (! screen(flags)) {
    exitAndError();
  }

  // メインループ
  int angle_degree = 0;
  while (! isKeyPressed()) {

    clearScreen();
    drawLine(angle_degree++);
    swapScreenBuffers();

    SDL_Delay(10);
  }

  return 0;
}
