uniform float iGlobalTime;
uniform vec2 iResolution;
out vec4 fragColor;

#define DUR (5.323173125 / 4.0) // duration
#define REPEATS 4.0
#define TOTALDUR (DUR * REPEATS)
#define SPD 1.0 // timescale
#define OFF 0.1 // time offset for audio timings

float rand1d(float n)
{
	return fract(sin(n) * 43758.5453);
}

float time()
{
    return mod((iGlobalTime + OFF) * SPD, DUR * REPEATS) / DUR;
}

float time(float offset)
{
    return (mod((iGlobalTime + OFF) * SPD, DUR * REPEATS) + offset) / DUR;
}

float quart()
{
    return mod(time(), 0.25);
}

float quart(float offset)
{
    return mod(time(offset), 0.25);
}

float halft()
{
    return mod(time(), 0.5);
}

float halft(float offset)
{
    return mod(time(offset), 0.5);
}

float wholet()
{
    return mod(time(), 1.);
}

float wholet(float offset)
{
    return mod(time(offset), 1.);
}

float doublet()
{
    return mod(time(), 2.);
}

float doublet(float offset)
{
    return mod(time(offset), 2.);
}

float smin(float a, float b, float k)
{
    float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
    return mix( b, a, h ) - k*h*(1.0-h);
}

float displacement(vec3 p, float k)
{
	return sin(k*p.x)*sin(k*p.y)*sin(k*p.z);
}

float texture1(vec2 p)
{
    p *= 0.03;

    float a = sin(p.x) * cos(p.y * 0.9 + 0.4) * 2.;
    float b = cos(p.x * 1.22 + 2.) * sin(p.y * 1.4) * 3.;
    pR45(p.xy);
    float c = cos(p.x * 3.22 + 1.) * sin(p.y * 3.4 + 0.5) * 2.;
    pR45(p.xy);
    float d = tan(p.x * 3.22 + 2.) * cos(p.y * 3.4 + 0.5) * 2.;

    float x = a + b * c + d;

    float val = cos(x * 1.) * 1.0;
    val += sin(c) * 0.2;
    val *= cos(b * 8.) * 0.2 + 1.0;
    return val;
}

// returns a sawtooth (0-1) between start and end
float superclamp(float val, float start, float end)
{
    float dur = end - start;
    float halfdur = dur / 2.;
    float prog = clamp(val, start, end) - start;
    return (halfdur - abs(prog - halfdur)) / halfdur;
}

// float getNoddingFactor()
// {
//     return superclamp(iGlobalTime, 21.2, 21.5);
// }

