#include "stereokit.hlsli"

// Port of https://github.com/SimulaVR/Simula/blob/master/addons/godot-haskell-plugin/TextShader.tres to StereoKit and HLSL.

//--name = stardust/text_shader
//--diffuse     = white
//--uv_offset   = 0.0, 0.0
//--uv_scale    = 1.0, 1.0
//--fcFactor    = 1.0
//--ripple      = 4.0
//--alpha_min   = 0.0
//--alpha_max   = 1.0
Texture2D    diffuse   : register(t0);
SamplerState diffuse_s : register(s0);
float4       diffuse_i;
float2       uv_scale;
float2       uv_offset;
float        fcFactor;
float        ripple;
float        alpha_min;
float        alpha_max;

struct vsIn {
	float4 pos  : SV_Position;
	float3 norm : NORMAL0;
	float2 uv   : TEXCOORD0;
};
struct psIn {
	float4 pos   : SV_POSITION;
	float2 uv    : TEXCOORD0;
	uint view_id : SV_RenderTargetArrayIndex;
};

psIn vs(vsIn input, uint id : SV_InstanceID) {
	psIn o;
	o.view_id = id % sk_view_count;
	id        = id / sk_view_count;

	float3 world = mul(float4(input.pos.xyz, 1), sk_inst[id].world).xyz;
	o.pos        = mul(float4(world,         1), sk_viewproj[o.view_id]);

	o.uv    = (input.uv + uv_offset) * uv_scale;
	return o;
}

float map(float value, float min1, float max1, float min2, float max2) {
  return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}

// float gaussian(float x, float t) {
// 	float PI = 3.14159265358;
// 	return exp(-x*x/(2.0 * t*t))/(sqrt(2.0*PI)*t);
// }

float besselI0(float x) {
	return 1.0 + pow(x, 2.0) * (0.25 +  pow(x, 2.0) * (0.015625 +  pow(x, 2.0) * (0.000434028 +  pow(x, 2.0) * (6.78168e-6 +  pow(x, 2.0) * (6.78168e-8 +  pow(x, 2.0) * (4.7095e-10 +  pow(x, 2.0) * (2.40281e-12 + pow(x, 2.0) * (9.38597e-15 + pow(x, 2.0) * (2.8969e-17 + 7.24226e-20 * pow(x, 2.0))))))))));
}

float kaiser(float x, float alpha) {
	if (x > 1.0) { 
		return 0.0;
	}
	return besselI0(alpha * sqrt(1.0-x*x));
}

float4 lowpassFilter(Texture2D tex, sampler2D texSampler, float2 uv, float alpha) {
	float PI = 3.14159265358;
	
	float4 q = float4(0.0);
	
	float2 dx_uv = ddx(uv);
	float2 dy_uv = ddy(uv);
	//float width = sqrt(max(dot(dx_uv, dx_uv), dot(dy_uv, dy_uv)));
	float2 width = abs(float2(dx_uv.x, dy_uv.y));
	
	float2 pixelWidth = floor(width * diffuse_i.xy);
	float2 aspectRatio = normalize(pixelWidth);
	
	float2 xyf = uv * diffuse_i.xy;
	int2 xy = int2(xyf);
	
	pixelWidth = clamp(pixelWidth, float2(1.0), float2(2.0));

	int2 start = xy - int2(pixelWidth);
	int2 end = xy + int2(pixelWidth);
	
	float4 outColor = float4(0.0);
	
	float qSum = 0.0;
	
	for (int v = start.y; v <= end.y; v++) {
		for (int u = start.x; u <= end.x; u++) {
			float kx = fcFactor * (xyf.x - float(u))/pixelWidth.x;
			float ky = fcFactor * (xyf.y - float(v))/pixelWidth.y;
			 
			//float lanczosValue = gaussian(kx, fcx);
			float lanczosValue = kaiser(sqrt(kx*kx + ky*ky), alpha);
			
			q += tex.Sample(texSampler, (float2(u, v)+float2(0.5))/diffuse_i.xy) * lanczosValue;
			// q += tex.Load(int3(u, v, 0)) * lanczosValue;
			qSum += lanczosValue;
		}
	}
	
	return q/qSum;
}

float4 ps(psIn input) : SV_TARGET {
	float gamma = 2.2;
	// float4 col = diffuse.Sample(diffuse_s, input.uv);

	// float4 col = lowpassFilter(diffuse, diffuse_s, diffuse_i.xy, float2(1.0 - input.uv.x, input.uv.y), ripple);
	float4 col = lowpassFilter(diffuse, diffuse_s, input.uv, ripple);
	// float4 col = diffuse.Sample(diffuse_s, input.uv);
	col.rgb = pow(col.rgb, float3(gamma));
	col.a = map(col.a, 0, 1, alpha_min, alpha_max);

	return col; 
}
