#include "common/samplers11.hlsl"
#include "common/states11.hlsl"
#include "common/context.hlsl"
#include "deferred/blur.hlsl"
#include "noise/noise3D.hlsl"
#include "NVD_common.hlsl" 
//#include "common/samplers_NVG.hlsl"


/////////
#include"../common/MOD_nvg.hlsl"
/////////

Texture2D DiffuseMap;
Texture2D NVDMap;
Texture2D DepthMap;

uint2	dims;		// used in creating of mask...... might be (x,y) dimension of screen in pixels.
float4	viewport;	// view port pos??
float3 	color;		// pixel colour (rgb)??
float 	gain;			// gain value - from key assignment in game, roughly values betweeen 0 and 1 
float 	noiseFactor;	//Added in DCS 2.8 - used in BlurY function, fix found by MTrenda
float 	time;
float 	blurM;


struct VS_OUTPUT {
	float4 pos:		SV_POSITION;
	float4 projPos:	TEXCOORD0;
};

static const float2 quad[4] = {
	{-1, -1}, {1, -1},
	{-1,  1}, {1,  1}
};

VS_OUTPUT VS(uint vid: SV_VertexID) {
	VS_OUTPUT o;
	o.projPos = o.pos = float4(quad[vid], 0, 1);
	return o;
}

/* //from DCS 2.2, moved to NVD_common.hlsl
float getMask(float2 c, float mul) {
	return saturate(mul*(1 - sqrt(dot(c, c))));
}
*/

float hash31(float3 p3) {
	p3 = frac(p3 * float3(.1031,.11369,.13787));
	p3 += dot(p3, p3.yzx + 19.19);
	return frac((p3.x + p3.y) * p3.z);
}

float noise1(float2 p, float seed) {
	float2 i = floor(p);
	float2 f = frac(p);

	float2 u = f*f*(3.0 - 2.0*f);

	return lerp(
			lerp(hash31(float3(i + float2(0.0, 0.0), seed)),
				hash31(float3(i + float2(1.0, 0.0), seed)), u.x),
			lerp(hash31(float3(i + float2(0.0, 1.0), seed)),
				hash31(float3(i + float2(1.0, 1.0), seed)), u.x),
			u.y);
}

float noise2(float2 p, float seed) {
	float n = noise1(p, seed);
	return (n*0.1 + pow(n, 50)*0.4);
}

float noise3(float2 p, float time) {
	time += sin(p.x*20) + cos(p.y*20);
	float i = floor(time);
	float f = frac(time);
	return lerp(noise2(p, i), noise2(p, i + 1), f);
}


float3 fxNVG(const float2 uv, const int nvg, const float3 rgb): SV_TARGET0
{
  //const float4 blur = ResampleMap.Sample(sResampleMap, uv);
  const float3 result = float3(0.0f, 0.0f, 0.0f);						//sets pixel to black before calculations are made.
  float3 NoiseInput = float3(uv*200, gModelTime*2);						//uv coord and time number to be fed into noise generator (perlin, simplex etc)
  
  if (nvg == NVG_A10)
   {
    //***********
    // The A-10C effect here.
    //***********
	
	float N = clamp(snoise(NoiseInput), 0.0f, 1.0f);							//alt noise pattern generator - clamp is an inclusive domain. snoise (part of default noise3D.hlsl), for snoise *100 gives large, *300 gives medium *600 gives small. 
	//float N = noise3((uv*0.5+0.5) * 400, gModelTime * 10);					//default noise pattern generator
	N = NVG_NOISE[NVG_A10]*(gain) * pow(N,(5/gain));							//magn is nvg intenisty from in-game control (ranges between 0 and 200)  N = (magn*.005) * pow(N,(2500/magn));
	float L = dot(rgb, LUM);													//dot product of the pixels rgb value with LUM constant (ie NVG pickup ability). LUM skews the pickup to mainly red colors (like real device)
	float L2 = 1 - exp(-(gain*NVG_LIFT[NVG_A10]) * L);							// NVG_LIFT (approx 0.06) corrects multiplication factor of 'magn' value.
	float L3 = clamp(contrast * (L2 - 0.5) + 0.5, 0.0, 1.0);					// applies contrast (user set value) to image, 0.5's = even clipping, 0.7s=darks being clipped more
	//L3 = L3 + N + (blur.r * (0.10 * blurM));									// N=noise blur.r = blur for high intensity, blurM = multiple lights close together overpowering NVG.
	L3 += N;
	float3 result = HSLtoRGB(float3(COLORIZE_COLOR[NVG_A10], 0.6f, L3));		// Col_Color = hue (120 degrees), 0.6f = saturation, L3 = amplified/noise impacted intensity of pixel
	//return N;																	//shows just the noise pixels
	return result;
   }
   	else
  {
    //***********
    // The KA-50 effect here.
    //***********

    float L = dot(rgb, LUM);												//dot product of the pixels rgb value with LUM constant (ie NVG pickup ability). LUM skews the pickup to mainly red colors (like real device)
	float L2 = 1.1 - (1.03 * exp(-(gain*NVG_LIFT[NVG_KA50]) * L));			// NVG_LIFT (approx 0.06) corrects multiplication factor of 'magn' value. magn is nvg intenisty from ingame control (ranges between 0 and 200)
	float L3 = clamp(contrast * (L2 - 0.7) + 0.7, 0.0, 1.0);								// applies contrast (user set value) to image, is weighted to darks. Change both 0.7 to 0.5 for even weighting
	//L3 = L3 + (blur.r * (0.10 * blurM));									// applies bloom blur effect since all NVG's have lenses. blur.r is general blur for high intensity, blurM is for multiple lights close together overpowering NVG.
	float3 result = HSLtoRGB(float3(COLORIZE_COLOR[NVG_KA50], 0.8f, L3));	// uses new intenisty and HSL to RGB conversion to add green tinge.
	return result;
  }
}

