#pragma once

#include <cassert>

#include "GlInclude.h"


namespace lib_gl
{


class GLBitmapRender
{
public:
	GLBitmapRender(void);
	virtual ~GLBitmapRender(void);

	bool InitializeRender(int w, int h, int num_bits);

	void FinalizeRender(void);

	void BeginRender(void);
	void EndRender(void);

	HDC     GetBmpDC        (void) const { return m_hdc;     }
	HGLRC   GetBmpRC        (void) const { return m_hglrc;   }
	HBITMAP GetBmpHandle    (void) const { return m_hbmp;    }
	HBITMAP GetBmpPreHandle (void) const { return m_hbmpOld; }

	int GetWidth(void) const { return m_Width; }
	int GetHeight(void) const { return m_Height; }
	int GetNumPixelBits(void) const { return m_NumPixelBits; }

protected:
	HBITMAP CreateBitmapRenderBuffer(int w, int h, int num_bits, const HDC& hdc);
	bool SetPfdForGDIBuffer(const HDC& hdc, int color_bits);


protected:
	HDC     m_hdc;
	HGLRC   m_hglrc;
	HBITMAP m_hbmp;
	HBITMAP m_hbmpOld;

	int m_Width;
	int m_Height;
	int m_NumPixelBits;

private:
	bool m_Initialized;
};


inline GLBitmapRender::GLBitmapRender(void) :
	m_Initialized(false),
	m_Width(0),
	m_Height(0),
	m_NumPixelBits(0)
{
}

inline GLBitmapRender::~GLBitmapRender(void)
{
	if( m_Initialized )
		FinalizeRender();
}

inline bool GLBitmapRender::InitializeRender(int w, int h, int num_bits)
{
	assert( !m_Initialized );
	if( m_Initialized )
		return false;

	// 現在のディスプレイと同様の設定のHDCを生成
	m_hdc = CreateCompatibleDC( NULL );

	// OpenGL描画先のビットマップを生成
	m_hbmp = CreateBitmapRenderBuffer(w, h, num_bits, m_hdc);

	// HDCの描画先バッファを設定
	m_hbmpOld = (HBITMAP)::SelectObject(m_hdc, m_hbmp);

	// HDCにGDIのビットマップを対象としたPFDを設定
	if( !SetPfdForGDIBuffer(m_hdc, num_bits) )
		return false;

	m_hglrc = wglCreateContext( m_hdc );
	if( m_hglrc == NULL )
		return false;

	m_Width = w;
	m_Height = h;
	m_NumPixelBits = num_bits;

	m_Initialized = true;
	return true;
}

//! OpenGL描画用のビットマップバッファを生成する
inline HBITMAP GLBitmapRender::CreateBitmapRenderBuffer(int w, int h, int num_bits, const HDC& hdc)
{
	BITMAPINFOHEADER bih;
	memset(&bih, 0, sizeof(BITMAPINFOHEADER));
	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biWidth  = w;
	bih.biHeight = h;
	bih.biPlanes = 1;
	bih.biBitCount = num_bits;
	bih.biCompression = BI_RGB;

	void* pbits;
	return CreateDIBSection( hdc, (BITMAPINFO*)&bih, DIB_PAL_COLORS, &pbits, NULL, 0);
}

//! GDIのビットマップバッファに描画するためのPFDを設定する
inline bool GLBitmapRender::SetPfdForGDIBuffer(const HDC& hdc, int color_bits)
{
	PIXELFORMATDESCRIPTOR pfd =
	{
		sizeof (PIXELFORMATDESCRIPTOR),  //構造体のサイズ
		1,                               //OpenGL バージョン
		PFD_DRAW_TO_BITMAP |             //ウィンドウスタイル
		PFD_SUPPORT_OPENGL |             //OpenGL を使う
		PFD_SUPPORT_GDI,                 //GDIのバッファを使う?
		PFD_TYPE_RGBA,                   //ピクセルのカラーデータ
		color_bits,                      //色のビット数
		0, 0,                            //RGBAのビットとシフト設定
		0, 0,                            //G
		0, 0,                            //B
		0, 0,                            //A
		0,                               //アキュムレーションバッファ
		0, 0, 0, 0,                      //RGBAアキュムレーションバッファ
		32,                              //デプスバッファ     のピクセル当りのビット数
		8,                               //ステンシルバッファ のピクセル当りのビット数
		0,                               //補助バッファ       のピクセル当りのビット数
		PFD_MAIN_PLANE,                  //レイヤータイプ
		0,                               //予約 
		0, 0, 0                          //未使用
	};

	int pixelformat = ChoosePixelFormat( hdc , &pfd );
	if( pixelformat == 0 )
		return false;

	if( SetPixelFormat( hdc , pixelformat , &pfd ) == FALSE )
		return false;

	return true;
}


inline void GLBitmapRender::FinalizeRender(void)
{
	if( !m_Initialized )
		return;

	BOOL hglrc_deleted = wglDeleteContext( m_hglrc );

	// HDCのバッファを元に戻す
	SelectObject(m_hdc, m_hbmpOld);

	BOOL bmp_deleted = DeleteObject(m_hbmp);
	BOOL dc_deleted = DeleteDC(m_hdc);

	assert( hglrc_deleted );
	assert( bmp_deleted   );
	assert( dc_deleted    );

	m_Initialized = false;

	m_Width = 0;
	m_Height = 0;
	m_NumPixelBits = 0;
}


inline void GLBitmapRender::BeginRender(void)
{
	assert( m_Initialized );
	wglMakeCurrent( m_hdc , m_hglrc );
}

inline void GLBitmapRender::EndRender(void)
{
	assert( m_Initialized );
	wglMakeCurrent( NULL , NULL );
}


}
