// Copyright (C) Kimmo Lahtinen - All Rights Reserved

///////////////////////////////////////////////////////////////////////////////
// Background effect detail level

// Supported quality macros, #defined in common.vert!
//#define BG_ENABLE_PLASMA_ANIMATION
//#define BG_ENABLE_FANCY_GLOW
//#define BG_ENABLE_GRADIENT
//#define BG_ENABLE_STARS

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

#ifdef PLATFORM_IOS
// High precision the whole shader as most of stuff need it (could opt some of them for mediump?)
precision highp float;
#endif

// Input from vertex shader
varying vec2 frag_clip_pos;
varying vec2 frag_map_pos;

// Uniforms
uniform float uni_global_time;
uniform vec4 uni_effect_params[5];

/////////////////////////////////////////////////////////////
// Smoother blending of RGB colors
// https://www.shadertoy.com/view/lsdGzN

// Changes the strength of the displacement
//#define DSP_STR 1.5
//#define DSP_STR 0.75
#define DSP_STR (uni_effect_params[3][3])

// Optimizaton for getting the saturation (HSV Type) of a rgb color
float getsat(vec3 c)
{
    float mi = min(min(c.x, c.y), c.z);
    float ma = max(max(c.x, c.y), c.z);
    return (ma - mi)/(ma+ 1e-7);
}

//Improved rgb lerp
vec3 iLerp(vec3 a, vec3 b, float x)
{
    //Interpolated base color (with singularity fix)
    vec3 ic = mix(a, b, x) + vec3(1e-6, 0.0, 0.0);
    
    //Saturation difference from ideal scenario
	float sd = abs(getsat(ic) - mix(getsat(a), getsat(b), x));
    
    //Displacement direction
	vec3 dir = normalize(vec3(2.0*ic.x - ic.y - ic.z, 2.0*ic.y - ic.x - ic.z, 2.0*ic.z - ic.y - ic.x));

    //Simple Lighntess
	float lgt = dot(vec3(1.0), ic);
    
    //Extra scaling factor for the displacement
	float ff = dot(dir, normalize(ic));
    
    //Displace the color
    ic += DSP_STR*dir*sd*ff*lgt;
    return clamp(ic, 0.0, 1.0);
}

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

// Noise, standard method found everywhere
float noise(vec2 co)
{
    return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
}

// Less intensive noise for sparser stars
float noise_smoother(vec2 co)
{
	float a = noise(co);
	float b = noise(co * 12.345);
	return mix(a, b, 0.5);
}

vec3 star_color()
{	
	float star_blink_speed = uni_effect_params[0][0];
	float star_brightness_factor = uni_effect_params[0][1];

	// Just put some random specs in the air
	vec2 noise_co = floor(frag_clip_pos * 1000.0);
	float star_brightness = pow(noise_smoother(noise_co), 60.0);
	star_brightness *= star_brightness_factor;

	// Move a screen of holes over the stars to fadein/out
	// Note: input is continuous time! but should ok, as it's not supposed to run hours
	float t = star_blink_speed * uni_global_time;
	//t = mod(t, 3.14159 * 2.0);
	float brightness = 1.0 -
		(sin(t + 120.0	* frag_clip_pos.x) * 0.5 + 0.5) *
		(sin(t + 200.0	* frag_clip_pos.y) * 0.5 + 0.5);
//	brightness = 1.0;

	vec3 res = vec3(star_brightness * brightness);
//	res.r = brightness;
    return res;
}

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

// Rotate map coordinates to be screen space aligned
vec2 to_screen_aligned(vec2 pos_world)
{
	const float rot = 1.4142135 * 0.5;
	float x_r = pos_world.x * rot;
	float y_r = pos_world.y * rot;
	return vec2(x_r - y_r, x_r + y_r);
}

