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

#include "stdafx.h"
#include "BKOscScrOGL.h"

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

BKEXTERN_C
{

	BKOSCDLL_API BKOSCSCRHANDLE WINAPI GetBKOscScr()
	{
		return new CBKOscScrOGL;
	}

	//сколько вершин, столько и цветов должно быть
	//группа из трёх чисел - цвет одной вершины
	const GLdouble CBKOscScrOGL::m_cpColors3[] =
	{
		0.5, 0.5, 0.5,  0.5, 0.5, 0.5,
		1.0, 1.0, 1.0,  1.0, 1.0, 1.0,
		1.0, 1.0, 1.0,  1.0, 1.0, 1.0
	};

	//группа из двух чисел - координаты вершины
	const GLdouble CBKOscScrOGL::m_cpVertices2[] =
	{
		-1.0,  0.0,    1.0,  0.0, //разделительная линия между каналами
		    -1.0,  0.5,    1.0,  0.5, //линии центров каналов.
		    -1.0, -0.5,    1.0, -0.5  //линии центров каналов.
	    };
	//массив порядка просмотра вершин и цветов в двух верхних массивах.
	const GLint CBKOscScrOGL::m_cpIndices1[] = { 0, 1, 2, 3, 4, 5 };

	CBKOscScrOGL::CBKOscScrOGL()
		: CBKOScScr_Shared()
		, m_pVerticesL(nullptr)
		, m_pVerticesR(nullptr)
		, m_pIndices2(nullptr)
		, m_pDC(nullptr)
		, m_HDC(nullptr)
		, m_hGLRC(nullptr)
		, m_cx(0)
		, m_cy(0)
	{
	}

	CBKOscScrOGL::~CBKOscScrOGL()
	{
	}


	void CBKOscScrOGL::BKOSC_OnSize(int cx, int cy)
	{
		if (cx > 0 && cy > 0)
		{
			m_cx = cx;
			m_cy = cy;
		}
	}

	HRESULT CBKOscScrOGL::BKOSC_OnSetBuffer(int nLen)
	{
		SAFE_DELETE_ARRAY(m_pVerticesL);
		SAFE_DELETE_ARRAY(m_pVerticesR);
		SAFE_DELETE_ARRAY(m_pIndices2);
		m_pVerticesL = new GLdouble[nLen * 2];
		m_pVerticesR = new GLdouble[nLen * 2];
		m_pIndices2  = new GLint[nLen];

		if (m_pVerticesL == nullptr || m_pVerticesR == nullptr || m_pIndices2 == nullptr)
		{
			return E_FAIL;
		}

		for (GLint i = 0; i < nLen; ++i)
		{
			m_pIndices2[i] = i;
		}

		return S_OK;
	}

	HRESULT CBKOscScrOGL::BKOSC_Screen_Init(CWnd * pWndScreen)
	{
		HRESULT hr = E_FAIL;
		BOOL bRet = FALSE;
		m_pDC = new CClientDC(pWndScreen);

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

			if (m_HDC)
			{
				if (SetWindowPixelFormat(m_HDC))
				{
					m_hGLRC = wglCreateContext(m_HDC);

					if (m_hGLRC)
					{
						bRet = wglMakeCurrent(m_HDC, m_hGLRC);
					}

					hr = S_OK;
				}
			}

			wglMakeCurrent(nullptr, nullptr);
		}

		return hr;
	}

	void CBKOscScrOGL::BKOSC_Screen_Done()
	{
		if (wglGetCurrentContext())
		{
			wglMakeCurrent(nullptr, nullptr);
		}

		if (m_hGLRC)
		{
			wglDeleteContext(m_hGLRC);
			m_hGLRC = nullptr;
		}

		m_HDC = nullptr;
		SAFE_DELETE(m_pDC);
		SAFE_DELETE_ARRAY(m_pVerticesL);
		SAFE_DELETE_ARRAY(m_pVerticesR);
		SAFE_DELETE_ARRAY(m_pIndices2);
	}

	bool CBKOscScrOGL::SetWindowPixelFormat(HDC hDC)
	{
		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 | PFD_DEPTH_DONTCARE, // 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 nGLPixelIndex;
		nGLPixelIndex = ChoosePixelFormat(hDC, &pfd);

		if (nGLPixelIndex == 0) // Let's choose a default index.
		{
			nGLPixelIndex = 1;

			if (DescribePixelFormat(hDC, nGLPixelIndex, sizeof(PIXELFORMATDESCRIPTOR), &pfd) == 0)
			{
				return false;
			}
		}

		return SetPixelFormat(hDC, nGLPixelIndex, &pfd) == TRUE;
	}


	bool CBKOscScrOGL::create_context()
	{
		bool bRet = false;

		if (m_hGLRC) //каждый раз пересоздаём контекст
		{
			wglMakeCurrent(nullptr, nullptr);
			wglDeleteContext(m_hGLRC);
			m_hGLRC = wglCreateContext(m_HDC);

			if (m_hGLRC)
			{
				bRet = wglMakeCurrent(m_HDC, m_hGLRC) == TRUE;
			}
		}

		return bRet;
	}

	void CBKOscScrOGL::BKOSC_OnDisplay(SAMPLE_TYPE * inBuf, int inLen)
	{
		if (!wglMakeCurrent(m_HDC, m_hGLRC))
		{
			if (!create_context())
			{
				return;
			}
		}

		glViewport(0, 0, m_cx, m_cy);
		register auto dblinLen = GLdouble(inLen);
		register GLdouble s = dblinLen / 2.0;
		//теперь надо сформировать массивы вертексов.
		register int kL = 0, kR = 0;
		register GLdouble i = 0.0f;

		while (i < dblinLen)
		{
			//рисуем левый канал -32768..32767 => 0..1
			register GLdouble fX = i++ / s - 1.001;
			m_pVerticesL[kL++] = fX;
			m_pVerticesL[kL++] = (GLdouble(*inBuf++) + FLOAT_BASE_HALF) / FLOAT_BASE;
			//рисуем правый канал -32768..32767 => -1..0
			m_pVerticesR[kR++] = fX;
			m_pVerticesR[kR++] = (GLdouble(*inBuf++) + FLOAT_BASE_HALF) / FLOAT_BASE - 1.0;
		}

		glClearColor(0.0, 0.0, 0.0, 0.0); //очистка экрана
		glClear(GL_COLOR_BUFFER_BIT);
		glEnableClientState(GL_COLOR_ARRAY);
		glColorPointer(3, GL_DOUBLE, 0, m_cpColors3);
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(2, GL_DOUBLE, 0, m_cpVertices2);
		glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, m_cpIndices1);
		glDisableClientState(GL_COLOR_ARRAY);
		glColor3f(0.0f, 1.0f, 0.0f);
		glVertexPointer(2, GL_DOUBLE, 0, m_pVerticesL);
		glDrawElements(GL_LINE_STRIP, inLen, GL_UNSIGNED_INT, m_pIndices2);
		glVertexPointer(2, GL_DOUBLE, 0, m_pVerticesR);
		glDrawElements(GL_LINE_STRIP, inLen, GL_UNSIGNED_INT, m_pIndices2);
		glDisableClientState(GL_VERTEX_ARRAY);
		glFlush();
		SwapBuffers(m_HDC);
		wglMakeCurrent(nullptr, nullptr);
	}

};