

#include <XConfig.h>
#include <XOpenGLView.h>
#include <XWindow.h>
#include <XDraw.h>

XGOpenGLView *XGOpenGLView::sCurrentCtx = 0;

XGOpenGLView::XGOpenGLView( XGView *parent, XGArgStream &s ):
	XGView( parent, s )
{
#if OPT_MACOS
	fCtx = 0;
#endif
#if OPT_WINOS
	fHWND = 0;
	fDC = 0;
	fGLRC = 0;
	fWindowShown = false;
#endif
	fBackBuffer = !s.ReadBoolean();
	fDepthBuffer = !s.ReadBoolean();
}



XGOpenGLView::~XGOpenGLView()
{
	if (GetCurrentContext() == this) {
		SetCurrentContext( 0 );
	}
	
#if OPT_MACOS
	if (fCtx) {
		aglSetDrawable( fCtx, 0 );
		aglDestroyContext( fCtx );
		fCtx = 0;
	}
#endif

#if OPT_WINOS
	if (fDC) {
		ReleaseDC( fHWND, fDC );
		fDC = 0;
	}
	
	if (fGLRC) {
		wglDeleteContext( fGLRC );
		fGLRC = 0;
	}
	
	if (fHWND) {
		DestroyWindow( fHWND );
		fHWND = 0;
	}
#endif
}


void XGOpenGLView::DoDrawView( XRect )
{
	XGOpenGLDraw draw( this );	// set clip region, etc.
	
	glClearColor( 1, 0, 0, 0 );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glBegin(GL_POLYGON);
	glColor3d(1.0, 1.0, 0.0);
	glVertex3d( 0.8,  0.8, 0.0);
	glColor3d(0.0, 1.0, 0.0);
	glVertex3d( 0.8, -0.8, 0.0);
	glColor3d(0.0, 0.0, 1.0);
	glVertex3d(-0.8, -0.8, 0.0);
	glColor3d(1.0, 0.0, 1.0);
	glVertex3d(-0.8,  0.8, 0.0);
	glEnd();
	glFinish();
	this->ShowBackBuffer();
}


void XGOpenGLView::DoSizeView()
{
	XGView::DoSizeView();
	UpdateViewport();
}


void XGOpenGLView::DoMoveView()
{
	XGView::DoMoveView();
	UpdateViewport();
}


#if OPT_WINOS
void XGOpenGLView::DoShowHide()
{
	if (fHWND && fWindowShown && !IsVisible()) {
		ShowWindow( fHWND, SW_HIDE );
		fWindowShown = false;
	}
}
#endif


void XGOpenGLView::ShowBackBuffer()
{
#if OPT_MACOS
	if (fCtx && fBackBuffer) {
		aglSwapBuffers( this->fCtx );
	}
#endif
#if OPT_WINOS
	if (fDC && fGLRC) {	
		SwapBuffers( fDC );
	}
#endif
}


#if OPT_MACOS
void XGOpenGLView::InitOpenGLView( bool doublebuf, bool depthbuf )
{
	if (fCtx) return;
	
	GLint attrib[10];
	int attrib_count = 0;
	attrib[attrib_count++] = AGL_ALL_RENDERERS;
	attrib[attrib_count++] = AGL_RGBA;
	if (doublebuf) {
		attrib[attrib_count++] = AGL_DOUBLEBUFFER;
	}
	if (depthbuf) {
		attrib[attrib_count++] = AGL_DEPTH_SIZE;
		attrib[attrib_count++] = 16;
	}
	attrib[attrib_count++] = AGL_NONE;
	
	AGLPixelFormat fmt = aglChoosePixelFormat( NULL, 0, attrib );
	if (!fmt) return;
	
	AGLContext ctx = aglCreateContext( fmt, NULL );
	if (!ctx) {
		aglDestroyPixelFormat( fmt );
		return;
	}
	
	if (!aglSetDrawable( ctx, (CGrafPtr)GetWindow()->GetGrafPtr() )) {
		aglDestroyContext( ctx );
		aglDestroyPixelFormat( fmt );
		return;
	}
	
	aglDestroyPixelFormat( fmt );
	fCtx = ctx;
	
	if (depthbuf) {
		XGOpenGLView *old_ctx = GetCurrentContext();
		SetCurrentContext( this );
		glEnable( GL_DEPTH_TEST );
		SetCurrentContext( old_ctx );
	}
	UpdateViewport();
}
#endif


#if OPT_WINOS
static PIXELFORMATDESCRIPTOR sPFD = { 
    sizeof(PIXELFORMATDESCRIPTOR),    // size of this pfd 
    1,                                // version number 
    PFD_DRAW_TO_WINDOW |              // support window 
    PFD_SUPPORT_OPENGL,               // support OpenGL 
    PFD_TYPE_RGBA,                    // RGBA type 
    24,                               // 24-bit color depth 
    0, 0, 0, 0, 0, 0,                 // color bits ignored 
    0,                                // no alpha buffer 
    0,                                // shift bit ignored 
    0,                                // no accumulation buffer 
    0, 0, 0, 0,                       // accum bits ignored 
    32,                               // 32-bit z-buffer     
    0,                                // no stencil buffer 
    0,                                // no auxiliary buffer 
    PFD_MAIN_PLANE,                   // main layer 
    0,                                // reserved 
    0, 0, 0                           // layer masks ignored 
};

ATOM XGOpenGLView::sWindowClass = 0;

LRESULT CALLBACK XGOpenGLView::WndProc(
    HWND hwnd,        // handle to window
    UINT uMsg,        // message identifier
    WPARAM wParam,    // first message parameter
    LPARAM lParam)    // second message parameter
{ 
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
            // Initialize the window. 
            return 0; 
 
        case WM_PAINT: 
            // Paint the window's client area. 
            return 0; 
 
        case WM_SIZE: 
            // Set the size and position of the window. 
            return 0; 
 
        case WM_DESTROY: 
            // Clean up window-specific data objects. 
            return 0; 
 
        // 
        // Process other messages. 
        // 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return 0; 
} 

