struct Fragment
{
    float depth;
    vec3 diffuse;
};

mat3 lookAt(vec3 p, vec3 t, vec3 u)
{
    vec3 z = normalize(t - p);
    vec3 x = normalize(cross(z, u));
    vec3 y = normalize(cross(x, z));
    return mat3(x, y, z);
}

bool less(vec3 a, vec3 b)
{
    if(a.x < b.x && a.y < b.y && a.z < b.z)
        return true;
    else return false;
}

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

float sphereB(vec3 p, float r, vec3 c)
{
    return length(p) - r - c.r*0.01;
}

Fragment minF(Fragment a, Fragment b)
{
    //ig's line ;)
    if( a.depth < b.depth ) return a; return b;
}

Fragment map(vec3 p)
{
    vec2 tc = p.xy + vec2(iTime*0.05, iTime*0.01);
    vec3 t = texture(iChannel0, tc).xyz;
    vec3 c = vec3(0.8, 0.4+0.07*sin(p.x*3.0)*sin(-p.x+p.y*10.0), 0.2+0.07*sin(p.x*3.0)*sin(-p.x+p.y*10.0));

    vec3 t2 = texture(iChannel0, tc / 3.0).xyz;
    vec3 c2 = vec3(t2.r, t2.r*0.3, 0.0) * t2*2.0;

    float size = iTime * 0.2;

    Fragment f;
    f = Fragment(sphereB(p-vec3(1.2, 0.3, 0.0), size < 1.0 ? size : 1.0, t*2.0),						c	);
    f = minF(Fragment(sphereB(p-vec3(-2.5, -1.5, 2.0), 2.0, t2*2.0),	c2), f);
    return f;
}

Fragment ray(vec3 ro, vec3 rd)
{
    float depth = 0.0;
    for(int i = 0; i < 128; i++)
    {
        Fragment f = map(ro + rd * depth);
        depth += f.depth;
        if(f.depth < 0.01)
        {
            f.depth = depth;
            return f;
        }
    }

    return Fragment( -1.0, vec3(0.0) );
}

vec3 normal(vec3 p)
{
    vec2 t = vec2(0.01, 0.0);
    return normalize(
        vec3(
            map(p+t.xyy).depth - map(p-t.xyy).depth,
            map(p+t.yxy).depth - map(p-t.yxy).depth,
            map(p+t.yyx).depth - map(p-t.yyx).depth
            )
        );
}

vec3 render(vec3 ro, vec3 rd, vec2 uv)
{
    vec3 color;
    vec3 ambient = vec3(0.05, 0.05, 0.05);
    vec3 lightPos = vec3(0.0, 0.0, 1.0);

    Fragment f = ray(ro, rd);
    if(f.depth < 0.0)
    {
        vec4 tex = texture(iChannel0, uv*2.5);
        color = vec3(1.0, 0.8, 0.0)-vec3(length(vec2(uv.x+0.3, uv.y-0.1)/2.0+0.6));

        return color + vec3(pow(tex.x, 20.0));
    }

    vec3 p = ro + rd * f.depth;

    vec3 n = normal(p);

    float diffuse = max(0.0, 0.8+dot(n, -normalize(p-lightPos)));

    color = f.diffuse * diffuse;

    return color + ambient;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 p = 2.0 * fragCoord / iResolution.xy - 1.0;
    p.x *= iResolution.x / iResolution.y;

    vec3 cp = vec3(0.0, 1.0, 5.0);
    vec3 ct = vec3(0.0, 0.0, 0.0);
    vec3 cu = vec3(0.0, 1.0, 0.0);
    mat3 cam = lookAt(cp, ct, cu);

    vec3 rd = cam * normalize(vec3(p, 2.5));

    fragColor = vec4(render(cp, rd, p), 1.0);
}
