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

#define PRECISION 500.
#define MAX_ITERATIONS 80
#define MAX_DIST 10.
#define EPSILON (1. / PRECISION + 0.008)
#define time (iGlobalTime * (94. / 60.) / 1.)
const vec3 purple = normalize(vec3(1.0, 0.3, 0.05));

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

// const int RM_MAX_ITER = 9;
// const int DISTANCE = 14;

// const float EPSILON = 0.1;

// 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 quadt()
// {
//     return mod(time(), 4.);
// }

// 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 getDistortFactor()
// {
//     return superclamp(iGlobalTime, 18., 18.2) + 
//         superclamp(iGlobalTime, 18.3, 18.55) + 
//         superclamp(iGlobalTime, 20.33, 20.5) + 
//         superclamp(iGlobalTime, 20.9, 21.) * 0.7 + 
//         superclamp(iGlobalTime, 21.07, 21.12) * 0.7
//         ;
// }

// vec4 scene(in vec3 p)
// {
//     p.y *= 0.85;
//     p.y += p.x * 0.3;
    
//     float scaleN = max(.0, 0.15 - doublet(-0.3));
//     p *= 0.9 - scaleN * 1.; // scale
    
//     float distort = getDistortFactor();
//     p *= 1. + rand1d(distort) * 0.4 * distort;
//     p.y += rand1d(distort) * 0.4 * distort;
//     p.z -= rand1d(distort) * 0.8 * distort;
    
//     float opening = max(0., 0.5 - iGlobalTime) * 5.;
//     float opening2 = max(0., 10. - sin(iGlobalTime * 0.1)) * 0.05 - 0.8;

// 	mat4 matrix = mat4(
// 		vec4(cos(p.y + time(2.2)), 0, sin(p.y + time(2.2)), 0),
// 		vec4(p.z * ((0.3 * opening2)), 0, 0, 0),
// 		vec4(-sin(p.y + time(2.2)), 0, cos(p.x + time(2.2)), 0),
// 		vec4(0, 0, 2, 1)
// 	);


//     p = (vec4(p, 0.) * matrix).xyz;


//     vec3 c = vec3(1.0, 0.3, 0.05) * 1.0;

//     float sphere = fSphere(p, 1.0);

//     return vec4(c, sphere);
// }

// vec3 getNormal(in vec3 p)
// {
// 	vec3 normal;
// 	vec3 ep = vec3(EPSILON, 0, 0);

// 	normal.x = scene(p + ep.xyz).a - scene(p - ep.xyz).a;
// 	normal.y = scene(p + ep.yxz).a - scene(p - ep.yxz).a;
// 	normal.z = scene(p + ep.yzx).a - scene(p - ep.yzx).a;

// 	return normalize(normal);
// }

// float bgColor(in vec3 p)
// {
//     float smokeTime = 10.7;
//     float smok = superclamp(iGlobalTime, smokeTime, smokeTime + 7.35);
    
//     float c = p.x / (90 + smok * 90000.);
//     c = clamp(c, 0., 1.) + clamp(-c, 0., 1.);
    
//     // return 0.5;
    
//     return c;
// }

// float bgScene(in vec3 p)
// {
//     p.x -= 5.;
//     p.z += 5.;
    
//     // pR45(p.xy);
//     // pR(p.yz, iGlobalTime * 0.02);
    
//     pR(p.xy, sin(iGlobalTime * 0.2) * 0.04);
//     pR(p.yz, sin(iGlobalTime * 0.7) * 0.04);
//     pR(p.zx, sin(iGlobalTime * 0.3) * 0.04); 
//     p.z += iGlobalTime * 0.5;
//     p.z += sin(iGlobalTime + 1.15);
//     // pR45(p.xy);
//     pR(p.xy, iGlobalTime * 0.04 - 0.15);
    
//     vec2 cell = pMod2(p.xy, vec2(3.5));
//     float evenfactor = mod(cell.x + cell.y + round(time() / 2), 2);
    
//     float lenfactor = length(cell) * 0.1;
    
//     float len = 3.;
//     len += sin(superclamp(doublet(), 0.2, 0.6)) * (1. - lenfactor) * evenfactor * 2.;
//     len *= 1. + lenfactor;
    
//     p.z -= len;
    
//     return fBox(p, vec3(1.5, 1.5, len));
//     // pR45(p.zy);
//     // pR45(p.zy);
//     // return fCylinder(p, 1.5, len);
// }

