#include <windows.h> // Win32 API 헤더 
#include <gl/gl.h>   // OpenGL 핵심 헤더 
#include <gl/glu.h>  // GLU 유틸리티 헤더 
// OpenGL 렌더링 컨텍스트 핸들 
HGLRC g_hRC = NULL; 
// 디바이스 컨텍스트 핸들 
HDC g_hDC = NULL; 
// 윈도우 핸들 
HWND g_hWnd = NULL; 
// 인스턴스 핸들 
HINSTANCE g_hInstance = NULL; 
// 창 클래스 이름 (유니코드 문자열) 
LPCWSTR szWindowClass = L"OpenGLWindow"; 
// 창 제목 (유니코드 문자열) 
LPCWSTR szTitle = L"Simple OpenGL Triangle (No GLUT)"; 
// OpenGL 초기화 함수 
void InitOpenGL() { 
    // 배경색을 검정색으로 설정 (RGBA 값) 
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
    // 현재 매트릭스를 프로젝션 매트릭스로 설정 
    glMatrixMode(GL_PROJECTION); 
    // 2D 직교 투영 설정 (좌표계: x=0.0~200.0, y=0.0~200.0) 
    gluOrtho2D(0.0, 200.0, 0.0, 200.0); 
} 
// 그리기 함수 
void DrawScene() { 
    // 컬러 버퍼 클리어 
    glClear(GL_COLOR_BUFFER_BIT); 
    // 삼각형 그리기 시작 
    glBegin(GL_TRIANGLES); 
    // 첫 번째 꼭지점: 빨간색 (위쪽) 
    glColor3f(1.0f, 0.0f, 0.0f); 
    glVertex2i(100, 150); 
    // 두 번째 꼭지점: 녹색 (왼쪽 아래) 
    glColor3f(0.0f, 1.0f, 0.0f); 
    glVertex2i(50, 50); 
    // 세 번째 꼭지점: 파란색 (오른쪽 아래) 
    glColor3f(0.0f, 0.0f, 1.0f); 
    glVertex2i(150, 50); 
    glEnd(); // 삼각형 그리기 끝 
    // 프론트 버퍼와 백 버퍼 교체 (더블 버퍼링 사용 시) 
    SwapBuffers(g_hDC); 
} 
// OpenGL 활성화 함수 
// 창 핸들(hWnd), 디바이스 컨텍스트 포인터(hDC), 렌더링 컨텍스트 포인터(hRC)를 인자로 받음 
void EnableOpenGL(HWND hWnd, HDC* hDC, HGLRC* hRC) { 
    PIXELFORMATDESCRIPTOR pfd; 
    int iFormat; 
    // 픽셀 형식 기술자 구조체를 0으로 초기화 
    ZeroMemory(&pfd, sizeof(pfd)); 
    // 픽셀 형식 기술자 구조체의 크기 설정 
    pfd.nSize = sizeof(pfd); 
    // 픽셀 형식 기술자 버전 설정 
    pfd.nVersion = 1; 
    // 픽셀 형식 플래그 설정: 윈도우에 그리기, OpenGL 지원, 더블 버퍼링 사용 
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 
    // 픽셀 타입 설정: RGBA 색상 모드 
    pfd.iPixelType = PFD_TYPE_RGBA; 
    // 색상 비트 깊이 설정: 32비트 색상 
    pfd.cColorBits = 32; 
    // 깊이 버퍼 비트 깊이 설정: 16비트 깊이 버퍼 
    pfd.cDepthBits = 16; 
    // 레이어 타입 설정: 메인 평면 
    pfd.iLayerType = PFD_MAIN_PLANE; 
    // 윈도우의 디바이스 컨텍스트를 얻음 
    *hDC = GetDC(hWnd); 
    // 주어진 픽셀 형식 기술자에 가장 적합한 픽셀 형식을 찾음 
    iFormat = ChoosePixelFormat(*hDC, &pfd); 
    // 디바이스 컨텍스트에 픽셀 형식을 설정 
    SetPixelFormat(*hDC, iFormat, &pfd); 
    // OpenGL 렌더링 컨텍스트 생성 
    *hRC = wglCreateContext(*hDC); 
    // 현재 스레드의 디바이스 컨텍스트에 렌더링 컨텍스트를 연결 
    wglMakeCurrent(*hDC, *hRC); 
    // OpenGL 초기화 함수 호출 
    InitOpenGL(); 
} 
// OpenGL 비활성화 함수 
// 창 핸들(hWnd), 디바이스 컨텍스트(hDC), 렌더링 컨텍스트(hRC)를 인자로 받음 
void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC) { 
    // 현재 렌더링 컨텍스트를 해제 
    wglMakeCurrent(NULL, NULL); 
    // 렌더링 컨텍스트를 삭제 
    wglDeleteContext(hRC); 
    // 디바이스 컨텍스트를 해제 
    ReleaseDC(hWnd, hDC); 
} 
// 윈도우 프로시저 (메시지 처리 콜백 함수) 
// 윈도우 핸들, 메시지 코드, 추가 정보(wParam, lParam)를 인자로 받음 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 
    switch (message) { 
        // 윈도우 생성 시 발생하는 메시지 
    case WM_CREATE: 
        // OpenGL 활성화 함수 호출 
        EnableOpenGL(hWnd, &g_hDC, &g_hRC); 
        break; 
        // 윈도우가 다시 그려져야 할 때 발생하는 메시지 
    case WM_PAINT: 
        // 그리기 함수 호출 
        DrawScene(); 
        // WM_PAINT 메시지를 유효화 (반복적인 WM_PAINT 메시지 방지) 
        ValidateRect(hWnd, NULL); 
        break; 
        // 윈도우 크기 변경 시 발생하는 메시지 
    case WM_SIZE: 
        // 뷰포트 크기를 윈도우 크기에 맞게 조정 (선택 사항) 
        // L_PARAM에서 새 너비와 높이를 추출하여 glViewport 함수에 전달 
        // glViewport(0, 0, LOWORD(lParam), HIWORD(lParam)); 
        break; 
        // 윈도우 닫기 버튼 클릭 시 발생하는 메시지 
    case WM_CLOSE: 
        // 윈도우 파괴 메시지를 보냄 
        DestroyWindow(hWnd); 
        break; 
        // 윈도우가 파괴될 때 발생하는 메시지 
    case WM_DESTROY: 
        // OpenGL 비활성화 함수 호출 
        DisableOpenGL(hWnd, g_hDC, g_hRC); 
        // 메시지 루프를 종료하는 메시지를 보냄 
        PostQuitMessage(0); 
        break; 
        // 처리하지 않는 다른 메시지들은 기본 윈도우 프로시저로 전달 
    default: 
        return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
    return 0; 
} 
// WinMain 함수 (Windows 애플리케이션의 진입점) 
// 인스턴스 핸들, 이전 인스턴스 핸들, 명령줄 문자열, 표시 상태 플래그를 인자로 받음 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { 
    WNDCLASS wc; // 윈도우 클래스 구조체 
    MSG msg;     // 메시지 구조체 
    BOOL bDone = FALSE; // 메시지 루프 종료 플래그 
    g_hInstance = hInstance; // 현재 인스턴스 핸들 저장 
    // 윈도우 클래스 스타일 설정: 고유한 DC, 수평/수직 리드로우 시 다시 그리기 
    wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; 
    // 윈도우 프로시저 함수 포인터 설정 
    wc.lpfnWndProc = WndProc; 
    // 추가 클래스 메모리 (0) 
    wc.cbClsExtra = 0; 
    // 추가 윈도우 메모리 (0) 
    wc.cbWndExtra = 0; 
    // 인스턴스 핸들 설정 
    wc.hInstance = hInstance; 
    // 아이콘 설정 (기본 애플리케이션 아이콘) 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    // 커서 설정 (기본 화살표 커서) 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    // 배경 브러시 설정 (검정색 솔리드 브러시) 
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 
    // 메뉴 이름 (없음) 
    wc.lpszMenuName = NULL; 
    // 윈도우 클래스 이름 설정 (LPCWSTR) 
    wc.lpszClassName = szWindowClass; 
    // 윈도우 클래스 등록 시도 
    if (!RegisterClass(&wc)) { 
        // 등록 실패 시 메시지 박스 표시 (유니코드 문자열 사용) 
        MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK); 
        return 0; // 프로그램 종료 
    } 
    // 윈도우 생성 시도 
    g_hWnd = CreateWindowEx( 
        WS_EX_APPWINDOW, // 확장된 윈도우 스타일: 작업 표시줄에 앱으로 표시 
        szWindowClass,   // 윈도우 클래스 이름 (LPCWSTR) 
        szTitle,         // 윈도우 제목 (LPCWSTR) 
        // 윈도우 스타일: 오버랩된 창, 형제 윈도우 영역 클리핑, 자식 윈도우 영역 클리핑 
        WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 
        CW_USEDEFAULT, CW_USEDEFAULT, // x, y 위치 (기본값 사용) 
        400, 400, // 윈도우 너비, 높이 
        NULL,    // 부모 윈도우 핸들 (없음) 
        NULL,    // 메뉴 핸들 (없음) 
        hInstance, // 인스턴스 핸들 
        NULL     // 추가 생성 데이터 (없음) 
    ); 
    // 윈도우 생성 실패 시 
    if (!g_hWnd) { 
        // 메시지 박스 표시 (유니코드 문자열 사용) 
        MessageBox(NULL, L"Window Creation Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK); 
        return 0; // 프로그램 종료 
    } 
    // 윈도우를 화면에 표시 
    ShowWindow(g_hWnd, nCmdShow); 
    // 윈도우를 업데이트하여 WM_PAINT 메시지 발생 
    UpdateWindow(g_hWnd); 
    // 메시지 루프 
    while (!bDone) { 
        // 메시지 큐에서 메시지를 확인 (메시지가 없어도 즉시 반환) 
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 
            // 종료 메시지 (WM_QUIT) 수신 시 
            if (msg.message == WM_QUIT) { 
                bDone = TRUE; // 루프 종료 플래그 설정 
            } 
            else { 
                // 키보드 메시지를 번역 (예: VK_SHIFT -> WM_KEYDOWN) 
                TranslateMessage(&msg); 
                // 메시지를 해당 윈도우의 윈도우 프로시저로 디스패치 
                DispatchMessage(&msg); 
            } 
        } 
        else { 
            // 메시지가 없을 때 지속적으로 그리기 (게임 루프와 유사) 
            DrawScene(); 
        } 
    } 
    // 프로그램 종료 코드 반환 
    return (int)msg.wParam; 
}