/*********************************************************************

Name:		Raytracer
Author:		Kamil Szatkowski "Netrix" (C)
Website:	http://www.netrix.org.pl/

*********************************************************************/
#pragma once
#include "NLib/nMath.hpp"
#include "Bitmap.hpp"
#include "Scene.hpp"
#include "Camera.hpp"
#include "Attributes.hpp"
#include "Lights.hpp"
#include "SceneDesc.hpp"

//#define USD
#define KD_TREE
#define KD_TREE_OCCLUDER

#define PRINT_PROGRESS

#ifdef KD_TREE_OCCLUDER
#define KD_TREE
#endif

#ifdef KD_TREE
	#include "KDTree.hpp"
	#ifndef KD_TREE_OCCLUDER
		#define USD
	#endif
#endif
#ifdef USD
	#include "UniformSpaceSubdivision.hpp"
#endif

struct RaytracerParameters
{
	char			szDescFile[256];		// 256
	char			szOutFile[256];			// 256
	NIne::NUint32	uPackSize;				// 4
	NIne::NUint32	uTextureAttribute;		// 4
	float			fAngleCoeff;			// 4
	float			fBrickWidth;			// 4
	float			fBrickHeight;			// 4
	float			fBrickMortar;			// 4
	bool			bDirt;					// 1
	bool			bCustomCamera;			// 1
	bool			bCustomScreenSize;		// 1
	bool			bResetCache;			// 1
	// Camera
	NIne::NMVector3f v3CamPos;				// 12
	NIne::NMVector3f v3CamLook;				// 12
	// Screen size
	NIne::NUint32	uWidth, uHeight;		// 8
};		// 568

class RayTracer
{
	/*
	 *	Na początek wywoływać rekursywnie na stosie i przy powrocie przeprowadzać obliczenia.
	 *	Teoretycznie dla kilku wątków można niezależnie wywołać funkcje raytrace z punktem ekranu
	 */
	typedef NIne::NDataVector<NIne::NUint32> NUint32Vector;
	typedef NIne::NDynamicTable<NIne::NUint32> NUint32Array;

public:
	RayTracer();
	~RayTracer();

	bool load(const char* szSceneDescFile = null, bool bCameraOnly = false);
	void release();

	void preprocessScene();

	void setCamera(const NIne::NMVector3f v3Pos, const NIne::NMVector3f v3Dir)	
	{
		parameters.v3CamPos = v3Pos;
		parameters.v3CamLook = v3Dir;
		parameters.bCustomCamera = true;
	}

	void setOutputResolution(NIne::NUint32 uWidth, NIne::NUint32 uHeight) 
	{
		parameters.uWidth = uWidth; 
		parameters.uHeight = uHeight; 
		parameters.bCustomScreenSize = true; 
	}

	const Camera& getCamera() const { return m_camera; }

	void run();
	void calculatePack(NIne::NUint32 x, NIne::NUint32 y, NIne::NUint8* opDest);

private:
	const FaceData* dummyFindTriangle(const NIne::NMVector3f& rayPos, const NIne::NMVector3f& rayDir, RayTestOutput* opRTO);
	bool dummyFindObstacle(const NIne::NMVector3f& rayPos, const NIne::NMVector3f& rayDir, const FaceData* pSrcFace, float fDirLength);

	NIne::NUint32 calculateColor(NIne::NUint32 uFace, const NIne::NMVector3f& vIntPos, const NIne::NMVector3f& vRayDir, bool bFront);

	NIne::NMVector3f traceRay(const NIne::NMVector3f& rayPos, const NIne::NMVector3f& rayDir, NIne::NUint32 uDepthLevel, float fEta);

	NIne::NMVector3f procedeTexture(const FaceData* pFace, const RayTestOutput& rto, const NIne::NMVector3f vDir, NIne::NMVector3f* pNormal, AttributeInstance* pAtt);

private:
	NBitmap m_bitmap;
	SceneDesc m_sceneDesc;
	Scene m_scene;
	Camera m_camera;
	Attributes m_attributes;
	Lights m_lights;

#ifdef USD
	UniformSpaceSubdivision m_uss;
#endif
#ifdef KD_TREE
	KDTree m_kdtree;
#endif
	float	m_fMaxDirLength;
	bool	m_bPreprocessDone;
	bool	m_bCameraOnly;

	NIne::NMVector3f	m_v3ViewPoint;
	NIne::NMVector3f	m_v3ScreenStart;
	NIne::NMVector3f	m_v3ScreenRight;
	NIne::NMVector3f	m_v3ScreenDown;

public:
	RaytracerParameters parameters;

	//NIne::NUint32 m_uTexturedAttribute;
	//float m_fBrickWidth;
	//float m_fBrickHeight;
	//float m_fBrickMortar;

	//float m_fAngleCoeff;
	//bool m_bDirt;

	//bool m_bCustomOut;
	//NIne::NUint32 m_uOutWidth;
	//NIne::NUint32 m_uOutHeight;
};