// BKScreenOGL.cpp: определяет экспортированные функции для приложения DLL.
//

#include "stdafx.h"
#include "BKScreenOGL.h"

#pragma comment (lib,"opengl32.lib")
#pragma comment (lib,"glu32.lib")

#include "Screen.h"

BKEXTERN_C
{
	BKSCRDLL_API BKSCREENHANDLE APIENTRY GetBKScreen()
	{
		return new CScreenOGL;
	}

	CScreenOGL::CScreenOGL(void) : CBKScreen_Shared()
		, m_pOldPalette(NULL)
		, m_pDC(NULL)
		, m_pDC_FullScreen(NULL)
		, m_HDC(NULL)
		, m_HDC_FullScreen(NULL)
		, m_HDC_Current(NULL)
		, hGLRC(NULL)

		, m_bScrParamChanged(TRUE)
		, m_bScrSizeChanged(TRUE)
	{
		m_rectWndWnd = RECT();
		m_rectViewPort = RECT();
	}


	CScreenOGL::~CScreenOGL(void)
	{
	}


	void CScreenOGL::BKSS_OnSize(int cx, int cy)
	{
		if (m_HDC_Current && (cy > 0) && (cx > 0))
		{
			m_rectWndWnd.right = cx;
			m_rectWndWnd.bottom = cy;
			m_rectViewPort = m_rectWndWnd; //т.к. это работает только в оконном режиме.
			m_bScrSizeChanged = TRUE;
		}
	}


	HRESULT CScreenOGL::BKSS_ScreenView_Init(ScrsParams & sspar, CWnd * pwndScr, CWnd * pwndFullScr)
	{
		CBKScreen_Shared::BKSS_ScreenView_Init(sspar, pwndScr, pwndFullScr);
		HRESULT hr = E_FAIL;
		m_pDC = new CClientDC(m_pwndScreen);
		ASSERT(m_pDC != NULL);

		if (m_pDC)
		{
			m_HDC = m_pDC->GetSafeHdc();

			if (!m_HDC)
			{
				return E_FAIL;
			}
		}

		m_pDC_FullScreen = new CClientDC(m_pwndFullScr);
		ASSERT(m_pDC_FullScreen != NULL);

		if (m_pDC_FullScreen)
		{
			m_HDC_FullScreen = m_pDC_FullScreen->GetSafeHdc();

			if (!m_HDC_FullScreen)
			{
				return E_FAIL;
			}
		}

		if (m_pDC == NULL || m_pDC_FullScreen == NULL)
		{
			return E_FAIL;
		}

		if (m_bFullScreen)
		{
			m_HDC_Current = m_HDC_FullScreen;
			BKSS_SetFullScreenMode();
		}
		else
		{
			m_HDC_Current = m_HDC;
			BKSS_SetWindowMode();
		}

		// Clear screen
		SAFE_DELETE_ARRAY(m_bits);
		m_bits = new DWORD[m_nTextureWidth * m_nTextureHeight ];
		ZeroMemory(m_bits, m_nTextureWidth * m_nTextureHeight * sizeof(DWORD));
		sspar.pBits = m_bits;
		// Create a texture
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_nTextureWidth, m_nTextureHeight, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid *>(m_bits));
		clear();
		return S_OK;
	}

	void CScreenOGL::BKSS_ScreenView_Done()
	{
		if (wglGetCurrentContext())
		{
			wglMakeCurrent(NULL, NULL);
		}

		if (hGLRC)
		{
			wglDeleteContext(hGLRC);
			hGLRC = NULL;
		}

		if (m_pOldPalette)
		{
			m_pDC->SelectPalette(m_pOldPalette, FALSE);
		}

		SAFE_DELETE(m_pDC);
		SAFE_DELETE(m_pDC_FullScreen);
		SAFE_DELETE_ARRAY(m_bits);
	}

	GLdouble texcoords2[] =
	{
		0.0,                   0.0,           1.0,           0.0,
		1.0,                   1.0,           0.0,           1.0
	};

	GLdouble vertices2[] =
	{
		0.0,                    0.0, SCREEN_WIDTH,            0.0,
		SCREEN_WIDTH, SCREEN_HEIGHT,          0.0,  SCREEN_HEIGHT
	};

	GLint indices[] = {0, 1, 2, 3};

	void CScreenOGL::BKSS_DrawScreen()
	{
		if (!wglMakeCurrent(m_HDC_Current, hGLRC))
		{
			if (hGLRC) //каждый раз пересоздаём контекст
			{
				wglDeleteContext(hGLRC);
				hGLRC = wglCreateContext(m_HDC_Current);
				wglMakeCurrent(m_HDC_Current, hGLRC);
			}

			m_bScrParamChanged = TRUE;
		}

		if (m_bScrParamChanged)
		{
			set_screen_param();
		}

		if (m_bScrSizeChanged)
		{
			glViewport(m_rectViewPort.left, m_rectViewPort.top, m_rectViewPort.right, m_rectViewPort.bottom);
			m_bScrSizeChanged = FALSE;
		}

		// Update Texture
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_nTextureWidth, m_nTextureHeight, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid *>(m_bits));
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_DOUBLE, 0, texcoords2);
		glVertexPointer(2, GL_DOUBLE, 0, vertices2);
		glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, indices);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);
		glFlush();
		SwapBuffers(m_HDC_Current);
	}

	BOOL CScreenOGL::BKSS_SetFullScreenMode()
	{
		BOOL bRet = FALSE;

		// Show background window
		if (m_hwndFullScreen)
		{
			m_pwndMain->ShowWindow(SW_HIDE);
			m_bMWHdn = TRUE;
			// Remove caption and border
			SetWindowLongPtr(m_hwndFullScreen, GWL_STYLE, GetWindowLongPtr(m_hwndFullScreen, GWL_STYLE) & (~(WS_CAPTION | WS_BORDER)));
			m_pwndFullScr->SetWindowPos(NULL, m_rectFullScreen.left, m_rectFullScreen.top, m_rectFullScreen.right, m_rectFullScreen.bottom, SWP_SHOWWINDOW);
			m_bFullScreen = TRUE;
			bRet = TRUE;
		}

		m_HDC_Current = m_HDC_FullScreen;
		CreateContext();
		m_rectViewPort = m_rectFSWnd;
		m_bScrSizeChanged = TRUE;
		return bRet;
	}

	BOOL CScreenOGL::BKSS_SetWindowMode()
	{
		// Hide Background window
		if (m_hwndFullScreen)
		{
			if (m_bMWHdn)
			{
				m_pwndMain->ShowWindow(SW_SHOW);
				m_bMWHdn = FALSE;
			}

			m_pwndFullScr->SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW);
		}

		m_HDC_Current = m_HDC;
		CreateContext();
		m_rectViewPort = m_rectWndWnd;
		m_bScrSizeChanged = TRUE;
		m_bFullScreen = FALSE;
		return TRUE;
	}

	void CScreenOGL::BKSS_SetColorMode()
	{
		m_bScrParamChanged = TRUE;
	}