vec4 scene(vec3 p)
{
    // float nod = getNoddingFactor();
    // p *= nod +1.;
    
    float orangeTime = 20.6;
    float orangeness = max(orangeTime, iGlobalTime) - orangeTime;
    
    // pR(p.zy, -p.z * 0.01);
    // pR(p.yz, sin(p.z * 0.1) * 0.11);
    
    float spinFactor = 0.5 + tan((iGlobalTime) * 0.1) * 0.5;
    
    pR(p.zy, (p.z * 0.1 + iGlobalTime) * 0.1 * sin(-orangeness * 0.15));
    spinFactor *= 1. - min(0.6, orangeness) * 1.8;
    
    spinFactor -= orangeness * 0.01;
    
    pR(p.xy, (-p.z * 0.1 + iGlobalTime) * spinFactor);
    
    
    pR(p.xy, -orangeness * 0.2);
    
    // pR(p.zy, -(p.x * 0.4) * 0.1 * sin(orangeness * 0.15));
    
    // pR(p.zy, (-p.z * 0.1 + iGlobalTime));
    // pR(p.zy, -p.z * 0.001 * (1. - spinFactor));

    // pR(p.xy, (cos(iGlobalTime * 1.)) * 1.);
    // pR(p.yx, (sin(iGlobalTime * 1.)) * 1.);

    float speed = tan(iGlobalTime * 0.1) * 0.1 + 0.7;
    
    float orangeness_dist = 0.;
    orangeness_dist = min(25., orangeness * 5);
    vec3 tPos = p + vec3(0., -2 - orangeness_dist, -iGlobalTime * 35. * speed);
    
    vec3 c = vec3(0.);

    float noise = displacement(tPos * 0.4, 0.5) * 0.4 + 1.;
    // noise = 1.;
    float terrain = -tPos.y + noise * sin(1.57 + tPos.x * 0.4) * 2.0;
    
    //pos *= rotX(-0.1);
    // pR(p.xy, 1.);
    // pR(p.yz, -iGlobalTime * 3.14);
    pR(p.zy, 1.8);
    
    float pillar = fBox(p + vec3(0., -1., 2.) + (p.y + 10.) / ((iGlobalTime) * 2. + 8.), vec3(0.2, 100., 0.2));
    pillar = 10000.;
    
    //float pillar_noise = texture(iChannel0, tPos.xy * 0.01).r;
    // float pillar_noise = texture1(tPos.xy * sin(iGlobalTime)) * 0.3 + 0.9;
    // float pillar_noise = displacement(tPos, 0.4) + 0.8;
    pillar -= 0.2;
    
    float ground = smin(pillar, terrain, 3.);
    
    // float tc = max(0., sin(tPos.z) * 5.) * 2. + 0.4;
    // float tc = max(0., sin(tPos.x * 1.) * 5.) * 2. + 0.4;

    float tca = max(0., sin(tPos.x) * 3.) * 1.5 + 0.3;
    float tcb = max(0., sin(tPos.z) * 3.) * 1.5;
    
    
    float tc = tca - tcb;
    
    // tc = clamp(tc, 0., 1.);
    
    // tc *= length(p);
    
    tc -= tan((iGlobalTime - 0.3) * 0.1) * 0.6;

    //c = texture(iChannel3, tPos.xz * 0.1).rgb;
    
    float orange_c_ratio = min(0.25, orangeness) * 4.;
    
    vec3 tint = vec3(0.25, 0.56, 0.96) * (1. - orange_c_ratio) + 
        vec3(0.92, 0.15, 0.05) * (orange_c_ratio);
    c = tint * tc * 1.5;
    
    
    
    // c.r = sin(p.x * 3.0) * 5.;
    // c.b = 0.;
    
    //vec3 pillar_c = texture(iChannel1, tPos.xy * 0.1 + vec2(iGlobalTime, -iGlobalTime * 2.5)).rgb;
    vec3 pillar_c = vec3(1.0, 0.3, 0.05) * 2.;
    
    float pillar_c_w = min(1., ground + pillar);
    
    c = pillar_c * (1. - pillar_c_w) + c * pillar_c_w;
    
    // c += max(0., p.z - 50);
    
    return vec4(c, ground);
}

float getDistortFactor()
{
    float dur = 10. - clamp(iGlobalTime, 9, 10);
    
    return superclamp(doublet(0.), 1.1, 1.2) * dur
        ;
}

