//================================================================
// E3D
//
// Rendering-related header file
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Feb-18 01:15:35
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#ifndef _E3D_PathTracer_h
#define _E3D_PathTracer_h

#include <E3D/Raytracing.h>

#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif


#define E3dPathTracer_core\
	E3dRayTracerCore;\
\
/* Resources  */\
\
	int32_t           NumOfPathSamples;     /* If > 0, use path-tracing with this many rays per pixel  */\
	int32_t           MaxIndirectRayDepth;  /* This is separate from the classical ray-tracer's MaxRayDepth. For example lots of glass may require a large MaxRayDepth, but we don't want a lot of path-tracer bounces.  */\
	EBool             DirectIllumination;\
\
	EIndex            NPathTracerLightsPerList, PathTracerLightLists_Count


typedef struct
{
	E3dPathTracer_core;
} E3dPathTracer;



#define E3dPathTracer_thread_core\
	E3dRayTracer_thread_core;\
\
/* Pre-allocated lists for ShaderLights, used in the path-tracer	*/\
/* This helps to avoid billions of malloc/frees while computing indirect illumination.	*/\
\
	EList_s(, E3dShaderLightPtr, PathTracerLightLists);\
	EIndex            NPathTracerLightsPerList;\
	E3dIRay*          PathRays

typedef struct
{
	E3dPathTracer_thread_core;
} E3dPathTracer_thread;


//================================================
// Rendering thread constructor / resetter
//================================================
static inline void E3dPathTracer_ThreadInit(const E3dRenderer* PRenderer, E3dRenderer_thread* PThread, const EBool PNew)
{
	const E3dPathTracer*   LRenderer = (E3dPathTracer*)PRenderer;
	E3dPathTracer_thread*  LThread = (E3dPathTracer_thread*)PThread;

//const EBool  LVerbose = LThread->Number == 0;
const EBool  LVerbose = FALSE;

	const EIndex  LNPathTracerLightsPerList = LRenderer->NPathTracerLightsPerList, LPathTracerLightLists_Count = LRenderer->PathTracerLightLists_Count;
	if(PNew || LThread->NPathTracerLightsPerList != LNPathTracerLightsPerList || LThread->PathTracerLightLists.Count != LPathTracerLightLists_Count)
	{
if(LVerbose) { printf("%s() %s:%d  Thread %03d (%p)  PathTracerLightLists_Count %lld  LNPathTracerLightsPerList %lld  %lld\n", __FUNCTION__, __FILE__, __LINE__, LThread->Number, LThread, (long long)LPathTracerLightLists_Count, (long long)LNPathTracerLightsPerList, (long long)(sizeof(E3dShaderLight) * LPathTracerLightLists_Count * LNPathTracerLightsPerList));fflush(stdout); }
		E3dShaderLight**  LLightLists;ENewN(LPathTracerLightLists_Count, LLightLists);
		LThread->PathTracerLightLists.Items = LLightLists;
		EIndex  LI = 0;const EIndex  LN = LPathTracerLightLists_Count;for(;LI < LN;LI++)
		{
			ENewN(LNPathTracerLightsPerList, LLightLists[LI]);
//			LThread->PathTracerLightLists[LI] = NULL;
if(LVerbose) { printf(" %p", LLightLists[LI]); }
		}
if(LVerbose) { printf("\n");fflush(stdout); }

		LThread->PathTracerLightLists.Count = LPathTracerLightLists_Count;
		LThread->NPathTracerLightsPerList = LNPathTracerLightsPerList;
	}

	if(PNew) ENewN((LRenderer->MaxIndirectRayDepth + 1) * LRenderer->NumOfSpectralSamples, LThread->PathRays);
	else if(LThread->PathRays == NULL) ENewN((LRenderer->MaxIndirectRayDepth + 1) * LRenderer->NumOfSpectralSamples, LThread->PathRays);
}


//================================================
// Rendering thread destructor
//================================================
static inline void E3dPathTracer_ThreadFree(E3dRenderer_thread* PThread, const E3dChanges* PChanges)
{
	E3dPathTracer_thread*  LThread = (E3dPathTracer_thread*)PThread;
//E3dRenderer*           LWRenderer = LThread->Renderer;
	const EChangeFlags     LRendererChangeFlags = PChanges ? PChanges->RendererChangeFlags : E3dRendererChgALL;

//const EBool  LVerbose = LThread->Number == 0;
const EBool  LVerbose = FALSE;
//const EBool  LVerbose = TRUE;

ETime  LPrev_us = E_GetMicroseconds();

// PathTracer
//
//E3dTHREAD_LOCK(Debug);
	if(PChanges == NULL)
	{
		if(LThread->PathTracerLightLists.Items)
		{
if(LVerbose) { printf("%s() %s:%d  Thread %03d (%p)  LThread->PathTracerLightLists.Count %lld\n", __FUNCTION__, __FILE__, __LINE__, LThread->Number, LThread, (long long)LThread->PathTracerLightLists.Count);fflush(stdout); }
			E3dShaderLight**  LLightLists = LThread->PathTracerLightLists.Items;
			EIndex  LI = 0;const EIndex  LN = LThread->PathTracerLightLists.Count;for(;LI < LN;LI++)
			{
if(LVerbose) { printf(" %p", LLightLists[LI]);fflush(stdout); }
				EFreeChk(LLightLists[LI]);
			}
if(LVerbose) { printf("\n");fflush(stdout); }

if(LVerbose) { const ETime  LNow_us = E_GetMicroseconds();char  s[256];EStr_MicrosecondsToString(LNow_us - LPrev_us, s, FALSE);LPrev_us = LNow_us;printf("%s() %s:%d  %sThread % 3lld%s %s\n", __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)LThread->Number, ECodeNormal, s);fflush(stdout); }
			EList_Free_s(LThread->PathTracerLightLists);

			LThread->NPathTracerLightsPerList = 0;
		}
	}

if(LVerbose) { const ETime  LNow_us = E_GetMicroseconds();char  s[256];EStr_MicrosecondsToString(LNow_us - LPrev_us, s, FALSE);LPrev_us = LNow_us;printf("%s() %s:%d  %sThread % 3lld%s %s\n", __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)LThread->Number, ECodeNormal, s);fflush(stdout); }

	if(LRendererChangeFlags & E3dRendererChgRAY_SETTINGS) EFreeChkClr(LThread->PathRays);
//E3dTHREAD_UNLOCK(Debug);
}


extern void	E3dPathTracer_InitForRender(const E3dScene* PScene, E3dPathTracer* PRenderer, const int PNumOfExtraLightSamples);


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif


#endif  // _E3D_PathTracer_h
