#version 120

#define MAX_DIST 1000.
#define MIN_DIST 0.001
#define MAX_STEPS 1000

uniform float iTime;

uniform float camPos_x = 0;
uniform float camPos_y = 1;
uniform float camPos_z = -5;

uniform sampler2D backgroundImage;


vec3 getColor(vec3 a, vec3 b, vec3 c, vec3 d, float t)
{
    return a + b * cos(6.28318 * (c * t + d));
}

float hash(vec3 p)
{
	p = fract(p * .1031);
    p += dot(p, p.zyx + 31.32);
    return fract((p.x + p.y) * p.z);
}

float smin(float a, float b, float k)
{
    float h = max(k - abs(a - b), 0.0);
    return min(a, b) - h * h * 0.25 / k;
}

float smax(float a, float b, float k)
{
    float h = max(k - abs(a - b), 0.0);
    return max(a, b) + h * h * 0.25 / k;
}

vec2 minX(vec2 a, vec2 b)
{
    if (a.x < b.x) return a;
    return b;
}

float subtract(float a, float b)
{
    return max(-a, b);
}

float sphere(vec3 p, vec3 c, float r)
{
    return distance(p, c) - r;
}

float box(vec3 p, vec3 b)
{
  vec3 q = abs(p) - b;
  return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}

float sphere_rand(vec3 ip, vec3 fp, vec3 c)
{
    float r = hash(ip + c) * 0.5;
    
    return length(fp - c) - r;
}

float spheres(vec3 p)
{
    vec3 pFl = floor(p);
    vec3 pFr = fract(p);
    
    float s = 
    min(sphere_rand(pFl, pFr, vec3(0., 0., 0.)),
    min(sphere_rand(pFl, pFr, vec3(0., 0., 1.)),
    min(sphere_rand(pFl, pFr, vec3(0., 1., 0.)),
    min(sphere_rand(pFl, pFr, vec3(0., 1., 1.)),
    min(sphere_rand(pFl, pFr, vec3(1., 0., 0.)),
    min(sphere_rand(pFl, pFr, vec3(1., 0., 1.)),
    min(sphere_rand(pFl, pFr, vec3(1., 1., 0.)),
    sphere_rand(pFl, pFr, vec3(1., 1., 1.)))))))));

    return s;
}

float box_rand(vec3 ip, vec3 fp, vec3 c)
{
    float h = hash(vec3(ip.x, 0., ip.z) + vec3(c.x, 0., c.z));
    float r = h * 0.5;
    c.y = sin(iTime + (h * 12.));
    float co = cos(iTime * h);
    float s = sin(iTime * h);
    
    mat3 rotX = mat3(vec3(1., 0., 0.),
                     vec3(0., co, -s),
                     vec3(0., s, co));

    mat3 rotZ = mat3(vec3(co, s, 0.),
                     vec3(-s, co, 0.),
                     vec3(0., 0., 1.));
                     
    mat3 rotY = mat3(vec3(co, 0., -s),
                     vec3(0., 1., 0.), 
                     vec3(s, 0., co));
    
    return box((fp - c) * rotX * rotY * rotZ, vec3(r)) - 0.01; //låda med rundade kanter
}

float boxes(vec3 p)
{
    vec3 pFl = floor(p);
    vec3 pFr = fract(p);
    
    float s = 
    smin(box_rand(pFl, pFr, vec3(0., 0., 0.)),
    smin(box_rand(pFl, pFr, vec3(0., 0., 1.)),
    smin(box_rand(pFl, pFr, vec3(0., 1., 0.)),
    smin(box_rand(pFl, pFr, vec3(0., 1., 1.)),
    smin(box_rand(pFl, pFr, vec3(1., 0., 0.)),
    smin(box_rand(pFl, pFr, vec3(1., 0., 1.)),
    smin(box_rand(pFl, pFr, vec3(1., 1., 0.)),
    box_rand(pFl, pFr, vec3(1., 1., 1.)), 0.3), 0.3), 0.3), 0.3), 0.3), 0.3), 0.3);
    
    return s;
}

float booxes(vec3 p)
{
    vec3 pFl = vec3(floor(p.x), 0., floor(p.z));
    vec3 pFr = vec3(fract(p.x), p.y, fract(p.z));
    
    float s = 
    smin(box_rand(pFl, pFr, vec3(0., 0., 0.)),
    smin(box_rand(pFl, pFr, vec3(0., 0., 1.)),
    smin(box_rand(pFl, pFr, vec3(1., 0., 0.)),
    box_rand(pFl, pFr, vec3(1., 0., 1.)), 0.3), 0.3), 0.3);
    
    
    
    return s;
}

float terrain(vec3 p, float d)
{
    mat3 m = mat3(vec3(1.93, 0.39, -0.3),
                vec3(-0.3, 1.9, 0.5),
                vec3(0.38, -0.44, 1.91));
   
   //float t = iTime / 2. + 0.0001;
   
   //int floorTime = int(floor(t));
   
   float s = 1.;
   for(int i = 0; i <= 3; i++)
   {
       //s *= min(1., t - float(i));
       //s *= max(0.0001, sin(iTime) / 2. + 0.5);
       
       //float boox = boxes(p);
       //float sfeer = spheres(p);
       
       //float slask = subtract(sfeer, boox);
       
       float n = s * /*slask;*/ boxes(p);//spheres(p);
	
       n = smax(n, d - 0.5 * s, 0.5 * s);
       d = smin(n, d, 0.15 * s);
	       
       p = m * p;
       
       s = 0.25 * s;
       
   }
   
   return d;
}