vec3 level_glow(float plasma_input)
{
	float cutaway_drop_adjust = uni_effect_params[0][2];
	float falloff_rate = uni_effect_params[0][3];
	float cutaway_extra_smoothing_distance = uni_effect_params[1][0];
	float cutaway_extra_brightness = uni_effect_params[1][1];
	float plasma_input_strength = uni_effect_params[1][2];
	float plasma_input_pow = uni_effect_params[1][3];
	vec3 color_factors = uni_effect_params[2].rgb;

	// Clamp map position to limits
	vec2 clamped = max(frag_map_pos, vec2(0.0));
	clamped = min(clamped, uni_map_size_current + cutaway_drop_adjust);

	// Clamp the edge dropoffs away by working in screen aligned space
	vec2 pos_screen  = to_screen_aligned(frag_map_pos);
	vec2 pos_clamped = to_screen_aligned(clamped);

	float limit_max = to_screen_aligned(vec2(uni_map_size_current.x, 0.0)).x;
	float limit_min = to_screen_aligned(vec2(0.0, uni_map_size_current.y)).x;
	pos_clamped.x = clamp(pos_clamped.x, limit_min, limit_max);

	// Distance to clamped area
	float dist = length(pos_screen - pos_clamped);
	dist = max(0.01, dist); // Add a bit if zero so it doesn't zero out the log()

	// Logaritmic falloff for glow
	float l = 1.0 - log(dist * falloff_rate);
	l = max(0.0, l);

	// Add a bit of extra brightness to the bottom
#ifdef BG_ENABLE_FANCY_GLOW	
	vec2 bottom_factor = smoothstep(
		uni_map_size_current, 
		uni_map_size_current + cutaway_extra_smoothing_distance, 
		frag_map_pos);

	float bottom_adjust = 1.0 + cutaway_extra_brightness * dot(bottom_factor, bottom_factor);

	// Plasma adjust
	float plasma_factor = pow(1.0 + plasma_input * plasma_input_strength + plasma_input_strength, plasma_input_pow);

	// Calculate final color
	vec3 final_color = l * color_factors * bottom_adjust * plasma_factor;
	return final_color;
#else	
	return l * color_factors * 1.75;
#endif	
} 

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

// cleanup and opt and see whats up anyway to make it clear
float plasma(vec2 s1, vec2 s2)
{
	// Make some weird sin movement to generate a field of plasma or something I dunno

	// Balls of sin (low freq)
	float f = sin(s1.x) * sin(s1.y);
	// f *= 0.0001;
    
    // Rotated and higher freq balls
    const float rot = 1.2;
    float s = sin(rot);
    float c = cos(rot);

    const float freq = 2.0;
	s2 = vec2(
	  	s2.x * c - s2.y * s, 
	  	s2.x * s + s2.y * c) * freq;

	float f2 = sin(s2.x) * sin(s2.y);
    return (f + f2) * 0.5;
}

vec3 gradient(float plasma_input)
{
	// Simple one direction gradient
	vec3 color_a = uni_effect_params[3].rgb;
	vec3 color_b = uni_effect_params[4].rgb;
	float plasma_gradient_strength = uni_effect_params[4][3];
	
	float a = (frag_clip_pos.y + 1.0) * 0.5;

	// Tweak interpolation by plasma to make it alive
	a += plasma_input * plasma_gradient_strength;

	return iLerp(color_a, color_b, a);
}

void main(void) 
{
#ifdef BG_ENABLE_PLASMA_ANIMATION	
	// Setup adhoc plasma input variables
	// Note: input is continuous time! but should be okayish, as it's not supposed to run hours...
	float plasma_animation_speed = uni_effect_params[2][3];
	vec2 plasma_uv = frag_clip_pos * 2.005;
	plasma_uv.x += uni_global_time * plasma_animation_speed;
	plasma_uv.y *= 0.7;

	vec2 plasma_uv2 = frag_clip_pos;
	plasma_uv2.x -= uni_global_time * plasma_animation_speed;

	// Calculate plasma
	float p = plasma(plasma_uv, plasma_uv2);
#else    
	float p = 0.0;
#endif

	// Combine all effects
#if defined(BG_ENABLE_GRADIENT)	
	vec3 final_color = gradient(p);
#else
	vec3 final_color = vec3(uni_effect_params[4].rgb);
#endif

	final_color += level_glow(p);

#if defined(BG_ENABLE_STARS)
	final_color += star_color();
#endif	

	gl_FragColor.rgb = final_color;
	gl_FragColor.a = 1.0; // Doesn't matter, opaque rendering
}
