// CC0: Saturday weirdness
//  I saw a tweet from Kali where I thought I understood how he did something cool.
//  Turns out I didn't understand . Instead ended up with weird stuff.

in vec2 texCoord;

uniform float iTime;
uniform sampler2D iChannel0;
uniform float testvalue;

#define TIME        iTime
#define RESOLUTION  iResolution
#define PI          3.141592654
#define TAU         (2.0*PI)

#define ROT(a)      mat2(cos(a), sin(a), -sin(a), cos(a))





// CC0: Saturday weirdness
//  I saw a tweet from Kali where I thought I understood how he did something cool.
//  Turns out I didn't understand . Instead ended up with weird stuff.

// License: WTFPL, author: sam hocevar, found: https://stackoverflow.com/a/17897228/418488
const vec4 hsv2rgb_K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 hsv2rgb(vec3 c) {
  vec3 p = abs(fract(c.xxx + hsv2rgb_K.xyz) * 6.0 - hsv2rgb_K.www);
  return c.z * mix(hsv2rgb_K.xxx, clamp(p - hsv2rgb_K.xxx, 0.0, 1.0), c.y);
}
// License: WTFPL, author: sam hocevar, found: https://stackoverflow.com/a/17897228/418488
//  Macro version of above to enable compile-time constants
#define HSV2RGB(c)  (c.z * mix(hsv2rgb_K.xxx, clamp(abs(fract(c.xxx + hsv2rgb_K.xyz) * 6.0 - hsv2rgb_K.www) - hsv2rgb_K.xxx, 0.0, 1.0), c.y))

#define TOLERANCE           0.0001
#define MAX_RAY_LENGTH      10.0
#define MAX_RAY_MARCHES     80
#define NORM_OFF            0.005

const vec3 sunCol    = HSV2RGB(vec3(0.7, 0.95, 1E-2));
const vec3 sunDir    = normalize(vec3(0.,0., 1.));

const vec3 lightCol0 = HSV2RGB(vec3(0.65, 0.85, 1.0));
const vec3 lightPos0 = 4.0*vec3(1.0, 1.0, -2.0);

const vec3 lightCol1 = HSV2RGB(vec3(0.65, 0.65, 1.0));
const vec3 lightPos1 = 2.0*vec3(-1.0, -1.0, -2.0);
  
  
const vec3 bottomBoxCol = HSV2RGB(vec3(0.7, 0.80, 0.5));
const vec3 topBoxCol    = HSV2RGB(vec3(0.57, 0.90, 1.));
  
// License: MIT, author: Inigo Quilez, found: https://iquilezles.org/articles/noacos/
mat3 rot(vec3 d, vec3 z) {
  vec3  v = cross( z, d );
  float c = dot( z, d );
  float k = 1.0/(1.0+c);

  return mat3( v.x*v.x*k + c,     v.y*v.x*k - v.z,    v.z*v.x*k + v.y,
               v.x*v.y*k + v.z,   v.y*v.y*k + c,      v.z*v.y*k - v.x,
               v.x*v.z*k - v.y,   v.y*v.z*k + v.x,    v.z*v.z*k + c    );
}


// License: Unknown, author: Matt Taylor (https://github.com/64), found: https://64.github.io/tonemapping/
vec3 aces_approx(vec3 v) {
  v = max(v, 0.0);
  v *= 0.6f;
  float a = 2.51f;
  float b = 0.03f;
  float c = 2.43f;
  float d = 0.59f;
  float e = 0.14f;
  return clamp((v*(a*v+b))/(v*(c*v+d)+e), 0.0f, 1.0f);
}

// License: MIT, author: Inigo Quilez, found: https://iquilezles.org/articles/intersectors/
float rayPlane(vec3 ro, vec3 rd, vec4 p) {
  return -(dot(ro,p.xyz)+p.w)/dot(rd,p.xyz);
}

