/////////////////////////////////////////////////////////////////////
// Exercice : retourner la distance entre le point m et le segment AB
/////////////////////////////////////////////////////////////////////
float pointToSegmentDistance(vec3 m, vec3 A, vec3 B) {
    vec3 AB = B - A;
    float L = dot (AB, m - A) / length(AB);
    if(L < 0.) {
        return length(A - m); 
    } 
    else if (L > length(AB)){
        return length(B - m); 
    }
    else { 
        vec3 mprim = A + normalize(AB) * L; 
        return  length(m - mprim);
    }
}


/////////////////////////////////////////////////////////////////////
// Private
/////////////////////////////////////////////////////////////////////
float map(vec3 m) {
    
    float anim = iTime;
    
    vec3 A = vec3(cos(anim)*5., 0, 20.0+sin(anim)*5.);
    vec3 B = A + (vec3(0, 0, 20)- A)*2.;    
    
    float radius = 3.0;
    
    return pointToSegmentDistance(m, A, B) - radius;
}

vec3 pal( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ) {
    return a + b*cos( 6.28318*(c*t+d) );
}

vec3 spectrum(float n) {
    return pal( n, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.33,0.67) );
}

vec4 iridescent(in vec3 ray, in vec3 m, in vec3 normal) {

   	vec3 pos = m;
    vec3 ref = reflect(ray, normal);
    vec3 lig = normalize(vec3(.5,1,-.5));
    vec3 dome = vec3(0,1,0);
    vec3 eye = vec3(0,0,-1);

    vec3 perturb = sin(pos * 1.);
    vec3 color = spectrum( dot(normal + perturb * .05, eye) * 2.);

    float specular = clamp(dot(ref, lig), 0., 1.);
    specular = pow((sin(specular * 20. - 3.) * .5 + .5) + .1, 32.) * specular;
    specular *= .1;
    specular += pow(clamp(dot(ref, lig), 0., 1.) + .3, 8.) * .1;

    float shadow = pow(clamp(dot(normal, dome) * .5 + 1.2, 0., 1.), 3.);
    color = color * shadow + specular;

    return vec4(color, 1.0);
}


vec4 computeColor(in vec3 ray, in vec3 p, in vec3 normal, in vec3 lightSource) {
    
    vec3 light = normalize(p-lightSource);
    vec3 light2 = normalize(vec3(-1,1,-8));    
    
    float distorsion = -0.6;
    float diffuse = clamp(dot(normal, light2), 0.0, 1.0) * 1.;
    vec3 outlight = normalize(light2 + normal * distorsion);
    float scatter = clamp(dot(-ray, outlight), 0.0, 1.0) * 0.5;

    vec4 outter_color = vec4(121, 179, 255, 255)/255.;    
    vec4 inner_color  = vec4(0, 179, 255, 255)/255.;

    vec3 cp = (p - vec3(0., 0.0, +20.0)) / 20.0;
    outter_color = vec4(normalize(vec3(cp.x, 1.0-cp.z, cp.x)), 1.0);
    
    outter_color = vec4(normalize(vec3(1.0+cp.x, cp.y*10.0, -cp.z*10.)), 1.0);
    inner_color = outter_color;
    
    vec4 solid_color = outter_color * (0.15+diffuse) + inner_color * scatter;        
    
    vec3 ref = reflect(light2, normal);
    float specular = pow(clamp(dot(ref, ray), 0.0, 1.0), 60.0);
    
    vec4 irri = iridescent(ray, p, normal);

    return mix(solid_color, irri, 0.75);
    
}

bool rayMarching(in vec3 origin, in vec3 ray, out vec3 m) {

    const float zFar  = 40.0;

    float	marchingDist = 0.0;
    float 	nbIter 		 = 0.0;
    for(int i = 0; i<200; i++) {
        m = origin + ray * marchingDist;    
    	float dist = map(m);
        if(dist < 0.001) {
            return true;
        }
        else {
            marchingDist += dist * 0.75;
            if(marchingDist >= zFar) {
                break;
            }
        }
    }
	return false;    
}

vec3 computeNormal(in vec3 pos)
{
	vec3 eps = vec3( 0.01, 0.0, 0.0 );
	vec3 nor = vec3(
	     map(pos+eps.xyy) - map(pos-eps.xyy),
	     map(pos+eps.yxy) - map(pos-eps.yxy),
	     map(pos+eps.yyx) - map(pos-eps.yyx));
	return normalize(nor);
}


vec4 run(in vec2 fragCoord) {
    float 	ratio 			= iResolution.x/iResolution.y;
    vec3 	viewportCoord 	= vec3(fragCoord.xy/iResolution.y - vec2(ratio/2.0, 0.5), 1.);
    vec3	eye				= vec3(0.0, 1.25, -0.185);
    vec3	ray				= normalize(viewportCoord);
    vec3	p;
    
    vec3	lightSource		= vec3(-20.0, 0.0, 40.0);
    
    if(rayMarching(eye, ray, p)) {
        vec3 normal = computeNormal(p);
        return computeColor(ray, p, normal, lightSource);
    }
    else {
        return vec4(0.5, 0, 1.0, 1.0);  
    }
}


void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    fragColor = run(fragCoord); 
}