void main()
{
    float distorto = getDistortFactor();
    vec2 fragCoord = gl_FragCoord.xy;
    // fragCoord.x += rand1d(distorto + fragCoord.y) * distorto * 100.;    

    vec3 cameraOrigin = vec3(.0, -0.4, 18.0);
    cameraOrigin += vec3(sin(iGlobalTime) * 3., sin(iGlobalTime * 0.4) * 0.5, sin(iGlobalTime) * 1.); // gungis
    ////cameraOrigin += vec3(0., 0., -iGlobalTime * 25.); 
    vec3 cameraTarget = cameraOrigin + vec3(.0, -1., -10);
    pR(cameraTarget.yx, -iGlobalTime * 2.);
    pR(cameraTarget.xz, -iGlobalTime * 1.5);

    vec3 upDirection = vec3(.0, 1.0, .0);

    // pR(upDirection.xz, sin(iGlobalTime * 2.) * 100.0);
    //upDirection *= rotZ(sin(iGlobalTime * 2.) * 0.1); // gungis
    //upDirection *= rotX(-abs(sin(iGlobalTime * 0.3)) * 0.7);
    vec3 cameraDir = normalize(cameraTarget - cameraOrigin);
    vec3 cameraRight = normalize(cross(upDirection, cameraOrigin));
	vec3 cameraUp = cross(cameraDir, cameraRight);
    
    vec2 screenPos = -1.0 + 2.0 * fragCoord.xy / iResolution.xy;
	screenPos.x *= iResolution.x / iResolution.y;
    
    screenPos += (vec2(rand1d(iGlobalTime), rand1d(iGlobalTime + 1.0)) - 0.5)* 0.02;


    float orangeTime = 20.6;
    float orangeness = max(orangeTime, iGlobalTime) - orangeTime;
    float orange_shake = superclamp(orangeness, 0., 0.8) * 0.23;

    // fx
    float scaleN = max(.0, 0.1 - halft(-0.1));
    screenPos *= 1. - 3. * orange_shake; // scale
    
    float shakeN = max(.0, 0.2 - doublet(1.22));
    shakeN += max(.0, 0.1 - halft(1.22)) * 0.2;
    float shakeDur = 10. - clamp(iGlobalTime, 9, 10);
    screenPos += (vec2(rand1d(iGlobalTime), rand1d(iGlobalTime + 1.0)) - 0.5) * shakeN * shakeDur; //shake

    // orange shake
    screenPos += (vec2(rand1d(iGlobalTime), rand1d(iGlobalTime + 1.0)) - 0.5) * orange_shake; //shake

    
    vec3 rayDir = normalize(cameraRight * screenPos.x + cameraUp * screenPos.y + cameraDir);
    
    // // nodding
    // vec3 nodding = getNoddingFactor();
    // rayDir += nodding * 0.5;
    
    
    const float MAX_DIST = 200000.0;
    const float EPSILON = 0.1;

    float totalDist = 0.0;
    vec3 pos = cameraOrigin;
    float dist = EPSILON;
    vec3 color = vec3(0.);
    vec4 dist_color = vec4(EPSILON, vec3(0.));

    for(int i = 0; i < 200; i++)
    {
        if (dist < EPSILON || totalDist > MAX_DIST)
            break;
        
		dist_color = scene(pos);
        color = dist_color.rgb;
        dist = dist_color.a;
        
        //dist = scene(pos);
        totalDist += dist;
        pos += dist * rayDir;
    }
    
    vec3 bgColor = vec3(0.2, 0.3, 0.45) * 1.0;
    vec3 fadeColor = vec3(0.3, 0.31, 0.3);
    
    if(dist < EPSILON)
    {
        vec2 eps = vec2(0.0, EPSILON);
        vec3 normal = normalize(vec3(
            scene(pos + eps.yxx).a - scene(pos - eps.yxx).a,
            scene(pos + eps.xyx).a - scene(pos - eps.xyx).a,
            scene(pos + eps.xxy).a - scene(pos - eps.xxy).a));
        
        float diffuse = max(.0, dot(-rayDir, normal));
        //float diffuse = max(.0, dot(normalize(vec3(0., -100., 5.)), normal));
        float specular = pow(diffuse, 60.0);
        
        vec3 result = vec3(diffuse + specular);
        
        //color = color * 0.6 + fadeColor * 0.4;
        result = result * normalize(fadeColor) * 0.8 + bgColor * 0.4 + 0.05;
        
        result *= color * 1.3;
        
        fragColor = vec4(result, 1.0);
    }
    else
    {
        //fragColor = vec4(texture(iChannel1, screenPos * 0.25 - 0.5 + vec2(0., iGlobalTime * 0.03)).rgb, 1.0) * 0.6;
        
        vec3 bgc = vec3(0.25, 0.56, 0.96);

        pR(screenPos.yx, iGlobalTime * 0.1);
        float x = abs(sin(floor((screenPos.x) * 10))) * 1.2;
        fragColor = vec4(bgc * x, 1.);
    }
    
    

    fragColor += scaleN * 10.;
    
    float colorFactor = max(0.01, 1.0 - (halft(-0.03)) * 5.0);
    fragColor = round(fragColor / colorFactor) * colorFactor;
    
    fragColor = clamp(fragColor, 0., 1.);
    float fadetime = 31;
    float fadeFactor = clamp(iGlobalTime, fadetime, fadetime + 1.) - fadetime;
    fragColor -= fadeFactor * 0.5;
    
    
    float startFlash = 0.2 - min(0.2, iGlobalTime);
    fragColor += startFlash * 20.;
}