/*
* $Id: RayTracer.h 319 2011-08-16 16:17:38 pojkatom $
*
* Description  : Ray tracing module
* Author       : Tomas Pojkar
*
* Purpose      :
*     Ray tracer module for realistic rendering
*/

#ifndef __RAYTRACER_H__
#define __RAYTRACER_H__

#include "rendermodule.h"
#include "RayTracerCamera.h"
#include "iesLight.h"
#include "bvh.h"
#include "RayTracerCommon.h"
#include "SceneShadow.h"
#ifdef USE_TEMPORAL
#include "OglShader.h"
#include "OglFrameBuffer.h"
#define WAVELEVELS 6
#else
#include "glgetproc.h"
#endif
#ifndef VRUT_HPC
/*#ifdef __WXMSW__
#define CONTEXTTYPE HGLRC
#else
#define CONTEXTTYPE GLXContext
#endif*/
#include "openVRwrapper.h"
#ifndef NOVR
#include "VRHeroWrapper.h"
#endif
#endif

// supported values are: 3 - RGB and 4 - RGBA (faster on some HW)
#define COLOR_COMPONENTS 4

//supported values are: 1 - rendering will be copied by memcpy to the PBO, 2 - memory will be allocated by OpenGL
#define PBO_COUNT 2

// use visual debugging
#define VISUAL_DEBUGGER 0
#define RAYTRACER_CLUSTER_SYNCHRONIZE

namespace VRUT
{

	enum OpenGLVendor
	{
		NVIDIA,
		AMD
	};

	enum StereoType
	{
		MONO,
		LEFT_TOP,
		LEFT_BOTTOM,
		OPENVR,
		VRHERO,
		STEREO_MAX_STEREO_TYPES //the last item
	};

	class WhittedRenderer;

	///Ray Tracer class - implements ray tracing
	class RayTracer : public RenderModule
	{
	protected:

		//Add \c var param identifier to register this as parameter
		Parameter::ParameterIdentifier maxLightsParamID;

		///Add \c var param identifier to register this as parameter
		Parameter::ParameterIdentifier maxRecursionDepthParamID;

		//Add \c var param identifier to register this as parameter
		Parameter::ParameterIdentifier trianglesPerLeafParamID;

		// An ID of check box used to switch shadowing on/off
		Parameter::ParameterIdentifier castShadowRaysParamID;

		// An ID of shadow offset input parameter
		Parameter::ParameterIdentifier rayOffsetParamID;

		// An ID of check box used to switch using head light on/off
		Parameter::ParameterIdentifier headlightParamID;

		/// A check box enabling special polarization computing
		Parameter::ParameterIdentifier fullPolarizationParamID;

		Parameter::ParameterIdentifier optimizationParamID;
		int optRatio;
		int optMaxIterations;
		int optThreshold;
		int optRandomThreshold;

		/// A combo box for sampling strategy setup
		wxArrayString samplingStrategies;
		Parameter::ParameterIdentifier samplingStrategyParamID;
		int oldSamplingStrategy;

		/// A text box for viewing cone angle thresholds
		wxArrayInt viewConeAngles;
		Parameter::ParameterIdentifier viewConeAnglesParamID;

		/// A slider for samples per pixel
		Parameter::ParameterIdentifier samplesPerPixelParamID;

		///Camera Type strings
		wxArrayString cameraTypes;
		Parameter::ParameterIdentifier cameraTypeParamID;
		int selectedCameraType;

		// An ID of check box used to switch radialDistortion on/off
		Parameter::ParameterIdentifier useRadialDistortionID;
		bool useRadialDistortion;

		// An ID of Horizontal FieldOfView (for pano camera)
		Parameter::ParameterIdentifier hFOVParamID;
		int hFOV;
		Parameter::ParameterIdentifier vFOVParamID;
		int vFOV;

		int drawTimeouts[2];
		Parameter::ParameterIdentifier drawTimeoutsParamID;

		/// Filter radius for direct and indirect illumination
		Parameter::ParameterIdentifier filterRadiusParamID;

		/// Sync rendering parameter ID
		Parameter::ParameterIdentifier syncRenderingParamID;

		Parameter::ParameterIdentifier usePathTracingParamID;
#ifdef USE_TEMPORAL
		Parameter::ParameterIdentifier showAlbedoID;
		Parameter::ParameterIdentifier useReprojectID;
		Parameter::ParameterIdentifier useTemporalPathTracingParamID;
		bool useTemporalPathTracing, showAlbedo, useReproject; int texturesUsed;
		unsigned int w, h;
#endif

		Parameter::ParameterIdentifier toneMappingWhitePointParamID;
		Parameter::ParameterIdentifier toneMappingKeyValueParamID;

