#define S(a, b, t) smoothstep(a, b, t)

float noise(float t)
{
    return fract(sin(t*3456.)*6547.);
}

vec4 noise14(float t)
{
    return fract(sin(t*vec4(123., 1024., 3567., 8745.))*vec4(4568., 345, 9821., 8632.));
}

struct ray
{
    vec3 origin, direction;
};

ray getRay(vec2 uv, vec3 camPos, vec3 lookAt, float zoom)
{
    ray a;
    a.origin = camPos;
    
    // Basis vectors for the camera.
    vec3 forward = normalize(lookAt - camPos);
    vec3 right = cross(vec3(0., 1., 0.), forward);
    vec3 up = cross(forward, right);
    vec3 centerScreen = a.origin + forward*zoom;
    vec3 intersectionPoint = centerScreen + uv.x*right + uv.y*up;
    
    a.direction = normalize(intersectionPoint - a.origin);
    
    return a;
}

vec3 closestPoint(ray r, vec3 point)
{
    return r.origin + max(0., dot(point - r.origin, r.direction))*r.direction;
}

float distToRay(ray r, vec3 point)
{
    return length(point - closestPoint(r, point));
}

float bokeh(ray r, vec3 point, float size, float blur)
{
    float dist = distToRay(r, point);
    size *= length(point);
    float s = S(size, size*(1.-blur), dist);
    s *= mix(.6, 1., S(size*.8, size, dist));
    return s;
}

vec3 streetLights(ray r, float time)
{
    float side = step(r.direction.x, 0.);
    r.direction.x = abs(r.direction.x);
    
    float s = 1./10.; // 0.1
    float mask = 0.;
    for(float i = 0.; i < 1.; i += s)
    {
        float ti = fract(time + i + side*s*.5);
        vec3 point = vec3(2., 2., 100. -ti*100.);
    
    	mask += bokeh(r, point, .05, .1)*ti*ti*ti;
    }
    
    return vec3(1., .5, 0.)*mask;
}

vec3 envLights(ray r, float time)
{
    float side = step(r.direction.x, 0.);
    r.direction.x = abs(r.direction.x);
    
    float s = 1./10.; // 0.1
    vec3 col_1 = vec3(0.);
    for(float i = 0.; i < 1.; i += s)
    {
        float ti = fract(time + i + side*s*.5);
        
        vec4 noise = noise14(i+side*100.);
        float fade = ti*ti*ti;
        
        float occlusion = sin(ti*6.28*10.*noise.x)*.5+.5;
        
        fade = occlusion; 
        float x = mix(2.5, 10., noise.x);
        float y = mix(.1, 1.5, noise.y);
        vec3 point = vec3(x, y, 50. -ti*50.);
        
        vec3 col_2 = noise.wzy;
    
    	col_1 += bokeh(r, point, .05, .1)*fade*col_2*.5;
    }
    
    return col_1;
}

vec3 headLights(ray r, float time)
{
    time *= 2.;
    float w1 = .25;
    float w2 = w1*1.2;
    float s = 1./30.; // 0.1
    float mask = 0.;
    
    for(float i = 0.; i < 1.; i += s)
    {
        float noise = noise(i);
        
        if(noise > .1)
        {
            continue;
        }
        
        float ti = fract(time + i);
        float z = 100. -ti*100.;
        float fade = ti*ti*ti*ti*ti;
        float focus = S(.8, 1., ti);
        
        float size = mix(.05, .03, focus);
    	mask += bokeh(r, vec3(-1.-w1, .15, z), size, .1)*fade;
        mask += bokeh(r, vec3(-1.+w1, .15, z), size, .1)*fade;
        
        mask += bokeh(r, vec3(-1.-w2, .15, z), size, .1)*fade;
        mask += bokeh(r, vec3(-1.+w2, .15, z), size, .1)*fade;
        
        float reflection = 0.;
        reflection += bokeh(r, vec3(-1.-w2, -.15, z), size*3., 1.)*fade;
        reflection += bokeh(r, vec3(-1.+w2, -.15, z), size*3., 1.)*fade;
        
        mask += reflection*focus;
        
    }
    
    return vec3(.9, .9, 1.)*mask;
}

