//
// OpenGL window
//

#include "stdafx.h"
#include "glwindow.h"
#include "tools.h"

typedef struct
{
	HGLRC m_hrc;
	CClientDC* m_pDC;
	CPalette* m_pPal;
  HWND m_hWnd;
} GLWindowPrivate;

// ============================================================================

BOOL GLWindowPreTranslateMessage (GLWindow *wnd, MSG *pMsg)
{
	switch (pMsg->message)
	{
		case WM_PAINT:
		{
			GLWindowPrivate* prv = (GLWindowPrivate*)wnd->GetData();
			PAINTSTRUCT ps;
			BeginPaint(prv->m_hWnd, &ps);
			wnd->OnDraw ();
			EndPaint(prv->m_hWnd, &ps);
		} break;
		case WM_SIZE:
			wnd->OnSize (LOWORD (pMsg->lParam), HIWORD (pMsg->lParam));
			break;
		case WM_LBUTTONDOWN:
			wnd->OnLeftButtonDown((SHORT)LOWORD(pMsg->lParam), wnd->GetHeight() - (SHORT)HIWORD(pMsg->lParam) - 1,
				(pMsg->wParam & MK_CONTROL) != 0, (pMsg->wParam & MK_SHIFT) != 0);
			break;
		case WM_LBUTTONUP:
			wnd->OnLeftButtonUp((SHORT)LOWORD(pMsg->lParam), wnd->GetHeight() - (SHORT)HIWORD(pMsg->lParam) - 1,
				(pMsg->wParam & MK_CONTROL) != 0, (pMsg->wParam & MK_SHIFT) != 0);
			break;
		case WM_LBUTTONDBLCLK:
			wnd->OnLeftButtonDoubleClick((SHORT)LOWORD(pMsg->lParam), wnd->GetHeight() - (SHORT)HIWORD(pMsg->lParam) - 1,
				(pMsg->wParam & MK_CONTROL) != 0, (pMsg->wParam & MK_SHIFT) != 0);
			break;
		case WM_RBUTTONDOWN:
			wnd->OnRightButtonDown((SHORT)LOWORD(pMsg->lParam), wnd->GetHeight() - (SHORT)HIWORD(pMsg->lParam) - 1,
				(pMsg->wParam & MK_CONTROL) != 0, (pMsg->wParam & MK_SHIFT) != 0);
			break;
		case WM_RBUTTONUP:
			wnd->OnRightButtonUp((SHORT)LOWORD(pMsg->lParam), wnd->GetHeight() - (SHORT)HIWORD(pMsg->lParam) - 1,
				(pMsg->wParam & MK_CONTROL) != 0, (pMsg->wParam & MK_SHIFT) != 0);
			break;
		case WM_MOUSEMOVE:
			wnd->OnMouseMove((SHORT)LOWORD(pMsg->lParam), wnd->GetHeight() - (SHORT)HIWORD(pMsg->lParam) - 1,
				(pMsg->wParam & MK_CONTROL) != 0, (pMsg->wParam & MK_SHIFT) != 0);
			break;
		case WM_ERASEBKGND:
			return TRUE;
		case WM_CREATE:
			wnd->OnInitialUpdate ();
			break;
		case WM_DESTROY:
			wnd->DestroyContext ();
			return FALSE;
			break;
		case WM_PALETTECHANGED:
			if ((HWND)pMsg->wParam == pMsg->hwnd)  // Responding to own message.
				break;
		case WM_QUERYNEWPALETTE:
		{
			GLWindowPrivate *prv = (GLWindowPrivate*)wnd->GetData ();

			if (prv->m_pPal)
			{
				prv->m_pDC->SelectPalette (prv->m_pPal, FALSE);
					if (prv->m_pDC->RealizePalette () != 0)
					{
						// Some colors changed, so we need to do a repaint.
						InvalidateRect (prv->m_hWnd, NULL, TRUE);
					}
			}
		} break;

		default:
			return FALSE;
	}

	return TRUE;
}

LRESULT CALLBACK GLWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  static CMapPtrToPtr WindowMap;
  GLWindow *wnd;

  if (uMsg == WM_CREATE)
  {
    LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;

    wnd = (GLWindow*)cs->lpCreateParams;
    wnd->Create (hwnd);
    wnd->IncRef ();

    WindowMap.SetAt (hwnd, wnd);
  }

  wnd = (GLWindow*)WindowMap[hwnd];

  if (wnd)
  {
    MSG msg;
    msg.hwnd = hwnd;
    msg.message = uMsg;
    msg.wParam = wParam;
    msg.lParam = lParam;

    GLWindowPreTranslateMessage (wnd, &msg);

    if (uMsg == WM_DESTROY)
    {
      WindowMap.RemoveKey (hwnd);
      wnd->DecRef ();
    }
  }

  return DefWindowProc (hwnd, uMsg, wParam, lParam);
}