		Parameter::ParameterIdentifier renderThreadsParamID;
		Parameter::ParameterIdentifier remoteTilesParamID;

		Parameter::ParameterIdentifier rrStartDepthParamID;
		Parameter::ParameterIdentifier rrSmoothParamID;
		Parameter::ParameterIdentifier fStrengthParamID;
		Parameter::ParameterIdentifier fSteepParamID;
		float smooth,filterStrength,filterSteep;

		wxArrayString stereoTypes;
		Parameter::ParameterIdentifier stereoParamID;
		int selectedStereo;

		bool trackingEnable;
		Parameter::ParameterIdentifier trackingEnableParamID;
		bool createGeometries;
		Parameter::ParameterIdentifier createGeometriesParamID;
		int trackingBodyID;
		Parameter::ParameterIdentifier trackingBodyIDParamID;
		float sceneUnits;
		Parameter::ParameterIdentifier sceneUnitsParamID;

		bool useOpenVrSubsampling;
		bool isOpenVrSubsamplingChanged;
		Parameter::ParameterIdentifier openVrSubsamplingParamID;

#ifndef VRUT_HPC
		OpenVRWrapper *openVRWrapper;
#ifndef NOVR
		VRHeroWrapper *vrHeroWrapper;
#endif
#endif
		void updateRenderTarget(GLsizei w, GLsizei h);
		void submitFrameToOpenVR();

		int toneMappingWhitePoint;
		int toneMappingKeyValue;

		///texture data info
		unsigned int texWidth, texHeight;
		unsigned int texDownscaleFactor; //implicitly 1, set to >1 when rendering to VR headset and useOpenVrSubsampling is true

		size_t texSize;
#ifndef USE_TEMPORAL
		GLfloat * texData;
		GLuint texID;
#else
		GLfloat **texData, **texDataStereo;
		//GLuint *texID;
		OglTexTarget *texCPU;
#endif
		/// parameter of the gl filter
		int filterRadius;

		/// OpenGL image presentation shader handler
		//synchronization parameters
		///this renderer is synchronized, so it should send messages and wait for barrier
		bool synchronized;

		// Forces update in slave mode
		// 0 - off
		// 1 - redraw
		int slaveModeRedraw;

		///shortcut for synchronization
		RENDER_WINDOW_ID windowID;
		//last rendered state
		int lastRenderedState;
		//last release barrier state
		int lastBarrierState;
		// Shadow of the scene to keep global matrix consistent
		SceneShadow *sceneShadow;

		WhittedRenderer * renderer;
		RayTracerCamera * cameras[2]; // Array of cameras, both are used for active stereo
		RayTracerBHV * bvh;

		bool initialized;

		/// saved that scene was changed - need to rebuild/checked up rayTracer structurer
		bool sceneChanged, lightChanged, rerender;
#ifdef USE_TEMPORAL
		bool camMoved, camMovedPrev;
		int camNotMovedCnt;
		Matrix MVP[3], MVPinv[3];
		Vector3 origin[3];
#endif
		int pass;//0-first, 1-refinement

		OpenGLVendor openGLVendor;

		wxULongLong lastRenderingTime;

		// true if running as slave in master-slave mode...
		bool slaveMode;
#ifdef USE_TEMPORAL

#if PBO_COUNT > 0
		GLuint pboIds[NUM_TEMPORAL_TEXTURES][2];
#if PBO_COUNT == 2
		GLfloat **texData1;
		int currentPBO;
#endif
#endif

#else

#if PBO_COUNT > 0
		GLuint pboIds[2];
#if PBO_COUNT == 2
		GLfloat *texData1;
		int currentPBO;
#endif
#endif

#endif
#ifndef VRUT_HPC
		//OpenGL stuff
#ifdef USE_TEMPORAL
		///fullscreen quad and reprojection shaders
		OglShaderProg shProg, shProgQuad, shProgReproject, shProgVarBlur, shProgCopyBuffers;
		OglShaderProg shProgNewProjectDepth, shProgNewProjectUV, shProgNegativeClear;
		OglVertexShader shV, shVQuad;
		OglFragmentShader shF, shFQuad, shFReproject, shFVarBlur, shFCopyBuffers;
		OglComputeShader shCNewProjectDepth, shCNewProjectUV, shCNegativeClear;

		///normal&depth multipass interpolation shaders
		OglShaderProg shProgReduceND, shProgExpandND;
		OglFragmentShader shFReduceND, shFExpandND;

		///reprojection depth multipass minimum shaders
		OglShaderProg shProgReduceDMin, shProgReduceDMax;
		OglFragmentShader shFReduceDMin, shFReduceDMax;

		///wavelet spatial filtering along normals/depth shaders
		OglShaderProg shProgWavelet, shProgTemp;
		OglFragmentShader shFWavelet, shFTemp;

