

layout(location = 0, index = 0) out vec4 outcol;

uniform sampler2D tex0;
uniform sampler2D depthtex;
//uniform vec4 dof_distances;
uniform vec4 tex0siz; //xy=size in pixels, zw = 1/xy
uniform vec4 parms;

in vec2 texcoord0_nm;

//TODO: input from app, has to match
const float znear = 1.0f;
const float zfar = 1000.0f;

const float max_blursize_px = 5.0f; //TODO: size not in px
//float dof_near_blur;
//float dof_near_focus;
//const float far_focus = 100.0f;
//const float far_blur = 160.0f;
float far_focus;
float far_blur;

//TODO: input from app
float proj_z( float z_e ) {
	float A = -(zfar + znear) / (zfar - znear);
	float B = -2*zfar*znear / (zfar - znear);
	float z_ndc = -(A*z_e + B) / z_e; // [-1; 1]
	return 0.5*z_ndc + 0.5; //[0;1]
}

float rand( vec2 co ) {
	return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

vec2 rot2d( vec2 p, float a )
{
	vec2 sc = vec2(sin(a),cos(a));
	vec2 ret;
	ret.x = dot( p, sc.yx*vec2(1,-1) );
	ret.y = dot( p, sc.xy );
	return ret;
}

vec4 rot2d( vec4 p0p1, float a )
{
	vec4 p01 = p0p1.xzyw;

	vec2 sc = vec2(sin(a),cos(a));
	vec4 ret;
	vec4 t0 = p01 * vec2( sc.y, -sc.x ).xxyy;
	vec4 t1 = p01 * vec2( sc.x, sc.y ).xyxy;

	return ret;
}

float vs_depth_from_sample( float depth_sample ) {
	float z_ndc = 2.0f * depth_sample - 1.0f;
    float z_vs = 2.0f * znear * zfar / (zfar + znear - z_ndc * (zfar - znear));
	return z_vs;
}

float coc_from_depth( float depth_sample ) {
	float z_vs = vs_depth_from_sample( depth_sample );
	float coc = (z_vs - far_focus) / (far_blur - far_focus);
	return clamp( coc, 0.0f, 1.0f );
}

vec4 sample4( vec2 uv_nm, vec4 ofs01_nm, vec4 ofs23_nm, float coc ) {
	//TODO: only where needed (stencil prepass)
	//TODO: half-res depth
	//TODO: gather
	vec4 ds = vec4( texture( depthtex, uv_nm + ofs01_nm.xy ).r,
					texture( depthtex, uv_nm + ofs01_nm.zw ).r,
					texture( depthtex, uv_nm + ofs23_nm.xy ).r,
					texture( depthtex, uv_nm + ofs23_nm.zw ).r );

	//TODO: optimize by doing this check on minmax-image...
	const float transition = 100.0f; //wtf?
	float proj_focus = proj_z( -far_focus );
	float proj_trans = proj_z( -(far_focus+transition) );
	vec4 t = (ds - vec4(proj_focus)) / vec4(proj_trans-proj_focus);
	t = clamp( t, vec4(0.0f), vec4(1.0f)); //note: don't sample in-focus pixels

	vec4 w = vec4(t) + vec4( step(coc, 0.01f) ); //note: ...unless ctr-pixel in focus, TODO: fade in
	float num = w.x + w.y + w.z + w.w;
	w = w / num;

	vec4 smp1 = w.x * texture( tex0, uv_nm + ofs01_nm.xy );
	vec4 smp2 = w.y * texture( tex0, uv_nm + ofs01_nm.zw );
	vec4 smp3 = w.z * texture( tex0, uv_nm + ofs23_nm.xy );
	vec4 smp4 = w.w * texture( tex0, uv_nm + ofs23_nm.zw );

	return smp1 + smp2 + smp3 + smp4;
}

vec4 blursample( vec2 uv_nm, float coc )
{
	float rnd = 2 * 3.14159265 * rand( uv_nm.xy * tex0siz.xy );

	vec4 ofs01 = vec4(  0.25f,  0.0f, 0.0, 0.5f ) * coc;
	vec4 ofs23 = vec4( -0.75f,  0.0f, 0, -1 ) * coc;
	ofs01.xy = rot2d( ofs01.xy, rnd );
	ofs01.zw = rot2d( ofs01.zw, rnd );
	ofs23.xy = rot2d( ofs23.xy, rnd );
	ofs23.zw = rot2d( ofs23.zw, rnd );
	ofs01 *= tex0siz.zwzw;
	ofs23 *= tex0siz.zwzw;

	vec4 s0 = sample4( uv_nm, ofs01, ofs23, coc );
	vec4 s1 = sample4( uv_nm, -ofs01, -ofs23, coc );

	return 0.5f * ( s0 + s1 );
}

void main()
{
	far_focus = parms.x;
	far_blur = parms.y;

	float depth_sample = texture( depthtex, texcoord0_nm ).r;
	float coc = coc_from_depth( depth_sample );

	//DEBUG
	//outcol = vec4( vec3(coc), 1 );
	//outcol = texture( tex0, texcoord0_nm );
	//return;

	vec4 sample_col = blursample( texcoord0_nm, max_blursize_px * coc );

	outcol = sample_col;
	return;
}
