#version 330 core

uniform vec4 time;
uniform sampler2D tex0;
uniform vec4 opasity;
uniform vec4 twist;
uniform vec4 ofs;

uniform vec4 intensities;

in vec2 texcoord0;

layout(location = 0, index = 0) out vec4 out_color0;

vec3 render_tunnel( vec2 p )
{
	float a = atan(p.y,p.x);
	float r = sqrt(dot(p,p));

	//float t = ofs.x;

	vec2 uv;
	uv.x = ofs.x + 0.1f/ r;
	uv.y = a / 3.1416f;
	uv.x *= -2.0;

	uv.y += twist.x * sqrt(r);

	uv *= 2;

	vec3 col = texture(tex0, uv).xyz;

	return vec3( col*r*r * opasity.x );
}

////////////////////////////////////////////////////////
// https://www.youtube.com/watch?v=QCxTEkG5ILg&t=18m50s

float remap( float v, float i0, float i1, float o0, float o1 )
{
    return o0 + (o1-o0) * clamp( (v-i0)/(i1-i0), 0.0, 1.0 );
}

//note: uniform pdf rand [0;1[
float hash11n(float p)
{
	vec2 p2 = fract(vec2(p * 5.3987, p * 5.4421));
    p2 += dot(p2.yx, p2.xy + vec2(21.5351, 14.3137));
	return fract(p2.x * p2.y * 95.4307);
}
//note: uniform pdf rand [0;1[
vec2 hash22n(vec2 p)
{
	p  = fract(p * vec2(5.3987, 5.4421));
    p += dot(p.yx, p.xy +  vec2(21.5351, 14.3137));
	return fract(vec2(p.x * p.y * 95.4307, p.y * p.x * 97.5901));
}


//note: from https://www.shadertoy.com/view/ssGXDd
// uniformly random point on the edge of a unit circle
// produces 2d normal vector as well
vec2 uniform_circle_edge (float rnd) {
    float phi = 6.28318530718*rnd;
    return vec2(cos(phi),sin(phi));
}


// uniformly random point in unit circle
vec2 uniform_circle_area( vec2 rnd2 )
{
    return uniform_circle_edge(rnd2.x)*sqrt(rnd2.y);
}


vec2 rot2d( vec2 p, float v )
{
	vec2 sc = vec2(sin(v),cos(v));
	return vec2( dot( p, vec2(sc.y, -sc.x) ), dot( p, sc.xy ) );
}

vec3 lineits( vec2 p, int idx, float baserot, float in_time )
{
    float rnd0 = hash11n(float(idx));
    float rnd1 = hash11n(float(rnd0 + 81.0));
    float rnd2 = hash11n(float(rnd0 + 89.0));
    float rnd4 = hash11n(float(rnd0 + 1227.0));

    p = p * 2.0 - 1.0;

    float siz_deg = remap( rnd0*rnd0, 0.0, 1.0, 25.0, 5.0) * (3.14159265 / 180.0);

    //note: small ones move fast, big ones slow
    float rotspeed_s = remap( pow(rnd0, 0.25), 0.0, 1.0, 0.1, 1.0 );
    rotspeed_s *= (rnd4>0.5) ? 1.0 : -1.0;
    
    p = rot2d( p, baserot + rnd1 * 6.28 + rotspeed_s * in_time );

    //TODO: can be moved out
    float r = length( p );
    r = remap( r, 0.0, 1.46, 0.0, 1.0 );
    float v = 1.0 - pow(r, 0.5 );
    
    float a = acos( normalize(p).x );
    v *= remap( siz_deg - a, 0.0, 0.05, 0.0, 1.0 );

    v += pow( remap( r, 0.0, 0.3, 1.0, 0.0 ), 2.0);

    const vec3 c0 = vec3(1, 0.3, 0.8);
    const vec3 c1 = vec3(0.25, 0.5, 1);

    return v * mix( c0, c1, rnd2 );
}

vec3 renderlines( vec2 uv, float in_time )
{
    const int NUM_LINES = 6;
    vec3 its = vec3(0.0);
    for ( int i = 0; i<NUM_LINES; ++i )
    {
        vec3 its0 = lineits( uv, i, 0.0, in_time );
        vec3 its1 = lineits( uv, i, 2.1, in_time );
        vec3 its2 = lineits( uv, i, 4.2, in_time );
        its += max( its0, max(its1,its2) );
     }
    return its;
}

float glare(vec2 ssuv)
{
    vec2 iResolution = vec2(1280,720);
    float aspect = iResolution.x / iResolution.y;
    ssuv *= vec2(aspect,1.0);
    vec2 uv = abs( ssuv * 2.0 - 1.0 );
    uv = pow(uv,vec2(0.8));
    float v = exp(-(uv.x + uv.y + 2.0*uv.x*uv.y));
    v = pow( v, 5.0 );
    v = remap( v, 0.0, 1.0, 0.0, 1.2 );
    v += 0.01 / length( vec2(0.5,0.5) - ssuv );
    return v;
}

