//--// Settings //------------------------------------------------------------//

#include "/settings.glsl"

//#define DEV_VIS

#define VOXELMAP 0
#define RAY_READS 1
#define RAY_READS_ALT 2
#define RAY_LENGTH 3
#define HIT_TINT 4
#define DEV_VIS_MODE RAY_LENGTH // [VOXELMAP RAY_READS RAY_READS_ALT RAY_LENGTH HIT_TINT]

//--// Uniforms //------------------------------------------------------------//

uniform sampler2D colortex1;
uniform sampler2D shadowtex1; // LUT

uniform sampler2D shadowcolor0;
uniform sampler2D shadowcolor1;

#define atlas   colortex4 // Atlas, base color
#define atlas_n colortex5 // Atlas, _n
#define atlas_s colortex6 // Atlas, _s
uniform sampler2D atlas;
uniform sampler2D atlas_n;
uniform sampler2D atlas_s;

uniform sampler2D noisetex;

//--// Time uniforms

uniform float frameTimeCounter;

//--// Camera uniforms

uniform vec3 cameraPosition;

uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferProjectionInverse;

//--// Custom uniforms

uniform vec2 viewPixelSize;

uniform vec3 shadowLightVector;

#if defined STAGE_VERTEX
	//--// Vertex Functions //------------------------------------------------//

	void main() {
		gl_Position = vec4(gl_Vertex.xy * 2.0 - 1.0, 1.0, 1.0);
	}
#elif defined STAGE_FRAGMENT
	//--// Fragment Outputs //------------------------------------------------//

	out vec3 fragColor;

	//--// Fragment Includes //-----------------------------------------------//

	#include "/include/utility.glsl"
	#include "/include/utility/color.glsl"
	#include "/include/utility/rotation.glsl"
	#include "/include/utility/sampling.glsl"
	#include "/include/utility/sequence.glsl"
	#include "/include/utility/voxelization.glsl"

	#ifdef DEV_VIS
	#include "/include/voxelRaytracer.glsl"
	#endif

	//--// Fragment Functions //----------------------------------------------//

	#ifdef LUT
	vec3 LookupColor(sampler2D lookupTable, vec3 color) {
		const ivec2 lutTile = ivec2(8, 8); // 8x8=64 8x16=128 16x8=128 16x16=256
		const int   lutSize = lutTile.x * lutTile.y;

		color.b *= lutSize - 1;
		int i0 = int(color.b);
		int i1 = i0 + 1;

		vec2 c0 = vec2(i0 % lutTile.x, i0 / lutTile.x);
		vec2 c1 = vec2(i1 % lutTile.x, i1 / lutTile.x);

		vec2 c = color.rg * ((lutSize - 1.0) / (lutSize * lutTile)) + (0.5 / (lutSize * lutTile));

		return mix(
			texture(lookupTable, c0 / lutTile + c).rgb,
			texture(lookupTable, c1 / lutTile + c).rgb,
			color.b - i0
		);
	}
	#endif

	#ifdef DEV_VIS
	vec3 DevVis(vec2 uv) {
		vec3 color = vec3(0.0);

		#if   DEV_VIS_MODE == VOXELMAP
			color = texture(shadowcolor0, uv).rgb;
		#elif DEV_VIS_MODE == RAY_READS || DEV_VIS_MODE == RAY_READS_ALT || DEV_VIS_MODE == RAY_LENGTH || DEV_VIS_MODE == HIT_TINT
			vec3 pos = VoxelSpaceFromSceneSpace(gbufferModelViewInverse[3].xyz);
			ivec3 idx = ivec3(floor(pos));
			vec3 dir = vec3(uv * 2.0 - 1.0, -1.0);
			dir.xy *= vec2(gbufferProjectionInverse[0].x, gbufferProjectionInverse[1].y);
			dir = mat3(gbufferModelViewInverse) * dir;

			#if   DEV_VIS_MODE == RAY_READS || DEV_VIS_MODE == RAY_READS_ALT
				bool hit = RaytraceVoxels(pos, idx, dir);

				#if   DEV_VIS_MODE == RAY_READS
					uint totalReads = 0u;
					for (int i = 0; i < OCTREE_LEVELS; ++i) { totalReads += reads[i]; }
					color = vec3(totalReads) * 2.0 / 255.0;
				#elif DEV_VIS_MODE == RAY_READS_ALT
					color = vec3(reads[0], reads[1], reads[2]) * 5.0 / 255.0;
				#endif
			#elif DEV_VIS_MODE == RAY_LENGTH || DEV_VIS_MODE == HIT_TINT
				vec3 hitPos; ivec3 hitIdx; vec3 hitNormal; vec2 voxelmapCoord;
				RaytraceVoxels(pos, idx, dir, hitPos, hitIdx, voxelmapCoord, hitNormal);

				#if   DEV_VIS_MODE == RAY_LENGTH
					float rayLength = distance(hitPos, pos);
					color = vec3(LinearToSrgb(1.0 - exp(-0.01 * rayLength)));
				#elif DEV_VIS_MODE == HIT_TINT
					vec4[2] voxel = vec4[2](
						texture(shadowcolor0, voxelmapCoord),
						texture(shadowcolor1, voxelmapCoord)
					);

					color = voxel[0].rgb;
				#endif
			#endif
		#endif

		return color;
	}
	#endif

	void main() {
		vec2 uv = MC_RENDER_QUALITY * viewPixelSize * gl_FragCoord.xy;

		fragColor = texture(colortex1, uv).rgb;

		fragColor = LinearToSrgb(fragColor);

		#ifdef LUT
		// Apply LUT
		fragColor = LookupColor(shadowtex1, fragColor);
		#endif

		#ifdef DEV_VIS
		fragColor = DevVis(uv);
		#endif

		fragColor += R2Dither(gl_FragCoord.xy) / 255.0;
	}
#endif