// ============================================================================
// GLWindow class

GLWindow::GLWindow (GLWindow *share)
{
  m_nRef = 0;
  m_pShare = share;
  m_pData = (GLWindowPrivate*) malloc (sizeof (GLWindowPrivate));
  memset (m_pData, 0, sizeof (GLWindowPrivate));
}

GLWindow::~GLWindow ()
{
  free (m_pData);
}

bool GLWindow::Create (void* data)
{
  GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;

  prv->m_hWnd = (HWND)data;
 	prv->m_pDC = new CClientDC (CWnd::FromHandle (prv->m_hWnd));
  ASSERT (prv->m_pDC != NULL);

  // Fill in the Pixel Format Descriptor
  PIXELFORMATDESCRIPTOR pfd;
  memset (&pfd,0, sizeof(PIXELFORMATDESCRIPTOR));

  pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);  
  pfd.nVersion = 1;
  pfd.dwFlags  =  PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW;
  pfd.iPixelType = PFD_TYPE_RGBA;
  pfd.cColorBits = 24;
  pfd.cDepthBits = 24;
  pfd.iLayerType = PFD_MAIN_PLANE;

  int nPixelFormat = OpenGLChoosePixelFormat (prv->m_pDC->m_hDC, &pfd);
  if (nPixelFormat == 0)
    return false;

  if (!OpenGLSetPixelFormat (prv->m_pDC->m_hDC, nPixelFormat, &pfd))
    return false;

  prv->m_pPal = new CPalette;

  if (CreateRGBPalette (prv->m_pDC->m_hDC, &prv->m_pPal))
  {
    prv->m_pDC->SelectPalette (prv->m_pPal, FALSE);
    prv->m_pDC->RealizePalette ();
  }
  else
  {
    delete prv->m_pPal;
    prv->m_pPal = NULL;
  }

  // Create a rendering context.
  prv->m_hrc = pfnwglCreateContext (prv->m_pDC->m_hDC);
  if (!prv->m_hrc)
    return false;

  if (m_pShare)
  {
    GLWindowPrivate *share = (GLWindowPrivate*)m_pShare->m_pData;
    pfnwglShareLists (share->m_hrc, prv->m_hrc);
  }

  return true;
}

void GLWindow::DestroyContext ()
{
  GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;

	if (prv->m_pPal)
	{
    CPalette palDefault;
		palDefault.CreateStockObject (DEFAULT_PALETTE);
		prv->m_pDC->SelectPalette (&palDefault, FALSE);
		delete prv->m_pPal;
    prv->m_pPal = NULL;
	}

	if (prv->m_hrc)
		pfnwglDeleteContext (prv->m_hrc);
  prv->m_hrc = NULL;

  if (prv->m_pDC)
		delete prv->m_pDC;
  prv->m_pDC = NULL;
}

void GLWindow::OnInitialUpdate ()
{
  MakeCurrent ();
  GL_InitializeExtensions ();
}

bool GLWindow::MakeCurrent ()
{
  GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;

  if (prv->m_pPal)
	{
		prv->m_pDC->SelectPalette (prv->m_pPal, FALSE);
		prv->m_pDC->RealizePalette ();
	}

  return (pfnwglMakeCurrent (prv->m_pDC->m_hDC, prv->m_hrc) != 0);

//	RECT rc;
//	GetClientRect (&rc);
//	m_pFig->Resize (rc.right, rc.bottom);
}

void GLWindow::SwapBuffers ()
{
  GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;

  OpenGLSwapBuffers (prv->m_pDC->m_hDC);
}

void GLWindow::Redraw ()
{
  GLWindowPrivate *prv = (GLWindowPrivate*)m_pData;
  InvalidateRect (prv->m_hWnd, NULL, FALSE);
}

void GLWindow::CaptureMouse()
{
  GLWindowPrivate* prv = (GLWindowPrivate*)m_pData;
	SetCapture(prv->m_hWnd);
}

void GLWindow::ReleaseMouse()
{
	ReleaseCapture();
}
