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

#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 = 8;
const int DISTANCE = 9;

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 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;
}

vec4 scene(in vec3 p)
{
    float scaleN = max(.0, 0.15 - doublet(-0.3));
    p *= 0.9 - scaleN * 1.; // scale
    
    float distort = superclamp(iGlobalTime, 18., 18.2) + 
        superclamp(iGlobalTime, 18.3, 18.55) + 
        superclamp(iGlobalTime, 20.33, 20.5) + 
        superclamp(iGlobalTime, 20.9, 21.) * 0.5 + 
        superclamp(iGlobalTime, 21.07, 21.12) * 0.5
        
        ;
    p *= 1. + rand1d(distort) * 0.4 * distort;
    p.y += rand1d(distort) * 0.4 * distort;
    p.z -= rand1d(distort) * 0.8 * distort;
    
    // p.z += 10.;
    // p.z -= time() * 2.;
    // pR45(p.xy);
    
    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(0, 0, 0, 0),
		// vec4(p.z * ((0.3 * opening2) + opening), 0, 0, 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, 0, 1)
	);

    // mat4 matrix2 = mat4(
	// 	vec4(cos(p.y + iGlobalTime), 0, sin(p.y + iGlobalTime), 0),
	// 	vec4(0, 0, 0, 0),
	// 	// vec4(p.z * ((0.3 * opening2) + opening), 0, 0, 0),
	// 	vec4(-sin(p.y + iGlobalTime), 0, cos(p.x + iGlobalTime), 0),
	// 	vec4(0, 0, 0, 1)
	// );

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


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

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

    // c += clamp(vec3(0., sphere2 / sphere, 0.), 0., 1.) * 0.2;

    return vec4(c, sphere);
    // return fBox(p, vec3(1.0));

	// return length(max(abs(matrix * vec4(p, 0.0)) - 1.0, 0.0)) - 0.1;
}

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 c = p.x / 100.;
    c = clamp(c, 0., 1.);
    
    return c;
}

float bgScene(in vec3 p)
{
    float delayedTime = max(0., time() - 6.);
    
    p.x -= 0.;
    p.z -= 3.;
    
    pR(p.xy, delayedTime * 0.2);
    pR(p.zy, delayedTime * 0.2);
    
    return fBox(p, vec3(2., 2., 8.));
}

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

	vec2 uv = (fragCoord.xy - (iResolution.xy * 0.5)) / iResolution.yy;
    // uv.x += 0.5;
    // pR45(uv.xy);
    
    // fx
    float scaleN = max(.0, 0.15 - doublet(-0.3));
    uv *= 1. + scaleN * 1.2; // scale
    float shakeN = max(.0, 0.1 - doublet(-0.1));
    // screenPos += (vec2(rand1d(iGlobalTime), rand1d(iGlobalTime + 1.0)) - 0.5) * 0.5 * shakeN; //shake
    
	vec3 rayStart = vec3(uv, DISTANCE);
	vec3 rayDir = vec3(uv, -1.2);
    vec3 rayDir_bg = vec3(uv, -1.2);

	vec3 p;
    vec3 p_bg;
    
	vec4 t = vec4(0.0);
    vec4 t_bg = vec4(0.0);

	for (int i = 0; i < RM_MAX_ITER; i++)
	{
		p = rayStart + rayDir * t.a;
        p_bg = rayStart + rayDir_bg * t_bg.a;

		t += scene(p);
        t_bg += bgScene(p_bg);
	}

	vec4 value = 1.0 - (t * (t / 2.0)) * 0.0005;
    vec4 value_bg = 1.0 - (t_bg * (t_bg / 2.0)) * 0.0005;

	vec3 normal = getNormal(p);
    vec3 normal_bg = getNormal(p_bg);
	vec3 light = vec3(0, 2, 10);
    vec3 light_bg = vec3(0, 2, 10);
	// vec3 color = vec3(0.8, 0.8, 0.8);
	vec3 color = t.rgb * 0.1;
    vec3 color_bg = t_bg.rgb * 0.1;

	vec3 N = normalize(normal);
    vec3 N_bg = normalize(normal_bg);
	vec3 V = normalize(rayDir);
    vec3 V_bg = normalize(rayDir_bg);
	vec3 R = reflect(V, N);
    vec3 R_bg = reflect(V_bg, N_bg);
	vec3 L = normalize(light);
    vec3 L_bg = normalize(light_bg);

	vec3 ambient = color * vec3(value.a);
    vec3 ambient_bg = color_bg * vec3(value_bg.a);
	vec3 diffuse = color * value.a * max(dot(L, N), 0.0);
    vec3 diffuse_bg = color_bg * value_bg.a * max(dot(L_bg, N_bg), 0.0);
	vec3 specular = vec3(value.a) * pow(max(dot(R, L), 0.0), 5.0) * 0.3;
    vec3 specular_bg = vec3(value_bg.a) * pow(max(dot(R_bg, L_bg), 0.0), 5.0) * 0.3;
	
    vec4 blob = vec4(ambient + diffuse + specular, 1.0) * 1.0;
    vec4 blob_bg = vec4(ambient_bg + diffuse_bg + specular_bg, 1.) * 1.;
    
    // float x = fragCoord.x / iResolution.x - 0.5;
    // float y = fragCoord.y / iResolution.y - 0.5;
    // x *= 1350.0;
    // y *= 1.0;
    // float n = time() * 0.5;
    // float d = cos(n + (x*tan(y)) * y * 0.01) * 0.2;
    // vec4 pulse = vec4(1.0-d, 1.0-d, 1.0-d, 1.0);

    // blob.a = clamp(blob.a, 0., 1.);
    blob = clamp(blob, 0., 1.);
    
    float fadeIn = 5.8 - min(5.8, (iGlobalTime * 2.8) + uv.y);
    blob -= fadeIn * 7.;
    blob = clamp(blob, 0., 1.);
    
    // vec3 bgColor = vec3(0.25, 0.56, 0.96) * 0.4;
    
    vec3 bgColor = vec3(bgColor(p));
    bgColor += blob_bg.rgb;
    bgColor = clamp(bgColor, 0., 1.);
    
    // t.a /= (blob.r + blob.g + blob.b) / 3.;
    // bgColor *= clamp(t.a - 57., 0., 1.);


    fragColor = blob + vec4(bgColor, 1.);
    
    // fragColor /= pulse / 4. + 0.75;
	// fragColor = pulse - blob;
    // fragColor += vec4(0.5);
    
    // fragColor = sqrt(fragColor);
    
    // // clip to white
    // fragColor = clamp(fragColor, 0., 1.);
    // fragColor += (clamp(uv.x, 0.9, 2.) - 0.9) * 1000.;
    // fragColor = clamp(fragColor, 0., 1.);
    
    // float test = max(0., 1. - sin(time(3.0) * 2.)) * 0.05;
    // fragColor -= test;

    float startFlash = 0.05 - min(0.05, iGlobalTime);
    fragColor += startFlash * 100.;
}