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

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

  \author Satofumi KAMIMURA

  $Id: glScreen.cpp 1814 2010-05-03 07:57:38Z 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;
}
