uniform float RandParam;

uniform float EyePitch, EyeYaw, EyeRoll, EyeDistance;

uniform sampler2D LastTex;

float shitRand(float f)
{
	f *= 40.0;
	return cos((f + 1000.0) * f);
}

struct intersectionInfo
{
	bool Hit;
	float Distance;
	vec3 Pos, Normal, Color;
	float Emission, Reflectance;
};

intersectionInfo intersectSphere(vec3 pos, vec3 dir, vec3 spherePos, float sphereRad, vec3 sphereColor, float emission, float reflectance)
{
	vec3 dis = pos - spherePos;
	float b = dot(dis, dir);
	intersectionInfo ret;
	if (b < 0.0)
	{
		float c = dot(dis, dis) - sphereRad * sphereRad;
		float d = b * b - c;
		if (d > 0.0)
		{
			ret.Hit = true;
			ret.Distance = -b - sqrt(d);
			ret.Pos = pos + dir * ret.Distance;
			ret.Normal = normalize(ret.Pos - spherePos);
			ret.Color = sphereColor;
			ret.Emission = emission;
			ret.Reflectance = reflectance;
		}
		else
		{
			ret.Hit = false;
		}
	}
	else
	{
		ret.Hit = false;
	}
	return ret;
}

intersectionInfo intersectPlane(vec3 pos, vec3 dir, vec3 planeNormal, float planeDis, vec3 planeColor, float emission, float reflectance)
{
	intersectionInfo ret;
	float k = dot(dir, planeNormal);
	if (k < 0.0)
	{
		ret.Hit = true;
		ret.Distance = (-planeDis - dot(pos, planeNormal)) / k;
		ret.Pos = pos + dir * ret.Distance;
		ret.Normal = planeNormal;
		ret.Color = planeColor;
		ret.Emission = emission;
		ret.Reflectance = reflectance;
	}
	else
	{
		ret.Hit = false;
	}
	return ret;
}

#define obj(methodCall) tmp = methodCall; if (tmp.Hit && (!ret.Hit || tmp.Distance < ret.Distance)) ret = tmp;
intersectionInfo intersectScene(vec3 pos, vec3 dir)
{
	intersectionInfo ret, tmp;
	ret.Hit = false;
	
	obj(intersectPlane(pos, dir, vec3(-1,  0,  0), 1.0, vec3( 1, .6, .1), 0.0, 0.0));
	obj(intersectPlane(pos, dir, vec3( 1,  0,  0), 1.0, vec3(.1, .1,  1), 0.0, 0.0));
	obj(intersectPlane(pos, dir, vec3( 0, -1,  0),  .8, vec3( 1,  1,  1), 1.0, 0.0));
	obj(intersectPlane(pos, dir, vec3( 0,  1,  0),  .8, vec3( 1,  1,  1), 0.0, 0.0));
	obj(intersectPlane(pos, dir, vec3( 0,  0, -1), 1.0, vec3( 1,  1,  1), 0.0, 0.0));
	obj(intersectPlane(pos, dir, vec3( 0,  0,  1), 1.0, vec3( 1,  1,  1), 0.0, 1.0));

	obj(intersectSphere(pos, dir, vec3(0,  .6, 0), .2, vec3(1), 0.0, 0.0));
	obj(intersectSphere(pos, dir, vec3(0,  .2, 0), .2, vec3(1), 0.0, 0.0));
	obj(intersectSphere(pos, dir, vec3(0, -.2, 0), .2, vec3(1), 0.0, 0.0));
	obj(intersectSphere(pos, dir, vec3(0, -.6, 0), .2, vec3(1), 0.0, 0.0));
	
	return ret;
}

#define rand(f) (randomSeed = shitRand(randomSeed + f))
vec3 trace(vec3 pos, vec3 dir)
{
	vec3 color = vec3(0);

	intersectionInfo res = intersectScene(pos, dir);
	if (res.Hit)
	{
		color = res.Color * res.Emission * 3.0;
		vec3 originalPos = res.Pos;
		vec3 originalDir = dir;
		vec3 originalNormal = res.Normal;
		vec3 originalColor = res.Color;
		float originalReflectance = res.Reflectance;
		float randomSeed = RandParam;
		const int paths = 3;
		for (int i = 0; i < paths; i++)
		{
			vec3 pos = originalPos;
			dir = originalDir;
			vec3 normal = originalNormal;
			vec3 currentColor = originalColor;
			float reflectance = originalReflectance;
			for (int j = 0; j < 5; j++)
			{
				dir = normalize(mix(normal + vec3(rand(res.Pos.x), rand(res.Pos.y), rand(res.Pos.z)) * .499, reflect(dir, normal), reflectance));
				res = intersectScene(pos, dir);
				if (!res.Hit) break;
				pos = res.Pos;
				normal = res.Normal;
				reflectance = res.Reflectance;
				currentColor *= res.Color;
				color += currentColor * res.Emission;
			}
		}
	}

	return color;
}

void main()
{
	vec2 pixelPosition = (gl_FragCoord.xy - vec2(640, 360)) / 720.0;
	
	vec3 eyeDir = normalize(vec3(pixelPosition * 1.4, 1));
	eyeDir = vec3(eyeDir.x, eyeDir.y * cos(EyePitch) + eyeDir.z * sin(EyePitch), eyeDir.z * cos(EyePitch) - eyeDir.y * sin(EyePitch));
	eyeDir = vec3(eyeDir.x * cos(EyeRoll) + eyeDir.y * sin(EyeRoll), eyeDir.y * cos(EyeRoll) - eyeDir.x * sin(EyeRoll), eyeDir.z);
	eyeDir = vec3(eyeDir.x * cos(EyeYaw) + eyeDir.z * sin(EyeYaw), eyeDir.y, eyeDir.z * cos(EyeYaw) - eyeDir.x * sin(EyeYaw));
	vec3 eyePos = vec3(sin(EyeYaw), 0, cos(EyeYaw)) * -EyeDistance;
	
	vec4 lastTexel = texture2D(LastTex, gl_FragCoord.xy / vec2(1280, 720));
	if (lastTexel.w >= 10000.0)
	{
		gl_FragColor = lastTexel;
		return;
	}
	gl_FragColor = lastTexel + vec4(trace(eyePos, eyeDir), 3);
}