		///textures for light/albedo/normal and depth accumulation
		OglTexTarget texReproj[3], texAlbedo[3], texND[3], texVar[3], tex3D[3];
		/*OglTexTarget texReprojView, texVarView;
		OglTexTarget texReprojM[2], texVarM[2];*/
		OglTexImage reprojDepth[2], reprojUV[2];
		OglFrameBuffer bufferReproj[3];
		int nowTex, frame;

		///normal/depth multipass interpolation textures
		//0: 3x3
		//1: 9x9
		//2: 27x27
		//3: final
		OglTexTarget texNDF[2][4], texAlbedoF[2][4], texDMin[3], texDMax[2];
		OglFrameBuffer bufferND[2][4], bufferDMin[3], bufferDMax[2];

		///wavelet spatial filtering along normals/depth + final reprojection textures
		OglTexTarget texWavelet[WAVELEVELS], texTemp[2], texVarBlur[2], texWaveletMemory;
		OglFrameBuffer bufferWavelet[WAVELEVELS], bufferTemp[2], bufferVarBlur[2], bufferWaveletMemory;
#endif

		ShaderHandler *shader;
		GLuint shaderImageTextureLoc;
		GLuint shaderFilterRadiusLoc;
		GLuint shaderResolutionLoc;
		GLuint shaderAvgLuminanceLoc;
		GLuint shaderFrameIDLoc;
		GLuint shaderKeyValueLoc;
		GLuint shaderWhitePointLoc;
		GLuint shaderDownscaleLoc;

		void     initQuadData();

		bool	 useIndirectDraw;
		bool	 useVertexArray;

		GLuint	 fsquadVBO;
		GLuint	 fsquadVAO;
		GLuint	 fsquadIBO;
#endif

		void getScreenWidthHeightFromHardwareOrWindow(unsigned int& width, unsigned int& height);

	public:

		bool IsSynchronized() const { return synchronized; }
		
		///Class constructor - do not alter
		RayTracer(const MODULE_ID & _id, const wxString & _name, Kernel * _kernel);
		///Class destructor - deinitialize your data
		virtual ~RayTracer();

		virtual bool Initialize();
		///Use this to deinitialize and release render data
		///It is also called by kernel with GL context set
		///so you can use anything like glDelete* etc.
		virtual void Deinitialize();
		virtual void onDone();

		void DrawFullscreenQuad();

#ifdef USE_TEMPORAL
		///OpenGl stuff
		void initGl();
		void renderGl();
		void resize();

		///opengl stuff
		void bindCPUTexture(int tex);
		void subImage2D();
		int getFrame();

		///shader setup
		void prepFilter(int tex);
		void prepQuad(int tex);
		void prepQuad(OglTex *tex);
		void doNewProject(int i, int prev, int now);
		void prepReproject();
		void prepCopyBuffers(int from);
		void prepReduceND(OglTex *from, OglTex *fromAlbedo, int step);
		void prepExpandND(OglTex *prev, OglTex *now, OglTex *prevAlbedo, OglTex *nowAlbedo);
		void prepReduceDMin(OglTex *from, int step);
		void prepReduceDMax(OglTex *from, int step);
		void prepExpandDMin(OglTex *prev, OglTex *now);
		void prepVarBlur(OglTex *src, int level);
		void prepWavelet(OglTex *src, OglTex *srcND, int level, float time);
		void prepTemp();
#endif

		///Overload this to process events you need
		virtual void processEvent(Event * evt);

		///Synchronized events
		virtual ProcessResult processSyncEvent(Event::EVENT_TYPE evtType, void *data);

		virtual void Draw() {}
		void DrawFrame(int renderState);
		virtual void run();

		///Always overload method giving description for your module
		virtual wxString GetDesc() const;

		void DrawPixels(bool reset);
		void IncFramerate();

        NODE_ID GetCameraID() const
        {
            return cameraID;
        }

		/**
		Initializing variables for draw next frame - used when using more threads - called only from main thread
		*/
		void InitializeToDrawNewFrame();

		RayTracerCamera * GetCamera(unsigned int width, unsigned int height, NODE_ID cameraID, bool activeStereoEye=false, int selectedStereo = StereoType::MONO);

#ifndef VRUT_HPC
		/** Compiling shader program - temporary fix - after check it should be put into an updated rendergl class */
		ShaderHandler * CompileShaders(const wxString &vsSourceFileName, const wxString &fsSourceFileName);

		void ShaderInfoMessage(const GLuint handle, const unsigned int type = 0);
#endif
		float GetToneMappingWhitePoint() {return toneMappingWhitePoint;}
		float GetToneMappingKeyValue() {return toneMappingKeyValue;}
	};
}

#endif
