in vec2 uv;

out vec4 C;

uniform float u_t;
uniform float u_bpm;
uniform vec3 bg_col;
uniform vec3 main_col;
uniform vec3 hl_col;
uniform vec3 silent_col;
uniform int tex;

const vec2 ep=vec2(.02,-.02);
const float far=10.;
const int STEPCNT=32;
const int SAMPLES=3;
const float K=1.5;
const float IOR=1.2;
const float PI=acos(-1.);
const float TAU=PI*2.;
const float BEAT_DUR=60./u_bpm;

#include scene_nordlicht24/jy-sdf-font.glsl
#include scene_nordlicht24/rand.glsl

struct Material{
    float dist;
    bool refractive;
    vec3 al;
};

//float rand(vec3 p){return dot(fract(sin(p*849.163)*636.164),fract(sin(p*734.845)*936.368));}
float Bayer2(vec2 a) {
    a = floor(a);
    return fract(a.x / 2. + a.y * a.y * .75);
}

#define Bayer4(a)   (Bayer2 (.5 *(a)) * .25 + Bayer2(a))
const float time=u_t-Bayer4(gl_FragCoord.xy)/50.0;

float ease(float n){return smoothstep(0.,1.,smoothstep(0.,1.,n));}
float ease2(float n){return ease(ease(n));}
// https://www.shadertoy.com/view/lctGRB
mat3 orthBas(vec3 z) {
    z = normalize(z);
    vec3 up = abs(z.y) < 0.99 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, 0.0, 1.0);
    vec3 x = normalize(cross(up, z));
    return mat3(x, cross(z, x), z);
}
float y2kstar(vec2 p, float r){
    vec2 q=abs(p*2.);
    vec2 s=q.y>q.x?q.yx:q.xy;
    return (max(q.x,q.y)>1.?
    length(s-vec2(1.,0.)):
    1.-length(q-1.)
    )-r;
}
float y2kstar3(vec3 p, float r){
    return max(y2kstar(p.xy,r),abs(p.z)-.5);
}
float sdSegment( in vec2 p, in vec2 a, in vec2 b )
{
    vec2 pa = p-a, ba = b-a;
    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length( pa - ba*h );
}
float letter(vec3 p, int ascii)
{
    p*=3.;

    float accum=9999.;
    for(int j=0;j<SEGMENTS.length();j++){
        if((ascii_to_bitmask(ascii)&(1<<j))!=0){
            accum=min(accum,sdSegment(p.xy,SEGMENTS[j][0],SEGMENTS[j][1])-.3);
        }
    }
    
    return max(accum,abs(p.z)-.75);
}
float scrollingName(vec3 p){
    float i_t=floor(time*.125/BEAT_DUR),
    f_t=fract(time*.125/BEAT_DUR);
    
    p.x+=time+(i_t+ease2(f_t))*36.;
    p.y+=4.5;
    p.xy*=.2;
    vec2 i_p=floor(p.xy);
    vec2 f_p=fract(p.xy); f_p-=.5; f_p*=3.5;
    
    ivec2 ind=ivec2( mod(i_p.x+i_p.y*5.,18.), i_p.y+4.);

    int texResult = imageLoad(i32r_images[tex], ind).x;

    if(ind.x>=18 || ind.x<0 || ind.y>=10 || ind.y<0){
        return 1.;
    }
    if(texResult==-1){
        return 1.;
    }
    return letter(vec3(f_p,p.z)*.3,texResult);
}
Material backboard(vec3 p){
    vec3 q=p*.05+vec3(.3,.06,0)*time;
    vec3 tex=mix(main_col,hl_col,step(1.,mod(floor(q.x)+floor(q.y),2.)));
    return Material(-p.z, false, tex);
}
float scrollingStars(vec3 p){
    vec3 q=p;
    q.xy*=.05;
    q.y-=step(1.,mod(floor(q.x),2.))*.5;
    vec2 i_q=floor(q.xy);
    vec2 f_q=fract(q.xy); f_q-=.5; f_q*=2.;
    
    float i_t=floor((time+i_q.x)*.5/BEAT_DUR),
    f_t=fract((time+i_q.x)*.5/BEAT_DUR);
    
    f_q*=rot(mix(rand(vec3(i_q,i_t)),rand(vec3(i_q,i_t+1.)),ease2(f_t))*TAU);
    
    return y2kstar3(vec3(f_q*.8,q.z), 0.1);
}

Material getDist(vec3 p){
    const float PREV_END=160.;
    float i_t=floor(time/BEAT_DUR);
    Material objs[] = Material[](
    Material(scrollingName(p-vec3(0,0,3)),i_t<PREV_END+64.-2.,silent_col),
    backboard(p-vec3(0,0,10)),
    Material(scrollingStars(p-vec3(0,0,8)),false,hl_col)
    );
    Material m;
    m.dist=far;
    for(int i=0; i<objs.length(); i++){
        Material o=objs[i];
        if(o.dist<m.dist){
            m=o;
        }
    }
    return m;
}
vec3 getNorm(vec3 p){
    return normalize(ep.xyy*getDist(p+ep.xyy).dist+ep.yyx*getDist(p+ep.yyx).dist+
    ep.yxy*getDist(p+ep.yxy).dist+ep.xxx*getDist(p+ep.xxx).dist);
}
vec3 pathtrace(vec2 p) {
    vec3 cd=vec3(vec2(cos(time),sin(time))*.2,1), 
         ro=orthBas(cd)*vec3(p*30.,-10),
         rd=normalize(orthBas(cd)*vec3(0,0,1)),
         ld=normalize(vec3(-1.,1.,-.5)),
         po=ro+rd*3.5,
         att=vec3(1),
         co=bg_col;
    float side=1.;
    
    Material m;
    float t;
    for(int i=0; i<SAMPLES; i++){
        t=0.;
        bool hit=false;
        for(int j=0; j<STEPCNT; j++){
            m=getDist(po);
            m.dist*=side;
            t+=m.dist*-side;
            po+=rd*m.dist;
            if(m.dist<ep.x){
                hit=true;
                break;
            }
            if(po.z>far){
                break;
            }
        }
        if(hit){
            vec3 no=getNorm(po)*side;
            att*=m.al;

            if(side<0.){
                att*=exp(-t*m.al*K);
            }
            else{
                co=m.al;
                co=mix(co,bg_col,pow(dot(ld,reflect(rd,no))*.5+.5,10.));
            }

            if(m.refractive){
                vec3 ref=refract(rd,no,side>0.?IOR:(1./IOR));
                rd=length(ref)<ep.x?reflect(rd,no):ref;
                side*=-1.;
                po-=no*ep.x*3.;
            }
        }
    }
    return clamp(co, 0., 1.);
}

void main() {
    vec2 UV = vec2(uv.x*R.x/R.y, uv.y);
    C = vec4(pathtrace(UV), 1.0);
}