float plane(vec3 p, vec3 n, float h)
{
    return dot(p, normalize(n)) + h;
}

vec2 map(vec3 p)
{
    float pln = plane(p, vec3(0., 1., 0.), 1.0);
    //float b = box(p - vec3(0., 0., 5.), vec3(.5));
    float o = terrain(p, pln);
    
    //float o = booxes(p);
    //float o = min(b, t);//terrain(p, pln);
    //float o = spheres(p);
    
    //o = boxes(p + vec3(0.7, 0.,1.6));
    
    //float co = cos(iTime * 2.);
    //float s = sin(iTime * 2.);
    
    /*mat3 m =mat3(
        vec3(co, -s, 0),
        vec3(s, co, 0),
        vec3(0, 0, 1)
    );
    
    o = box((p - vec3(0.5, -0.25, 0.5)) * m, vec3(0.25));*/
    
    return vec2(o, 2);
    // return vec2(pln, 2);
}

float marchShadow(vec3 ro, vec3 lightP)
{
    vec3 p = ro;
    float travel = 0.1;
    float hit = 0.;
    
    vec3 pToLight = lightP - ro;
    float maxDist = length(pToLight);
    vec3 rd = normalize(pToLight);
    float o = 1.0;
    
    for (int i = 0; i < MAX_STEPS; i++)
    {
        p = ro + rd * travel;
        hit = map(p).x;// / 2.;
        travel += hit;
        
        o = min(o, 50. * hit / travel);
        
        if (abs(hit) <= MIN_DIST)
        {
            return 0.;
        }
        if (travel > maxDist) { break; }
    }
    
    return o;
}

vec3 phong(vec3 p, vec3 n, vec3 cam, vec3 light, vec3 diffuse, float spec, float shine)
{
    vec3 L = normalize(light - p);
    vec3 C = normalize(cam - p);
    
    float LN = max(0., dot(L, n));
    
    vec3 d = diffuse * LN;
    float s = 0.;
    
    if (LN > 0.)
    {
        vec3 R = -normalize(reflect(L, n));
        s = spec * pow(max(0., dot(R, C)), shine);
    }

    return vec3(d + s);
}

vec3 calcNormal(vec3 p)
{
    vec3 smallStep = vec3(0.00001, 0.0, 0.0);

    float x = map(p + smallStep.xyy).x - map(p - smallStep.xyy).x;
    float y = map(p + smallStep.yxy).x - map(p - smallStep.yxy).x;
    float z = map(p + smallStep.yyx).x - map(p - smallStep.yyx).x;

    return normalize(vec3(x, y, z));
}

void main()
{

    vec2 iResolution = vec2(1920., 1080.);

// oskar
    //vec2 uv = gl_TexCoord[0].xy / iResolution.xy * 2. - 1.;
    //uv.x *= iResolution.x / iResolution.y;

// albin
    vec2 uv = gl_TexCoord[0].xy  * 2. - 1.;
    uv.x *= iResolution.x / iResolution.y;
    
    //vec3 camPos = vec3(0. ,-2.5, -10.);// + vec3(0., 0., iTime / 2.);
    vec3 camPos = vec3(camPos_x, camPos_y, camPos_z);// + vec3(0., 0., iTime / 2.);
    vec3 lookAt = camPos + vec3(0., 0., -1.);
    //vec3 lookAt = vec3(0., 0., 1.);
    
    vec3 forward = normalize(lookAt - camPos);
    vec3 right = normalize(vec3(forward.z, 0., -forward.x));
    vec3 up = normalize(cross(forward, right));
    
    float fov = 1080. / 1920.;
    float slask = 0.;
    vec3 rayOrigin = camPos;
    vec3 rayDir = normalize(forward + fov * uv.x * right + fov * uv.y * up);
    
    vec3 lightPos = camPos + vec3(-3.5, 3.5, -0.2);// + vec3(0. , 0., iTime + slask);
    int id = -100;
    
    float traveled = 0.;
    vec3 p = vec3(0.);
    
    for (int i = 0; i < MAX_STEPS; i++)
    { 
        p = camPos + rayDir * traveled;// + vec3(0., 0., iTime + slask);
        vec2 hit = map(p);
        traveled += hit.x;
        
        if (hit.x <= MIN_DIST)
        {
            id = int(hit.y);
            break;
        }
        if (hit.x > MAX_DIST)
        {
            break;
        }
    }
    
    vec3 col = texture2D(backgroundImage, gl_TexCoord[0].xy).rgb;

    if (id != -100) {
        vec3 n = calcNormal(p);
        //col = vec3(0.25, 0., 0.15);
        //col.y += smoothstep(0., 1., n.y - 0.5) * 0.7;
        //col = mix(vec3(0.34, 0., 0.5), vec3(0.,0.52,0.52), n.y);
        //col += mix(vec3(0.12, 0.34, 0.33), vec3(0.5,0.2,-0.4), p.y + 0.5);
        float q = n.y + iTime / 3.;
        
        col = getColor(vec3(0.5), vec3(0.5), vec3(1.), vec3(0., 0.333, 0.667), q);
        
        col = phong(p, n, camPos, lightPos, col, 0.3, 64.);
        col *= vec3(pow(2.42, -0.0015 * traveled));
        //float shadow = marchShadow(p, lightPos);
        //col *= shadow;
       }

    gl_FragColor = vec4(col, 1.0);
}