// void main()
// {
//     vec2 fragCoord = gl_FragCoord.xy;
    
//     fragColor
// }

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

float glass(float x, float sharpness) {
    return (1. / max(0., x)) / (sharpness * 100.);
}

float lightness(float df, float density, float sharpness)
{
    return min(1., glass(df, sharpness)) * density * (1. + sharpness / 10.) * (1. + (PRECISION / 500.)) * 0.3;
}

void add(inout float d, inout vec3 c, float d2, vec3 color, float density, float sharpness)
{
    d = min(d, d2);
    c += lightness(d2, density, sharpness) * color;
}

void sub(inout float d, inout vec3 c, float d2, vec3 color, float density, float sharpness)
{
    d = max(-d,d2);
    c -= lightness(d2, density, sharpness) * color;
}

float shell(float d)
{
    return max(-d, d);
}


// https://thebookofshaders.com/11/
float rand(in vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

// 2D Noise based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    float a = rand(i);
    float b = rand(i + vec2(1.0, 0.0));
    float c = rand(i + vec2(0.0, 1.0));
    float d = rand(i + vec2(1.0, 1.0));
    vec2 u = smoothstep(0.,1.,f);
    return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

void ockel(vec3 p, float n, inout vec3 c, inout float d, float turn)
{
    // n -= 0.8;
    // n *= 0.5;


    // pR(p.xz, 1.1);
    n *= 2.0;
    


    vec3 p1 = p;
    
    p1.y += noise((p1.xz - n) * 0.2) * 0.9;
    // p1.y += noise((p1.xz - n) * 2.0) * 0.1;
    // p1.y -= noise((p1.xz - n) * 30.0) * 0.005;

    

    if(time > 127.0) {
    p1.y += noise((p1.xz - n) * 0.2) * 0.9;
    pR(p1.zy, 0.7 - n * 2.0);

    }


    // pR(p1.yx, 0.05);
    pR(p1.xz, 0.7);

    // p.z -= 0.5;
    // pR(p.yz, 2.1);
    // pR(p.xy, PI/2. + sin(n) * 1.5);

    float d1 = fBox(p1, vec3(0.35, 0.2, 0.2));

    vec3 p2 = p1 + vec3(-0.4, 0.0, 0.0);
    pR(p2.xy, -0.15);
    d1 = max(-fBox(p2, vec3(0.3)), d1);
    
    vec3 p3 = p1 + vec3(0.36, -0.46, 0.0);
    pR(p3.xy, 0.17);
    d1 = max(-fBox(p3, vec3(0.3)), d1);
    
    vec3 p4 = p1 + vec3(0.23, 0.4, 0.0);
    pR(p4.xy, 0.6);
    d1 = max(-fBox(p4, vec3(0.3)), d1);

    vec3 p5 = p1 + vec3(0.5, 0.23, 0.0);
    pR(p5.xy, -0.17);
    d1 = max(-fBox(p5, vec3(0.3)), d1);

    vec3 p6 = p1 + vec3(0.0, 0., 0.59);
    pR(p6.yz, -0.1);
    d1 = max(-fBox(p6, vec3(0.4)), d1);
    
    vec3 p7 = p1 + vec3(0.0, 0., -0.59);
    pR(p7.yz, 0.1);
    d1 = max(-fBox(p7, vec3(0.4)), d1);

    add(d, c, shell(d1), vec3(1.0, 0.3, 0.2), 10., 30.);

    vec3 p8 = p1 + vec3(-0.22, -0.18, 0.0);
    float d2 = fBox(p8, vec3(0.6, 0.02, 0.22));

    vec3 p10 = p1 + vec3(-0.45, -0.1, 0.0);
    d2 = min(fBox(p10, vec3(0.3, 0.1, 0.2)), d2);
    
    add(d, c, shell(d2), vec3(1.0, 0.3, 0.05), 5., 2.);

    vec3 p9 = p1 + vec3(-0.22, -0.23, 0.0);
    float d3 = fBox(p9, vec3(0.45, 0.04, 0.2));
    
    vec3 p11 = p1 + vec3(-0.45, -0.03, 0.);
    d3 = min(d3, fBox(p11, vec3(0.3, 0.03, 0.2)));
    
    add(d, c, (d3), vec3(0.0), 10., 2.);

    
    vec3 p12 = p1 + vec3(0.3, -0.3, -0.23);
    pR(p12.xy, 0.05);
    pR(p12.xz, turn);
    float d4 = fBox(p12, vec3(0.2, 0.02, 0.05));

    
    vec3 p13 = p1 + vec3(0.3, -0.3, 0.23);
    pR(p13.xy, 0.05);
    pR(p13.xz, turn);
    d4 = min(d4, fBox(p13, vec3(0.2, 0.02, 0.05)));
    
    add(d, c, (d4), vec3(1.0, 0.0, 0.0), 10., 2.);

    
    vec3 p14 = p1 + vec3(0.15, 0.1, 0.);
    // pR(p14.xy, 0.05);
    // pR(p14.xz, turn);
    float d5 = fDodecahedron(p14, 0.05);
    
    add(d, c, d5, vec3(0.1, 0.65, 0.8) * 1.5, 20.0, 0.5);
    


    // pR(p.yx,  n);
    // p.z += tan(n * PI) * 0.1;
    // add(d, c, max(-fBox(p, vec3(0.2, 0.2, 0.2)), fBox(p, vec3(0.2, 0.2, 0.04))), purple.grb, 5., 0.4);
}


void scene12(vec3 p, float n, inout vec3 c, inout float d)
{
    vec3 p1 = p;
    vec3 p2 = p;

    
    p2.y += noise((p1.xz - n) * 0.2) * 0.9;
    p2.y += noise((p1.xz - n) * 2.0) * 0.1;
    p2.y -= noise((p1.xz - n) * 30.0) * 0.005;

    // p1.x += sin(n * 0.25 - 1.0);
    // p1.y += cos(n * 0.2);
    // float sun = fSphere(p1, 0.2);
    // add(d, c, sun, purple.brg, 25., 0.05);
    // p1 += sin(p1.y * 10.0 + n) * 0.5;

    // p2.x -= 18.0;
    // p2.z += pow(n, 1.7) * 0.1;

    // p2.y -= 0.8;
    pR(p2.yz, -0.5);
    // pR(p2.xz, sin(n * 0.5) * 0.1);

    
    // pR(p2.yz, -1.0); // panup++
    // pR(p2.yz, -1.0); // panup^
    

    
    pR(p2.xy, 0.9 + n * 0.01);
    // p.z += n * 10.0;
    // pR(p2.yz, 0.5 + n * 0.0797);
    p2.xy += n;
    p2.xy *= 2.0;
    // pR(p2.xz, sin(n * 0.2) * 0.1);
    // pR(p2.zy, cos(n * 0.2) * 0.1);

    // p2.xy *= sin(n * 0.25) + 2.0;
    p2.z += sin(p.y - 1.0) * 0.5;


    p2.z -= 2.0;

    vec2 cell = pMod2(p2.xy, vec2(0.5));
    p2.z /= 3.0;

    // p2.z += (rand(cell.xy * 0.1) - 0.5) * 0.1;


    float star = fSphere(p2, 0.005 + sin(cell.y) * 0.04);
    // float star = fSphere(p2, 0.01);
    add(d, c, star, purple.rbg, 22.0, 0.5);
}

void snow(vec3 p, float n, inout vec3 c, inout float d)
{
    vec3 p1 = p;
    p1.y -= 0.7;

    if (n < 3.0 || (n > 75.0 && n < 79.0)) {
        n = 0.1;
    }

    if(time > 127.0 && time < 159.0) {
        n *= 0.48;
    }


    n *= 2.0;

    // p1.xz += n;

    p1.y += noise((p1.xz - n) * 0.2) * 0.9;
    p1.y += noise((p1.xz - n) * 2.0) * 0.1;
    p1.y -= noise((p1.xz - n) * 30.0) * 0.005;

    // p1.y += noise(p1.xz - n) * 0.2;
    // p1.y += noise(p.xz * 20. - n) * 0.1;

    // vec3 p2 = p1;

    // p2.xy -= n;

    // p1 += displacement(p2 * 4.0, 0.5) * 0.2;
    // p1 += displacement(p2 * 100.0, 0.5) * 0.01;
    

    if(time > 127.0 && time < 159.0) {
        // p1 += displacement(p1 * 2.0, 0.5) * 10.0;
    }
    
    float d1 = fBox(p1, vec3(8., 0.3, 8.0));

    pR(p1.xz, p1.x * 0.05 * sin(n * 0.1));

    pR(p1.xz, 0.6);
    p1.x -= 4.7;
    p1.z -= 0.2;
    p1.xy += 0.1;


    d1 = max(-fBox(p1, vec3(5., 0.3, 0.1)), d1);
    p1.z += 0.4;
    d1 = max(-fBox(p1, vec3(5., 0.3, 0.1)), d1);

    add(d, c, d1, vec3(1.0), 2.5, 0.2);
    
}

vec4 scene(vec3 p)
{
    float n = time;
    vec3 c = vec3(0.);
    float d = 1. / 0.;
    // n *= 2.0;
    

    
    if(time > 127.0 && time < 159.0) {
        scene12(p, n, c, d);
    }


    if(n * 2.0 > 85.5 && n * 2.0 < 118.0 || n > 159.0) {
        // n = 4.0;
        
        float b1 = superclamp(mod(n * 2.0 + 0.4, 1.0), 0.0, 0.5);

        

        pR(p.xy, p.z * sin(n * 2.0) * b1 * 0.2);
        p.y += noise(p.xz + vec2(n * 6.0)) * b1 * 0.8;
    }
    
        pR(p.zy, p.x * 0.05);

    if(n * 2.0 > 158.0) {

        // pR(p.zx, -p.y * 0.9);
        p.z -= 1.0;
        pR(p.zx, -p.y * 0.9);
        
    }


    // p.y -= 1.0;

    p.x += 0.3;
    // pR(p.xz, -0.9);
    pR(p.yz, 0.3);

    // pR(p.xz, n * 0.4);

    // n += + viewId * 11.0;

    


    vec3 p1 = p;
    pR(p1.xy, sin(n * 0.8) * 0.1);
    pR(p1.yz, cos(n * 0.8) * 0.1);
    p1.y += noise(vec2(n * 3.0)) * 0.02;
    ockel(p1, n, c, d, sin(n * 0.5) * 0.4);

    vec3 p2 = p;
    snow(p2, n, c, d);
    // scene2b(p1, n + 1.0, c, d);

    // backdrop2(p, n, c, d);
    // scene3(p, n, c, d);

    // // scene8(p, n, c, d);
    // scene7(p, n, c, d);

    // // if(n > 8.0) {
    //     scene13(p + vec3(0.0, 0.25, 0.0), n - 8.0, c, d);
    // // }
    
    return vec4(c, d);
}

vec3 origin()
{
    float n = time - 3.0;
    
    float i = mod(floor(n / 8.0), 4.0);

    if(time > 127.0 && time < 142.0) {
        return vec3(-1.2, 0.1, 4.1);
    }

    if(n < 0 || (n > 72.0 && n < 76.0)) {
        return vec3(-0.5, 0.7, -1.2);
    } 


    if(i == 0) {
        return vec3(0., -9., 0.1);
    } else if(i == 1) {
        return vec3(-1.2, -0.1, -2.1);
    } else if(i == 2) {
        return vec3(3., -2., 2.1);
    }

    // if(mod(i, 4.0))

    return vec3(-2.2, -1.1, -4.1);

}


vec3 target()
{
    float n = time - 3.0;

    if(time > 127.0 && time < 159.0) {
        // return origin() + vec3(-0.9, 0., 0.0);

        // vec3 p1 = vec3(1., 1.0, -4.0);
        // p1.y += noise((p1.xz - n) * 0.2) * 0.9;
        // p1.y += noise((p1.xz - n) * 2.0) * 0.1;
        // p1.y -= noise((p1.xz - n) * 30.0) * 0.005;
        // return p1;
    }

    float i = mod(floor(n / 8.0), 5.0);

    if(time < 3.0 || (n > 72.0 && n < 76.0)) {
        return vec3(-0.5, -0.8, 1.);
    }

    // if(i == 1) {
    //     return vec3(0., 0., 1.);
    // } else if(i == 2) {
    //     return vec3(1., 0., -1.);
    // } else if (i == 3) {
    //     return vec3(0., -1., -1.);
    // }


    return vec3(0., 0., 1.);
}

vec3 normalAt(in vec3 p)
{
    float c = scene(p).w;
	vec2 nOfs = vec2(0.001, 0.0);
	return normalize(vec3(scene(p + nOfs.xyy).w, scene(p + nOfs.yxy).w, scene(p + nOfs.yyx).w) - c);
}

vec3 march(vec3 pos, vec3 rayDir)
{
    vec3 c = vec3(0.);
    int reflections = 0;
    
    for(int i = 0; i < MAX_ITERATIONS; i++)
    {
        vec4 s = scene(pos);
        float d = max(s.w, (1. + (0.1 * pos.z)) / PRECISION);
        
        pos += rayDir * d;
        c += s.rgb / (20. + PRECISION);
        
        if(d < EPSILON && reflections < 1 )
        {
            reflections++;

            vec3 normal = normalAt(pos - rayDir * EPSILON * 2.);
            vec3 rayDir = reflect(rayDir, normal);
            vec3 pos = pos;

            for(int i = 0; i < MAX_ITERATIONS; i++)
            {
                vec4 s = scene(pos);
                float d = max(s.w, (1. + (0.1 * pos.z)) / PRECISION);
                
                pos += rayDir * d;
                c += (s.rgb / PRECISION) * 0.3;

                if(d > MAX_DIST)
                    break;
            }
        }
        else if(d > MAX_DIST)
            break;
        // else if(i == MAX_ITERATIONS - 1)
        //     c = vec3(1., 0., 0.);
    }

    return c;
}

vec3 image(vec2 uv, out vec4 fragColor)
{
    vec3 origin = origin();
    vec3 upDirection = vec3(0., 1.0, 0.);
    vec3 cameraDir = normalize(target() - origin);
    vec3 cameraRight = normalize(cross(upDirection, origin));
	vec3 cameraUp = cross(cameraDir, cameraRight);
    vec3 rayDir = normalize(cameraRight * uv.x + cameraUp * uv.y + cameraDir);

    vec3 c = march(origin, rayDir);

    return c;
}


float rand(float x)
{
    return fract(sin(dot(x, 12.9898)) * 43758.5453123);
}


float noise44(vec2 uv, float intensity, float n)
{
    pR(uv, n + 1.);
    return min(1., (1. / (rand(uv.x * 20. + 1.) + rand(uv.y * 40.))) * intensity);
}

void pp(inout vec3 c, vec2 uv)
{
    c = clamp(c, 0.02, 1.);
    c -= 0.02;
    c *= 1.1;
    c = sqrt(c);
    c = c*c*(2.5-1.45*c*c); // contrast
    c = pow(c, vec3(1.0,0.96,1.0)); // soft green
    c = pow(c, vec3(1.0,0.96,1.0)); // soft green
    c = pow(c, vec3(1.0,0.96,1.0)); // soft green
    c *= vec3(1.08,0.99,0.99); // tint red
    c *= vec3(1.08,0.99,0.99); // tint red
    c *= vec3(1.08,0.99,0.99); // tint red
    c.b = (c.b+0.05)/1.05; // bias blue
    // c = mix(c, c.ggg, 0.12); // desaturate
    c = 0.06 + (c * 0.9);
    c = 0.06 + (c * 0.9);
    c = 0.06 + (c * 0.9);
    c = 0.06 + (c * 0.9);
    

    c += noise44(floor(uv * 100.), 0.005, floor(time * 10.)); // rain
    c += noise44(floor(uv * 10.), 0.003, floor(time * 10.)); // rain

    c -= smoothstep(0.55, 1.3, abs(uv.x)) * 0.2; // vignette x
    c -= smoothstep(0.17, 0.7, abs(uv.y)) * 0.2; // vignette y
    c -= step(0.515, abs(uv.x)) * 10.0; // letterbox x
    c -= step(0.35, abs(uv.y))* 10.0; // letterbox y
    c += rand(uv.xy + iGlobalTime) * 0.02; // noise
    c *= 1.0 + noise(uv.xy * 1.0 + iGlobalTime) * 0.2; // soft glow
    // c += noise(floor(10.0 + uv.xy * 18.0)) * 0.01; // soft glow
    // c *= 1.0 + step(noise(10.0 + floor(uv.xy * 10.0) + iGlobalTime * 1000.0), 0.5) * 0.015; // soft glow
    
}

void main()
{
    vec2 uv = (gl_FragCoord.xy - (iResolution.xy * 0.5)) / iResolution.yy;
    uv.x *= 1.7;
    uv *= 0.8;

    uv *= 1.0 + ((abs(uv.x) + abs(uv.y)) * 0.1);

    // fragColor = vec4(0.5);
    vec3 c = vec3(0.0, 0.0, 0.0);

    c += image(uv, fragColor);

    if(uv.x > 10000.0) {
        c += 1.0;
    }

    pp(c, uv);


    if(time > 159.0) {
        c.r = -c.r;
    } else if(time > 59.0 && time < 79.0) {
        c.b = -c.b;
    }
    


    fragColor = vec4(c, 1.);
}