#version 330 core
precision lowp float;
in vec2 UV;
out vec4 fragColor;
uniform sampler2D iChannel0; // Noise tex
uniform vec2 iResolution;
uniform float time;
uniform float flashsync;
uniform float flashpower;

#define ITERMAX 50
//#define VISIBLE_LIGHT
uniform float EPS;
uniform float nprec;
vec3 LIGHTPOS = vec3(7.0*cos(1.61*time), 7.0*sin(1.89*time), 100.0+10.0*sin(-0.74*time));

mat3 rotX(float a){return mat3(1.0,0.0,0.0,0.0,cos(a),-sin(a),0.0,sin(a),cos(a));}
mat3 rotY(float a){return mat3(cos(a),0.0,sin(a),0.0,1.0,0.0,-sin(a),0.0,cos(a));}
mat3 rotZ(float a){return mat3(cos(a),-sin(a),0.0,sin(a),cos(a),0.0,0.0,0.0,1.0);}

float hash(float c){return fract(sin(dot(c,12.9898))*43758.5453);}
float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
float snoise(vec3 x) {
    vec3 p = floor(x);
    vec3 f = fract(x);
    f = f*f*(3.0-2.0*f);
    
    vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy;
    vec2 rg = texture(iChannel0, (uv+ 0.5)/256.0, -100.0 ).yx;
    return mix( rg.x, rg.y, f.z );
}

float box( vec3 p, vec3 b, float r )
{
  vec3 d = abs(p) - b;
  return min(max(d.x,d.y),0.0) +
         length(max(d.xy,0.0))-r;
}

float tunnel(vec3 p) {
    float am = 3.3;
    float nt = am*(snoise(0.15*(p+time*11.0)))-am*0.5;
    nt += 0.1*am*(snoise(0.42*(p+time*13.0)))-am*0.05;
    return box(p+nt, vec3(6), 18.0);
}

float df(vec3 p) {
    return max(0.0, -tunnel(p));
}

vec3 getNormal(vec3 p, float e)
{
    vec3 n = vec3(df(p + vec3(e, 0.0, 0.0)) - df(p - vec3(e, 0.0, 0.0)),
                  df(p + vec3(0.0, e, 0.0)) - df(p - vec3(0.0, e, 0.0)),
                  df(p + vec3(0.0, 0.0, e)) - df(p - vec3(0.0, 0.0, e)));
    return normalize(n);
}
#ifdef VISIBLE_LIGHT
float lit;
#endif
vec4 shade(vec3 p, float rat){
    vec3 normal = getNormal(p, 0.025);//-0.01*hash(dot(p.x,p.y))+0.01;
    vec3 lightDir = normalize(LIGHTPOS - p);
    float lf = 3.75;
    #ifdef VISIBLE_LIGHT
    lf = 1.0+2.5*sqrt(lit);
    #endif
    float ct = (lf) * max(0.0, dot(normal, lightDir));
    ct = pow(ct, 1.35)*0.78;
    vec4 colorcand = vec4(0.0);
            if (ct < 0.7) ct = 0.0;
            ct = smoothstep(-0.3, 6.60, ct);
            colorcand = vec4(ct,ct,ct, 1.0);
            colorcand.x = smoothstep(0.11, 0.95, colorcand.x);
            colorcand.y = smoothstep(0.12, 0.85, colorcand.x)+0.001;
            colorcand.z = smoothstep(0.25, 0.70, colorcand.x);
            colorcand *= colorcand;
            colorcand += vec4(vec3(0.001),0.0);
    colorcand += (1.0-rat)*70.0*pow(max(dot(normalize(LIGHTPOS-p),normal),0.0),35.0);
    return pow(colorcand, vec4(0.3))-0.06;
}

vec3 getRefl(vec3 p, vec3 ray){
    for (int i = 0; i < ITERMAX/2; i++) {
        float d = df(p);
        if (d < EPS || 24.0 < d) {
            break;
        }
        p += d *ray;
    }
    return p;
}

void main()
{
    vec2 uv = UV - vec2(0.5);
    //fragColor = vec4(snoise(vec3(10.0*uv, 0.0))); return;
    uv.y *= iResolution.y/iResolution.x;
    
    vec3 ap = vec3(10.0*sin(0.2*time),4.0*cos(0.3*time),-20.0+30.0*cos(0.24*time));
    vec3 p = ap;
    float brightn = 0.0;
    mat3 shake = rotX(.02*texture(iChannel0, vec2(time*0.0064)).x)*
                 rotY(.02*texture(iChannel0, vec2(time*0.0051)).y)*
                 rotZ(.02*texture(iChannel0, vec2(time*0.0049)).z);
    vec3 ray = normalize(vec3(uv, 0.6)*shake);
    vec3 r = vec3(0.0);
    
    #ifdef VISIBLE_LIGHT
    float val = mod(time, 0.9272)*1.5;
    val *= val;
    lit = val;
    #endif
    float syn = flashsync*flashpower;
    syn *= syn*syn*20.0;
    
    for (int i = 0; i < ITERMAX; i++) {
        float d = df(p);
        if (d < EPS || 24.0 < d) {
            // hienot-rumat heijastukset..
            vec3 tn = getNormal(p, nprec); //0.2*hash(dot(p.x,p.y))+0.3);
            r = getRefl(p+tn*2.0*EPS, tn );
            break;
        }
        p += d * ray;
        #ifdef VISIBLE_LIGHT
        brightn += val*2.25/pow(length(LIGHTPOS-p), 2.0)*d;
        #endif
    }
    // heijastukset voimakkaammat kun kauempana valosta 
    float rat = clamp(0.6/log(-0.5+0.1*length(LIGHTPOS-p)), 0.0, 1.0);
    float glow = 5.0/pow(length(LIGHTPOS-p), 1.3);
    vec3 c = brightn+glow+(rat*shade(p, rat)+(1-rat)*shade(r, rat)).xyz+.06*hash(dot(p.x,p.y)+time)-0.03;
    float v=1.0/(1.0+(3.0-syn)*dot(uv,uv));
    vec3 bw = smoothstep(-0.2, 1.3, vec3( pow((c.x+c.y+c.z)/3.0, 0.75) ));
    fragColor = vec4(v*bw, 1.0);
}