#if !defined(REFLECTIONS_SSR_INCLUDED)
#define REFLECTIONS_SSR_INCLUDED
	#include "common.h"
	// (c) KD, 323
	// Отражения SSR


	bool is_in_range(float val, float min, float max)
	{
		return val >= min && val <= max;
	}

	bool is_in_quad(float2 p, float2 lt = float2(0, 0), float2 rb = float2(1, 1))
	{
		return is_in_range(p.x, lt.x, rb.x) && is_in_range(p.y, lt.y, rb.y);
	}

	#define SSR_STEPS 				50
	#define SSR_STEP_SIZE		 	2.0
	#define SSR_STEP_INCREASE 		1.12
	#define SSR_REFINE_STEPS		5
	// #define SSR_FAR_PLANE			150
	#define SSR_THICKNESS			3

	float get_depth(float2 tc, uint iSample)
	{
		// return gbuffer_load_data(GLD_P(tc, tc * pos_decompression_params2.xy, 0)).P.z;
		#ifdef USE_MSAA
			return s_position.Load(int3(tc * pos_decompression_params2.xy, 0), iSample).z;
		#else
			return s_position.Sample(smp_nofilter, tc).z;
		#endif
	}

	void mirror_tc(inout float3 tc)
	{
		const float smooth	= 0.03f;
		if(tc.x < 0) {
			tc.z = smoothstep(0-smooth,0,tc.x);
			tc.xy = float2(0,tc.y - tc.x * 0.5);		// stretch
//			tc.xy = float2(-tc.x, tc.y - tc.x * 0.5);	// mirror
		}
		if(tc.x > 1) {
			tc.z = smoothstep(1+smooth,1,tc.x);
			tc.xy = float2(1,tc.y + (tc.x - 1) * 0.5);	// stretch
//			tc.xy = float2(1 - (tc.x - 1), tc.y + (tc.x - 1) * 0.5);	// mirror
		}
	}

	bool calc_intersection(int steps, inout float scale, inout float thickness, float increment,
		float3 pos, float3 vreflect, inout float3 pos_step, out float3 tc_step, uint iSample
	)
	{
		[unroll(steps)]for(int i = 0; i < steps; i++)
		{
			pos_step += vreflect * scale;
			tc_step = float3(pos_step.xy / pos_step.z, 1);
			mirror_tc(tc_step);
			if(!is_in_range(tc_step.y, 0, 1))
				return false;
			float hit = get_depth(tc_step.xy, iSample);
			float depth = pos_step.z - hit;
			if(depth > 0 && depth <= thickness && hit >= pos.z)
				return true;
			scale *= increment;
			thickness *= increment;
		}
		return false;
	}

	float4 calc_ssr(float2 tc, float3 P, float3 N, uint iSample = 0)
	{
		float3 pos = float3(tc, 1) * P.z, pos_step = pos;
		float3 tc_step;

		float3 eye = normalize(P);
		float3 vreflect = normalize(reflect(eye, N));
		if(dot(vreflect, eye) < 0) return 0;
		vreflect.xy = (vreflect.xy + pos_decompression_params.xy * vreflect.z) / (pos_decompression_params.zw * pos_decompression_params2.xy);

		float depth_scale = clamp(sqrt(P.z), 0.5, 5.0);
		float scale = SSR_STEP_SIZE * depth_scale / SSR_STEPS;
		float thickness = scale * SSR_THICKNESS;

		// Ищем пересечение грубым шагом
		bool intersected = calc_intersection(SSR_STEPS, scale, thickness, SSR_STEP_INCREASE, pos, vreflect, pos_step, tc_step, iSample);

#ifdef SSR_REFINE_STEPS
		if(intersected) // Если нашли, уточним его более мелким шагом
		{
			pos_step -= vreflect * scale;
			scale /= SSR_REFINE_STEPS;
			intersected = calc_intersection(SSR_REFINE_STEPS, scale, thickness, 1.0, pos, vreflect, pos_step, tc_step, iSample);
		}
#endif
#ifdef SSR_FAR_PLANE
		// if(!intersected) // Проецируем задний план
		// {
			// scale = SSR_FAR_PLANE;
			// thickness = scale;
			// intersected = calc_intersection(1, scale, thickness, 1.0, pos, vreflect, pos_step, tc_step, iSample);
		// }
#endif

		if(!intersected)
			return 0;
		if(!is_in_quad(tc_step.xy))
			return 0;

		float factor = 1 - smoothstep(0.1, 0.0, tc_step.y);
		factor		*= tc_step.z;
		float3 ssr = s_image.Sample(smp_rtlinear, tc_step);

		return float4(ssr, factor);
	}
#endif // REFLECTIONS_SSR_INCLUDED