#define MASK_SIZE (1.0/0.8)  										//an overall bigger number means smaller mask size

float4 PS(const VS_OUTPUT i, uniform bool useMask): SV_TARGET0 {	//if useMask set to false, will still create effect but no mask. If set to true will use effect and mask. 

	uint2 idx = i.pos.xy;
	float2 uv = float2(i.projPos.x*0.5+0.5, -i.projPos.y*0.5+0.5)*viewport.zw + viewport.xy; 	//reads pixel coordinate
	
	//float3 c1 = NVDMap.SampleLevel(ClampPointSampler, uv, 0).rgb;		//Default green NVG effect 	
	float3 c0 = DiffuseMap.SampleLevel(ClampPointSampler, uv, 0).rgb;	//reads standard rgb pixel values
	float3 c1= fxNVG(uv,0,c0);											//sends default rgb pixel values and coords to have green and ring effect applied
	
	if (useMask) {
		
		if (RenderMode == 2){
			const float3 mask = maskNVG(uv);
			float3 c2 = lerp(c1,(0.0f ,0.0f , 0.0f), mask.y);  	//blends black values (ring section all the way out to edge of screen) into NVG pixels
			return float4(lerp(c0, c2, mask.z), 1);				//blends above with stock rgb pixels
			//return float4(c2, 1);

		}	else if (RenderMode == 1){
			const float3 mask = maskNVG(uv);
			return float4(lerp(c0, c1, mask.z), 1);
			//return float4(c1, 1);

		}	else if (RenderMode == 0){
			return float4(c1, 1);
		}
		//DCS 2.8 shape code
		/*
		float3 c0 = DiffuseMap.SampleLevel(ClampPointSampler, uv, 0).rgb;		//reads standard rgb pixel
		float2 uvm = calcMaskCoord(i.projPos);
		float m0 = 1 - getMask(uvm*0.6, 3);
		float m1 = getMask(uvm, 10);
		return float4(m0*c0 + m1*c1, 1);
		*/
	} else {
		return float4(c1, 1);
	}
}

#define FOCUS_DISTANCE 10.0

float3 BlurOffs(const VS_OUTPUT i, float2 offs, out float depth) {			//called in PS_BlurX
	float2 uv = float2(i.projPos.x*0.5 + 0.5, -i.projPos.y*0.5 + 0.5);
	depth = DepthMap.SampleLevel(gBilinearClampSampler, uv, 0).r;
	float4 pos = mul(float4(i.projPos.xy, depth, 1), gProjInv);
	float sigma = 0.8 + 2 * saturate((FOCUS_DISTANCE - (pos.z / pos.w)) / FOCUS_DISTANCE);
	return Blur(uv, offs*(0.5 / dims), sigma, NVDMap);						// final Blur function is located in shaders/deferred/blur.hlsl // return Blur(uv, offs*(0.5 / dims), sigma, NVDMap);
}

float4 PS_BlurX(const VS_OUTPUT i): SV_TARGET0 {
	float depth;
	return float4(BlurOffs(i, float2(1, 0), depth), 1);
}


float4 PS_BlurY(const VS_OUTPUT i) : SV_TARGET0 {
	float depth;
	float3 result = BlurOffs(i, float2(0, 1), depth);							//blur for x and y?
	//result = dot(result, LUM) * 2 * color;									//used in dcs 2.8  GIVES TERRIBLE GREEN HIGHLIGHTS
	result += depth == 0 ? color * pow(gain*1.5, 4) : 0;						// more bright sky, commented out from DCS 2.? - GIVES WHITE HIGHLIGHTS INSTEAD OF GREEN
	result += color * noise3((i.projPos.xy*0.5+0.5) * 400, gModelTime * 10) * noiseFactor;	//bringing in noise function? Noise3 uses noise2 which uses noise1
	return float4(result, 1);
}

#define COMMON_PART 		SetVertexShader(CompileShader(vs_5_0, VS()));									\
							SetGeometryShader(NULL);														\
							SetDepthStencilState(disableDepthBuffer, 0);									\
							SetBlendState(disableAlphaBlend, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);	\
							SetRasterizerState(cullNone);

technique10 Compose {
	pass P0 {
		SetPixelShader(CompileShader(ps_5_0, PS(false)));		//False - defines to not use mask (will still use effect across whole screen)
		COMMON_PART
	}
	pass P1 {
		SetPixelShader(CompileShader(ps_5_0, PS(true)));		//True - defines use mask and effect
		COMMON_PART
	}
}

technique10 BlurX {
	pass P0 {
		SetPixelShader(CompileShader(ps_5_0, PS_BlurX()));		//creates a horizontal blur on objects close to goggles (no noise)
		COMMON_PART
	}
}

technique10 BlurY {
	pass P0 {
		SetPixelShader(CompileShader(ps_5_0, PS_BlurY()));		//creates a vertical blur on objects close to goggles and adds noise
		COMMON_PART
	}
}
