uniform float time;
uniform vec2 resolution;
uniform float fov;
uniform float alpha;
uniform float dist;
uniform vec3 cPosition;
uniform vec3 rotation;
uniform vec3 color;
uniform float darkness;
uniform float modifier;
uniform float enabled;

varying float vNoise;
varying vec3 vNormal;
varying vec3 vPos;
varying vec2 vUv;

//Maximum number of steps to make
const int maxSteps = 64;
//Minumum distance to surface.
const float distanceThreshold = 0.00005;


float sdMandleBox(vec3 pos, vec3 size, float twist, float bend, bool multiply)
{
    const int Iterations = 10;
    const float Scale = 2.0;
    const float FoldingLimit = 100.0;
    const float MinRad2 = 0.15;
    vec4 scale = vec4(size.x) / MinRad2;
    float AbsScalem1 = abs(Scale - 1.0);
    float AbsScaleRaisedTo1mIters = pow(abs(Scale), float(1-Iterations));
    vec4 p = vec4(pos,1.0), p0 = p;  // p.w is the distance estimate
    
    for (int i=0; i<Iterations; i++)
    {
        p.xyz = clamp(p.xyz, -1.3, 1.3) * 2.0 + (modifier) - p.xyz;
        float r2 = dot(p.xyz, p.xyz);
        p *= clamp(max(MinRad2 / r2, MinRad2), 0.19, 1.0);
        p = p * scale + p0;
        if (r2>FoldingLimit) break;
    }
    return ((length(p.xyz) - AbsScalem1) / p.w - AbsScaleRaisedTo1mIters);
}

vec3 rotateY(vec3 p, float a)
{
    float c,s;
    vec3 q=p;
    c = cos(a);
    s = sin(a);
    p.x = c * q.x + s * q.z;
    p.z = -s * q.x + c * q.z;
    return p;
}

//From camera(rayOrigin) ray is cast for every pixel in quad. Ray follows vector from 
//camera(rayOrigin) to direction of pixel(rayDirection). This is done in steps, and in
//every step combination of distance functions is calcuted. This value is compared to 
//threshold value and if it is smaller we know that we have hit surface.

vec4 raymarch(vec3 rayOrigin, vec3 rayDirection) {
    float totalDistance = 0.0;
    float steps = 0.0;
    for(int i = 0; i < maxSteps; i++) {
        float d = sdMandleBox(rayOrigin + rayDirection * totalDistance, vec3(2.8, 1.1, 2.1), 0.0, 0.0, false);
        steps += 1.0;
        if(d < distanceThreshold) {
            return vec4(rayOrigin + rayDirection * totalDistance, steps);
        }
        totalDistance += d;
    }
    return vec4(rayOrigin + rayDirection * totalDistance, steps);
}


//Process color pixel in question, now this only modifies pixel color values
//depending on how far ray had to travel and and many steps it had to take
//to find surface to hit.
vec4 processColor(vec4 t, vec3 rd, vec3 ro, vec2 uv)
{
    vec3 c = vec3(0.0, 1.0 - (t.y / float(maxSteps)), 0.0);
    
    c = color;
    c /= t.w / float(maxSteps);
    c /= length(t.xyz - rd);
    c *= darkness;
    return vec4(c, alpha);
}

//Main program to start raymarching for each pixel in quad. 
void main(void)
{
    if(enabled > 0.0) {
        float aspectRatio = resolution.y / resolution.x;
        vec2 uv = (gl_FragCoord.xy / resolution) - 0.5;
        
        vec3 rd = vec3(uv.x * fov, uv.y * fov * aspectRatio, 1.0);
    
        vec3 ro = vec3(cPosition.x, cPosition.y, cPosition.z);
        vec4 t = vec4(0.0);
        if(dist == 1.0) {
            t = raymarch(rotateY(rd, sin(time / 500.0)), rotateY(ro, cos(time / 500.0)));
        }
        else if(dist == 0.0) {
            t = raymarch(rotateY(ro, rotation.y), rotateY(rd, rotation.y));
        }
        else {
            t = raymarch(rotateY(rd, rotation.y), rotateY(ro, rotation.y));
        }
        
        gl_FragColor = processColor(t, rd, ro, uv);
    }
    else {
        gl_FragColor = vec4(0.0);
    }
    

}