vec3 tailLights(ray r, float time)
{
    time *= .25;
    float w1 = .25;
    float w2 = w1*1.2;
    float s = 1./15.;
    float mask = 0.;
    
    for(float i = 0.; i < 1.; i += s)
    {
        float noise = noise(i);		// At this point noise is between 0 - 1.
        
        if(noise > .5)
        {
            continue;
        }
        
        // If we get this far we know that noise value is between 0 - 0.5
        
        // This says if noise is < 0.25 then lane = 0
        // If noise is > 0.25 then lane = 1
        float lane = step(.25, noise);
        
        float ti = fract(time + i);
        float z = 100. -ti*100.;
        float fade = ti*ti*ti*ti*ti;
        float focus = S(.9, 1., ti);
        
        float size = mix(.05, .03, focus);
        float laneShift = S(1., .96, ti);
        float x = 1.5 - lane * laneShift;
        float indicator = step(0., sin(time*500.))*7.*lane*step(.96, ti);
        
        // Left bokeh
    	mask += bokeh(r, vec3(x-w1, .15, z), size, .1)*fade;
        mask += bokeh(r, vec3(x+w1, .15, z), size, .1)*fade;
        
        // Right bokeh
        mask += bokeh(r, vec3(x-w2, .15, z), size, .1)*fade;
        mask += bokeh(r, vec3(x+w2, .15, z), size, .1)*fade*(1.+ indicator);
        
        float reflection = 0.;
        reflection += bokeh(r, vec3(x-w2, -.15, z), size*3., 1.)*fade;
        reflection += bokeh(r, vec3(x+w2, -.15, z), size*3., 1.)*(1.+ indicator*.1);
        
        mask += reflection*focus;
        
    }
    
    return vec3(1., .1, .03)*mask;
}

vec2 rain(vec2 uv, float time)
{
    time *= 40.;
    vec2 a = vec2(3., 1.);
    vec2 st = uv*a;
    vec2 id = floor(st);
    st.y += time*.22;
    float noise = fract(sin(id.x*76.34)*758.58);
    st.y += noise;
    uv.y += noise;
    uv.x += noise*.08;
    id = floor(st);
    st = fract(st)-.5;
    
    time += fract(sin(id.x*76.34 +id.y*135.97)*758.58)*6.28;
    
    float y = -sin(time+sin(time+sin(time)*.5))*.43;
    vec2 pos_1 = vec2(0., y);
    vec2 offset_1 = (st-pos_1)/a;
    float dist = length((st - pos_1)/a);
    
    float mask_1 = S(.07, .0, dist);
    vec2 offset_2 = (fract(uv*a.x*vec2(1., 2.))-.5)/vec2(1., 2.);
    
    dist = length(offset_2);
    
    float mask_2 = S(.3*(.5-st.y), .0, dist)*S(-.1, .1, st.y-pos_1.y);
    //if(st.x > .46 || st.y > .49)
    //{
    //    mask_1 = 1.;
    //}
    
    return vec2(mask_1*offset_1*30.+mask_2*offset_2*10.);
}
    
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{ 
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    uv.x -= .75;
    uv.y -= .35;
    uv.x *= iResolution.x/iResolution.y;
    
    vec2 m = iMouse.xy/iResolution.xy;
    vec3 camPos = vec3(.5, .2, 0.);
    vec3 lookAt = vec3(.5, .2, 1.);
    float time = iTime * .04+m.x;
    
    //uv.x += sin(uv.y*40.)*.1;
    
    vec2 rainDistort = rain(uv*5., time)*.1;
    rainDistort += rain(uv*9., time)*.45;
    
    uv.x += sin(uv.y*20.)*.009;
    uv.y += sin(uv.y*90.)*.004;
    ray r = getRay(uv-rainDistort*.5, camPos, lookAt, 2.0);
    
    vec3 col = streetLights(r, time);
    
    col += headLights(r, time);
    col += tailLights(r, time);
    col += envLights(r, time);
    col += (r.direction.y+.25)*vec3(.2, .1, .5);

    //col = vec3(rainDistort, 0.);
    // Output to screen
    fragColor = vec4(col, 1.);
}