void XGOpenGLView::InitOpenGLView( bool doublebuf, bool depthbuf )
{
	if (fHWND || fDC || fGLRC) return;
	
	if (!sWindowClass) {
		WNDCLASS wc;
		wc.style = CS_HREDRAW | CS_OWNDC;
		wc.lpfnWndProc = XGOpenGLView::WndProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hInstance = _GInstance;
		wc.hIcon = NULL;
		wc.hCursor = NULL;
		wc.hbrBackground = NULL;//CreateSolidBrush(PALETTERGB(0, 0, 255));
		wc.lpszMenuName = NULL;
		wc.lpszClassName = "XGOpenGLView";
		sWindowClass = RegisterClass( &wc );
		if (!sWindowClass) return;
	}
	
	XRect r = GetContentRect();
	ViewToGlobal( &r );
	fHWND = ::CreateWindow( (char *)sWindowClass,	// class
							"",			// title
							WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CLIPSIBLINGS,	// style
							r.left,
							r.top,
							r.right - r.left,
							r.bottom - r.top,
							GetRootWindow(),
							NULL,			// menu
							_GInstance,
							NULL);			// data
	if (!fHWND) return;
	fWindowShown = true;
	
	PIXELFORMATDESCRIPTOR pfd;
	memcpy( &pfd, &sPFD, sizeof(PIXELFORMATDESCRIPTOR) );
	if (doublebuf) {
		pfd.dwFlags |= PFD_DOUBLEBUFFER;
	}
	if (!depthbuf) {
		pfd.cDepthBits = 0;
		pfd.dwFlags |= PFD_DEPTH_DONTCARE;
	}
	
	fDC = GetDC( fHWND );
	if (fDC) {
		int iPixelFormat = ChoosePixelFormat( fDC, &pfd );
		if (iPixelFormat && SetPixelFormat( fDC, iPixelFormat, &pfd )) {
			fGLRC = wglCreateContext( fDC );
		}
		if (!fGLRC) {
			ReleaseDC( fHWND, fDC );
			fDC = 0;
		}
	}
	
	if (depthbuf) {
		XGOpenGLView *old_ctx = GetCurrentContext();
		SetCurrentContext( this );
		glEnable( GL_DEPTH_TEST );
		SetCurrentContext( old_ctx );
	}
	UpdateViewport();
}
#endif


void XGOpenGLView::UpdateViewport()
{
#if OPT_MACOS
	if (fCtx) {
		XGOpenGLView *old_ctx = GetCurrentContext();
		SetCurrentContext( this );
		XRect r = GetContentRect();
		ViewToGlobal( &r );
		XRect w = GetWindow()->GetContentRect();
		glViewport( r.left, w.bottom - r.bottom, r.right - r.left, r.bottom - r.top );
		SetCurrentContext( old_ctx );
	}
#endif

#if OPT_WINOS
	if (fHWND && fDC && fGLRC) {
		XRect r = GetContentRect();
		ViewToGlobal( &r );
		SetWindowPos( fHWND, NULL,
					r.left, r.top, r.right-r.left, r.bottom - r.top,
					SWP_NOZORDER );
		XGOpenGLView *old_ctx = GetCurrentContext();
		SetCurrentContext( this );
		glViewport( 0, 0, r.right - r.left, r.bottom - r.top );
		SetCurrentContext( old_ctx );
	}
#endif
}


XGOpenGLView *XGOpenGLView::GetCurrentContext()
{
	return sCurrentCtx;
}


void XGOpenGLView::SetCurrentContext( XGOpenGLView *ctx )
{
	if (ctx == sCurrentCtx) return;
#if OPT_MACOS
	if (ctx && ctx->fCtx) {
		if (aglSetCurrentContext(ctx->fCtx)) {
			sCurrentCtx = ctx;
		}
	} else {
		if (aglSetCurrentContext( 0 )) {
			sCurrentCtx = 0;
		}
	}
#endif

#if OPT_WINOS
	if (ctx && ctx->fHWND && ctx->fDC && ctx->fGLRC) {
		if (wglMakeCurrent( ctx->fDC, ctx->fGLRC )) {
			sCurrentCtx = ctx;
		}
	} else {
		if (wglMakeCurrent( NULL, NULL )) {
			sCurrentCtx = 0;
		}
	}
#endif
}



XGOpenGLDraw::XGOpenGLDraw( XGOpenGLView *v ):
	XGDraw( v )
{
#if OPT_MACOS
	if (!v->fCtx) {
		v->InitOpenGLView( v->fBackBuffer, v->fDepthBuffer );
	}
#endif

#if OPT_WINOS
	if (!v->fHWND) {
		v->InitOpenGLView( v->fBackBuffer, v->fDepthBuffer );
	}
	if (v->fHWND && !v->fWindowShown && v->IsVisible()) {
		::ShowWindow( v->fHWND, SW_SHOW );
		v->fWindowShown = true;
	}
#endif
	
	old_ctx = XGOpenGLView::GetCurrentContext();
	XGOpenGLView::SetCurrentContext( v );
}


XGOpenGLDraw::~XGOpenGLDraw()
{
	XGView *v = GetView();
	if (v) {
		XGOpenGLView *gv = static_cast<XGOpenGLView *>(v);
		glFinish();
		if (gv->fBackBuffer) {
			gv->ShowBackBuffer();
		}
	}
	XGOpenGLView::SetCurrentContext( old_ctx );
}



