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

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;
    
    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, 0, 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 c = p.x / 100.;
    c = clamp(c, 0., 1.);
    
    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);
    
    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.4)) * (1. - lenfactor) * evenfactor;
    len *= 1. + lenfactor;
    
    p.z -= len;
    
    return fBox(p, vec3(1.5, 1.5, len));
}

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

	vec2 uv = (fragCoord.xy - (iResolution.xy * 0.5)) / iResolution.yy;
    // uv.x += 0.5;
    
    // 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 p;
    
	vec4 t = vec4(0.0);

	for (int i = 0; i < RM_MAX_ITER; i++)
	{
		p = rayStart + rayDir * t.a;
		t += scene(p);
	}

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

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

	vec3 N = normalize(normal);
	vec3 V = normalize(rayDir);
	vec3 R = reflect(V, N);
	vec3 L = normalize(light);

	vec3 ambient = color * vec3(value.a);
	vec3 diffuse = color * value.a * max(dot(L, N), 0.0);
	vec3 specular = vec3(value.a) * pow(max(dot(R, L), 0.0), 5.0) * 0.3;
	
    vec4 blob = vec4(ambient + diffuse + specular, 1.0) * 1.0;
    
    blob = clamp(blob, 0., 1.);
    
    
    //BG
    vec3 bg_cameraOrigin = vec3(.0, .0, 8.0) * 1.;
    vec3 bg_cameraTarget = vec3(.0, .0, .0);
    vec3 bg_upDirection = vec3(.0, 1.0, .0);
    vec3 bg_cameraDir = normalize(bg_cameraTarget - bg_cameraOrigin);
    vec3 bg_cameraRight = normalize(cross(bg_upDirection, bg_cameraOrigin));
    vec3 bg_cameraUp = cross(bg_cameraDir, bg_cameraRight);
    vec2 bg_screenPos = -1. + 2. * gl_FragCoord.xy / iResolution.xy;
    bg_screenPos.x *= iResolution.x / iResolution.y;
    bg_screenPos.y *= 1.5;
    vec3 bg_rayDir = normalize(bg_cameraRight * bg_screenPos.x + bg_cameraUp * bg_screenPos.y + bg_cameraDir);
    const float bg_MAX_DIST = 150.0;
    const float bg_EPSILON = 0.01;
    float bg_totalDist = 0.;
    vec3 bg_pos = bg_cameraOrigin;
    float bg_dist = bg_EPSILON;
    for(int i = 0; i < 200; i++)
    {
        if (bg_dist < bg_EPSILON || bg_totalDist > bg_MAX_DIST)
            break;

        bg_dist = bgScene(bg_pos);
        bg_totalDist += bg_dist;
        bg_pos += bg_dist * bg_rayDir;
    }
    vec3 bg_bgColor = vec3(0.25, 0.56, 0.96) * 0.6;
    vec3 bg_fadeColor = vec3(0.25, 0.56, 0.96) * 1.;
    vec4 bg_fragColor = vec4(0.);
    if(bg_dist < bg_EPSILON)
    {
        vec2 bg_eps = vec2(0.0, bg_EPSILON);
        vec3 bg_normal = normalize(vec3(
            bgScene(bg_pos + bg_eps.yxx) - bgScene(bg_pos - bg_eps.yxx),
            bgScene(bg_pos + bg_eps.xyx) - bgScene(bg_pos - bg_eps.xyx),
            bgScene(bg_pos + bg_eps.xxy) - bgScene(bg_pos - bg_eps.xxy)));
        
        float bg_diffuse = max(.0, dot(-bg_rayDir, bg_normal));
        float bg_specular = pow(bg_diffuse, 100.0);
        
        vec3 bg_color = vec3(bg_diffuse + bg_specular);
        bg_color = bg_color * normalize(bg_fadeColor) * 0.5 + bg_bgColor * 0.2 + bg_fadeColor * 0.4;
        
        bg_fragColor = vec4(bg_color, 1.0);
    }
    else
    {
        bg_fragColor = vec4(bg_bgColor, 1.);
    }
    
    
    
    float fadeIn = 5.8 - min(5.8, (iGlobalTime * 2.8) + uv.y);
    blob -= fadeIn * 7.;
    blob = clamp(blob, 0., 1.);
    
    
    vec3 bgColor = vec3(bgColor(p));
    bgColor = clamp(bgColor, 0., 1.);
    
    
    vec3 bgColor2 = bg_fragColor.rgb * bgColor;
    
    // t.a /= (blob.r + blob.g + blob.b) / 1.;
    // t.a = 0.;
    // bgColor *= clamp(t.a - 57., 0., 1.);
    
    
    fragColor = blob + vec4(bgColor2, 1.);

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