#ifndef VOLUMERENDERER_PRIVATE_H
#define VOLUMERENDERER_PRIVATE_H

#include "volumerenderer.h"
#include "volumeloader.h"
#include "geometry.h"

/*!
\file
This contains private data of the VolumeRenderer, and some implementation definitions.
*/


//these are the only functions that we use that are not in GL 3.3
//instead of using the whole 4.2 context, we just import this one function manually
extern PFNGLBINDIMAGETEXTUREPROC __glBindImageTexture;
extern PFNGLDISPATCHCOMPUTEPROC __glDispatchCompute;

#define glBindImageTexture __glBindImageTexture 
#define glDispatchCompute __glDispatchCompute

//vertex attribute positions
#define ATTRPOS_VERTEX 0
#define ATTRPOS_COLOR 1

//tex unit positions
#define TEXUNIT_VOLUME 0
#define TEXUNIT_TRANSFUNC 1
//these are only temporarily used while loading the volume
#define TEXUNIT_INTENSITY 2
#define TEXUNIT_SMOOTH 3

//current slice texture
#define TEXUNIT_SLICE 4
//previous eye/intermediate buffer
#define TEXUNIT_PREVIOUS 5
//final intermediate buffer (for merging)
#define TEXUNIT_INTERMEDIATE 6

//image units (for shader-writable textures)
#define IMGUNIT_VOLUME 0
#define IMGUNIT_SMOOTH 1

//the maximal number of intersection points of a plane with a cube is 6, + 1 centroid vertex, +1 repeated vertex
#define SLICEBUFFER_STRIDE 8
//the buffer has storage for this amount of slices (i.e. is of size SLICEBUFFER_STRIDE * 3(xyz) * 4(float) * SLICEBUFFER_SLICECOUNT bytes in total)
#define SLICEBUFFER_SLICECOUNT 2048

//1s sync timeout
#define SYNC_TIMEOUT 1000000

/*!
Contains private data of the volume renderer
*/
struct VolumeRendererPrivate
{
	VolumeLoader* volLoader;
	QOpenGLDebugLogger* glLogger;

	Cube bBox;
	bool volumeLoaded,fixSlicingMatrix,renderBB,smoothVolume,showLightDir;
	VolumeMetaData currentVolume;
	QByteArray currentVolumeData; //this is only filled after loadVolume and before update() has finished

	QVector2D window;
	//light dir vector, and halfway vector
	QVector3D lightVec,halfVec;

	VolumeRenderer::ProjectionType projType;
	QMatrix4x4 mProj, view, mvp, mSlicing;

	int sliceCount,renderedSlices;

	bool doDOF;
	float focalPlaneDist,blurAmount;
	
	int winWidth,winHeight;

	/*
	This is the offset in the SLICE BUFFER, not the Z offset of the current slice!
	The value lies in [0, SLICEBUFFER_SLICECOUNT)
	*/
	int currentSliceOffset;
	GLsync sliceSync[SLICEBUFFER_SLICECOUNT];

	QList<QString> glExtensions;
	
	
	float rotX,rotY,distance,sliceDelta;
	float lightAz,lightAngle;
	float gaussSigma;

	QElapsedTimer timer;
	qint64 frametime;
	struct
	{
		QOpenGLShaderProgram* blur;
		QOpenGLShaderProgram* merge;
		QOpenGLShaderProgram* transform;
		QOpenGLShaderProgram* color;
		QOpenGLShaderProgram* bBox;
		QOpenGLShaderProgram* slice;
		QOpenGLShaderProgram* gaussian;
		QOpenGLShaderProgram* gradient;
	} shaders;
	struct
	{
		//QOpenGLBuffer* uniformBuffer;
		QOpenGLBuffer* vBuffer;
		QOpenGLBuffer* lineBuffer;
		QOpenGLBuffer* sliceVBuffer;
		QOpenGLBuffer* iBuffer;
		size_t triangleIndexOffset, lineIndexOffset, sliceIndexOffset;
	} buffers;
	struct
	{
		QOpenGLVertexArrayObject* box;
		QOpenGLVertexArrayObject* line;
		QOpenGLVertexArrayObject* slice;
	} vaos;

	struct
	{
		QOpenGLFramebufferObject* slice;
		QOpenGLFramebufferObject* eye[2];
		QOpenGLFramebufferObject* intermediate[2];
		uint eDraw,eRead;
		uint iDraw,iRead;
	} fbos;

	struct
	{
		QOpenGLTexture* volume;
	} textures;

	VolumeRendererPrivate()
		: distance(2.0f),rotX(0),rotY(0),projType(VolumeRenderer::ProjectionType::Perspective),
		renderBB(true),sliceDelta(0.01f),window(0.0f,1.0f),frametime(0),volumeLoaded(false),fixSlicingMatrix(false),showLightDir(false),smoothVolume(true),lightAz(180.0f),lightAngle(15.0f),gaussSigma(0.6f),
		currentSliceOffset(0), focalPlaneDist(0.5f), blurAmount(0.5f), doDOF(true), sliceCount(0), renderedSlices(0)
	{
		fbos.slice = nullptr;
		fbos.eye[0] = fbos.eye[1] = nullptr;
		fbos.intermediate[0] = fbos.intermediate[1] = nullptr;
		fbos.eDraw = fbos.iDraw = 0;
		fbos.eRead = fbos.iRead = 1;

		for(int i =0;i<SLICEBUFFER_SLICECOUNT;++i)
		{
			sliceSync[i] = nullptr;
		}
	}
};

#endif