/////////////////////////////////////////////////////////////////////////////
// GL helper functions
	BOOL CScreenOGL::SetWindowPixelFormat()
	{
		static PIXELFORMATDESCRIPTOR pfd =
		{
			sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
			1,                              // version number
			PFD_DRAW_TO_WINDOW |            // support window
			PFD_SUPPORT_OPENGL |            // support OpenGL
			PFD_DOUBLEBUFFER,               // double buffered
			PFD_TYPE_RGBA,                  // RGBA type
			32,                             // 32-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
		};
		int pixelformat;

		if ((pixelformat = ChoosePixelFormat(m_HDC_Current, &pfd)) == 0)
		{
			AfxMessageBox(_T("Screen: ChoosePixelFormat failed"));
			return FALSE;
		}

		if (SetPixelFormat(m_HDC_Current, pixelformat, &pfd) == FALSE)
		{
			AfxMessageBox(_T("Screen: SetPixelFormat failed"));
			return FALSE;
		}

		return TRUE;
	}

	unsigned char threeto8[8] =
	{
		0, 0111 >> 1, 0222 >> 1, 0333 >> 1, 0444 >> 1, 0555 >> 1, 0666 >> 1, 0377
	};

	unsigned char twoto8[4] =
	{
		0, 0x55, 0xaa, 0xff
	};

	unsigned char oneto8[2] =
	{
		0, 255
	};

	static int defaultOverride[13] =
	{
		0, 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91
	};

	static PALETTEENTRY defaultPalEntry[20] =
	{
		{ 0,   0,   0,    0 },
		{ 0x80, 0,   0,    0 },
		{ 0,   0x80, 0,    0 },
		{ 0x80, 0x80, 0,    0 },
		{ 0,   0,   0x80, 0 },
		{ 0x80, 0,   0x80, 0 },
		{ 0,   0x80, 0x80, 0 },
		{ 0xC0, 0xC0, 0xC0, 0 },

		{ 192, 220, 192,  0 },
		{ 166, 202, 240,  0 },
		{ 255, 251, 240,  0 },
		{ 160, 160, 164,  0 },

		{ 0x80, 0x80, 0x80, 0 },
		{ 0xFF, 0,   0,    0 },
		{ 0,   0xFF, 0,    0 },
		{ 0xFF, 0xFF, 0,    0 },
		{ 0,   0,   0xFF, 0 },
		{ 0xFF, 0,   0xFF, 0 },
		{ 0,   0xFF, 0xFF, 0 },
		{ 0xFF, 0xFF, 0xFF, 0 }
	};

	unsigned char CScreenOGL::ComponentFromIndex(int i, UINT nbits, UINT shift)
	{
		unsigned char val;
		val = (unsigned char)(i >> shift);

		switch (nbits)
		{
			case 1:
				val &= 0x1;
				return oneto8[val];

			case 2:
				val &= 0x3;
				return twoto8[val];

			case 3:
				val &= 0x7;
				return threeto8[val];

			default:
				return 0;
		}
	}

	void CScreenOGL::CreateRGBPalette()
	{
		PIXELFORMATDESCRIPTOR pfd;
		LOGPALETTE *pPal;
		int n, i;
		n = ::GetPixelFormat(m_HDC_Current);
		::DescribePixelFormat(m_HDC_Current, n, sizeof(pfd), &pfd);

		if (pfd.dwFlags & PFD_NEED_PALETTE)
		{
			n = 1 << pfd.cColorBits;
			pPal = (PLOGPALETTE) new char[sizeof(LOGPALETTE) + n * sizeof(PALETTEENTRY)];
			ASSERT(pPal != NULL);
			pPal->palVersion = 0x300;
			pPal->palNumEntries = n;

			for (i = 0; i < n; ++i)
			{
				pPal->palPalEntry[i].peRed =
				    ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift);
				pPal->palPalEntry[i].peGreen =
				    ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift);
				pPal->palPalEntry[i].peBlue =
				    ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift);
				pPal->palPalEntry[i].peFlags = 0;
			}

			/* fix up the palette to include the default GDI palette */
			if ((pfd.cColorBits == 8)                           &&
			        (pfd.cRedBits   == 3) && (pfd.cRedShift   == 0) &&
			        (pfd.cGreenBits == 3) && (pfd.cGreenShift == 3) &&
			        (pfd.cBlueBits  == 2) && (pfd.cBlueShift  == 6)
			   )
			{
				for (i = 1 ; i <= 12 ; ++i)
				{
					pPal->palPalEntry[defaultOverride[i]] = defaultPalEntry[i];
				}
			}

			m_cPalette.CreatePalette(pPal);
			delete [] pPal;
			m_pOldPalette = m_pDC->SelectPalette(&m_cPalette, FALSE);
			m_pDC->RealizePalette();
		}
	}


	void CScreenOGL::set_screen_param()
	{
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
		glDisable(GL_DEPTH_TEST);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		// Set up the texture
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		// Enable textures
		glEnable(GL_TEXTURE_2D);
		m_bScrParamChanged = FALSE;
	}

	void CScreenOGL::CreateContext()
	{
		if (SetWindowPixelFormat())
		{
			int n = ::GetPixelFormat(m_HDC_Current);
			::DescribePixelFormat(m_HDC_Current, n, sizeof(m_pfd), &m_pfd);
			CreateRGBPalette();

			if (hGLRC)
			{
				wglDeleteContext(hGLRC);
			}

			hGLRC = wglCreateContext(m_HDC_Current);
			wglMakeCurrent(m_HDC_Current, hGLRC);
			set_screen_param();
		}
	}


	void CScreenOGL::clear()
	{
		glClearColor(0.0, 0.0, 0.0, 0.0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}

};