vec3 renderstars( vec2 uv, float in_time )
{
    vec3 c0 = vec3(154.0/255.0, 113.0/255.0, 255.0/255.0);
    vec3 c1 = vec3(191.0/255.0, 102.0/255.0, 255.0/255.0);

    vec2 o0 = vec2(0,0);

    float lcl_time = 0.25 * in_time;

    vec3 sumcol = vec3(0,0,0);
    for ( int i=0;i<16; ++i )
    {
        float h_idx = hash11n( float(i) );
        float t = ( lcl_time + h_idx );
        float t_i = floor( lcl_time + h_idx );

        vec2 h0 = hash22n( vec2(i,t_i));

        vec2 o = vec2(0.25, 0.5);
        vec2 ofs = uniform_circle_edge(h0.x) + o;

        vec2 rvec = ofs-o;
        float rdist = length(rvec);
        rdist = 1.5*fract( rdist + t );
        ofs = o + rdist * rvec;

        float its = glare( uv + ofs );

        vec3 c = mix( c0, c1, h0.x );

        vec2 h1 = hash22n( vec2(i,in_time));
        c *= remap( h1.x, 0.0, 1.0, 0.5, 1.0 );

        sumcol += c * vec3(its,its,its);
    }
    return sumcol * intensities.z;
}

////////////////

//nvidia hsv
// http://developer.download.nvidia.com/shaderlibrary/webpages/hlsl_shaders.html#post_RGB_to_HSV
// http://developer.download.nvidia.com/shaderlibrary/webpages/hlsl_shaders.html#post_RGB_from_HSV
float min_channel(vec3 v)
{
	float t = (v.x<v.y) ? v.x : v.y;
	t = (t<v.z) ? t : v.z;
	return t;
}
float max_channel(vec3 v)
{
	float t = (v.x>v.y) ? v.x : v.y;
	t = (t>v.z) ? t : v.z;
	return t;
}
vec3 rgb_to_hsv_nv(vec3 RGB)
{
	vec3 HSV = vec3(0,0,0);
	float minVal = min_channel(RGB);
	float maxVal = max_channel(RGB);
	float delta = maxVal - minVal; //Delta RGB value 
	HSV.z = maxVal;
	// If gray, leave H & S at zero
	if (delta != 0.0) { 
		HSV.y = delta / maxVal;
		vec3 delRGB;
		delRGB = ( ( vec3(maxVal) - RGB ) / 6.0 + ( delta / 2.0 ) ) / delta;
		if      ( RGB.x == maxVal ) HSV.x = delRGB.z - delRGB.y;
		else if ( RGB.y == maxVal ) HSV.x = 1.0/3.0 + delRGB.x - delRGB.z;
		else if ( RGB.z == maxVal ) HSV.x = 2.0/3.0 + delRGB.y - delRGB.x;
		if ( HSV.x < 0.0 ) { HSV.x += 1.0; }
		if ( HSV.x > 1.0 ) { HSV.x -= 1.0; }
	}
	return (HSV);
}
vec3 hsv_to_rgb_nv(vec3 HSV)
{
    vec3 RGB = HSV.zzz;
    if ( HSV.y != 0.0 ) {
        float var_h = HSV.x * 6.0;
        float var_i = floor(var_h);
        float var_1 = HSV.z * (1.0 - HSV.y);
        float var_2 = HSV.z * (1.0 - HSV.y * (var_h-var_i));
        float var_3 = HSV.z * (1.0 - HSV.y * (1.0-(var_h-var_i)));
        if      (var_i == 0.0) { RGB = vec3(HSV.z, var_3, var_1); }
        else if (var_i == 1.0) { RGB = vec3(var_2, HSV.z, var_1); }
        else if (var_i == 2.0) { RGB = vec3(var_1, HSV.z, var_3); }
        else if (var_i == 3.0) { RGB = vec3(var_1, var_2, HSV.z); }
        else if (var_i == 4.0) { RGB = vec3(var_3, var_1, HSV.z); }
        else                 { RGB = vec3(HSV.z, var_1, var_2); }
    }
    return (RGB);
}

vec3 radial_hue_offset( vec2 uv, vec3 col )
{
    vec3 hsv = rgb_to_hsv_nv(col);
    float rofs = length( vec2(0.5,0.5) - uv );
    hsv.x = fract( hsv.x + 0.5 + 0.5 * sin( 0.5*time.x - 2.0*rofs ) );
    return hsv_to_rgb_nv(hsv);
}

void main(void)
{
	vec2 ssuv = texcoord0;

	float tunnel_tile = 1.0; //TODO: input
	vec2 uv = fract( tunnel_tile * ssuv );
	uv = 2.0 * uv - 1.0;
	vec3 col_tunnel = render_tunnel( uv );

	vec3 col_lines = renderlines( ssuv, time.x );

    col_lines += renderstars(uv, time.x);

	out_color0 = vec4( intensities.x * col_tunnel + intensities.y * col_lines, 1 );


    out_color0.rgb = mix( out_color0.rgb, radial_hue_offset( ssuv, out_color0.rgb ), intensities.w );
}