// License: MIT, author: Inigo Quilez, found: https://iquilezles.org/articles/distfunctions/
float box(vec2 p, vec2 b) {
  vec2 d = abs(p)-b;
  return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
  

mat3 g_rot;
mat3 g_rot2;
float h_=0.0;
float opUsc( float d1, float d2, float k ) {
    float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
    return (mix( d2, d1, h) - k*h*(1.0-h)); 
}

float sphere4(vec3 p, float r) {
  p*=p;
  return pow(dot(p,p), 0.25) -r;
}

float df(vec3 p) {
  //vec3 op = p;
  vec3 p2=p+vec3(0.75,0,0.5);
  p=p-vec3(0.5,0,-1.1);
  p2 *= g_rot2;
  p *= g_rot;
  return opUsc(sphere4(p, 1.),sphere4(p2,1.),0.15);
}

#define BACKSTEP
float rayMarch(vec3 ro, vec3 rd, float tinit, out int iter) {
  float t = tinit;
  const float tol = TOLERANCE;
#if defined(BACKSTEP)
  vec2 dti = vec2(1e10,0.0);
#endif  
  int i = 0;
  for (i = 0; i < MAX_RAY_MARCHES; ++i) {
    float d = df(ro + rd*t);
#if defined(BACKSTEP)
    if (d<dti.x) { dti=vec2(d,t); }
#endif  
    if (d < TOLERANCE || t > MAX_RAY_LENGTH) {
      break;
    }
    t += d;
  }
#if defined(BACKSTEP)
  if(i==MAX_RAY_MARCHES) { t=dti.y; };
#endif  
  iter = i;
  return t;
}


vec3 normal(vec3 pos) {
  vec2  eps = vec2(NORM_OFF,0.0);
  vec3 nor;
  nor.x = df(pos+eps.xyy) - df(pos-eps.xyy);
  nor.y = df(pos+eps.yxy) - df(pos-eps.yxy);
  nor.z = df(pos+eps.yyx) - df(pos-eps.yyx);
  return normalize(nor);
}

vec3 render0(vec3 ro, vec3 rd) {
  vec3 col = vec3(0.0);
  
  vec3 ld0 = normalize(lightPos0-ro);
  vec3 ld1 = normalize(lightPos1-ro);

  float tp0  = rayPlane(ro, rd, vec4(vec3(0.0, -1.0, 0.0), -5.0));
  float tp1  = rayPlane(ro, rd, vec4(vec3(0.0, -1.0, 0.0), 6.0));

//if (false)
  if (tp0 > 0.0) {
    col += bottomBoxCol*exp(-0.5*(length((ro + tp0*rd).xz)));
  }

//if (false)
  if (tp1 > 0.0) {
    vec3 pos  = ro + tp1*rd;
    vec2 pp = pos.xz;
    float db = box(pp, vec2(5.0, 9.0))-3.0;
    
    col += topBoxCol*rd.y*rd.y*smoothstep(0.25, 0.0, db);
    col += 0.2*topBoxCol*exp(-0.5*max(db, 0.0));
    col += 0.05*sqrt(topBoxCol)*max(-db, 0.0);
  }

  col += 1E-2*lightCol0/(1.002-dot(ld0, rd));
  //col += 2E-2*lightCol1/(1.005-dot(ld1, rd));
  col += sunCol/(1.001-dot(sunDir, rd));
  return col; 
}

vec3 render1(vec3 ro, vec3 rd) {
  
  int ii;
  float t = rayMarch(ro, rd, 0., ii);

  vec3 col = render0(ro, rd);
  
  if (t < MAX_RAY_LENGTH) {
    vec3 p = ro+rd*t;
    p*=0.66; // dm intervention zoomin for the texture
    vec3 n = normal(p);
    vec3 r = reflect(rd, n);
    vec3 rcol = render0(p, r);
    float fre = 1.0+dot(rd,n);
    fre *= fre;
    fre = mix(0.5, 1.0, fre);
    vec3 ld0 = normalize(lightPos0-p);
    vec3 ld1 = normalize(lightPos1-p);
    
    float dif0 = pow(max(dot(ld0, n), 0.), 4.0)*0.5;
    float dif1 = pow(max(dot(ld1, n), 0.), 4.0)*0.5;

    col = vec3(0.);
    col += dif0*lightCol0;
    col += dif1*lightCol1;
    col += rcol*fre;
    vec2 p2 = p.xy*n.z+p.xz*n.y+p.zy*n.x;
    p2 *= 1.-0.3;
    p2 *= ROT(-20.*length(p2));
    //p2.x *= RESOLUTION.y/RESOLUTION.x;
    vec2 tp = 0.5+0.5*p2;
    
    tp*=1.2;
    float time=iTime/5.0;
    tp+=vec2(time,time/2.0);

    vec4 pcol = texture(iChannel0, tp);
    col += smoothstep(vec3(0.2, 0.25, 0.5), vec3(1.75, 1.6, 1.4), pcol.xyz);
  }
  
  return col;
}


//void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
void main(){
  vec2 q = texCoord;//fragCoord/RESOLUTION.xy;
  vec2 p = -1. + 2. * q;
  p.x *= 1920.0/1080.0;
  
  float a = TIME*1.0;
  float a2 = TIME*1.1+1000.0;
  vec3 r0 = vec3(1.0, sin(vec2(sqrt(0.5), 1.0)*a));
  vec3 r1 = vec3(cos(vec2(sqrt(0.5), 1.0)*a), 1.0);
  g_rot = rot(normalize(r0), normalize(r1));
  vec3 r02 = vec3(1.0, sin(vec2(sqrt(0.5), 1.0)*a2));
  vec3 r12 = vec3(cos(vec2(sqrt(0.5), 1.0)*a2), 1.0);
  g_rot2 = rot(normalize(r02), normalize(r12));

  const vec3 up = vec3(0., 1., 0.);
  vec3 ro   = vec3(0.0, 0.5, -3.0);
  vec3 la   = vec3(0.0);

  vec3 ww = normalize(la-ro);
  vec3 uu = normalize(cross(up, ww));
  vec3 vv = cross(ww, uu);
  vec3 rd = normalize(p.x*uu + p.y*vv + 2.*ww);
  rd.z-=0.52*1.37;
  rd.x-=0.5;
  //rd.y-=0.17;
  rd.y-=0.215;

  vec3 col = vec3(0.0); 
  col = render1(ro, rd);
  col -= 4E-2*vec3(1.,2.,3.).zyx*(length(p)+0.25); 
  col = aces_approx(col);
  col = sqrt(col)*1.2;
 
  vec4 fragColor = vec4(col, 1.0);
  gl_FragColor = fragColor;
}
