#define USE_CAUSTICS 1
#define USE_REFRACTION 1
#define USE_DISPLACEMENT 1
#define USE_STYLIZED_LIGHTING 0
#define USE_UNSHADED 0

shader_type spatial;
#if USE_UNSHADED
	render_mode unshaded, depth_draw_never;
#else
	render_mode depth_draw_never;
#endif

uniform sampler2D DEPTH_TEXTURE: hint_depth_texture;

group_uniforms Color;
uniform vec4 surface_color : source_color = vec4(0.2,1.0,0.8,1.0);
uniform vec4 depth_color : source_color = vec4(0.08,0.2,0.4,1.0);
uniform vec4 foam_color : source_color = vec4(1.0);
uniform float depth_size = 12.0;

group_uniforms Roughness;
uniform float surface_roughness : hint_range(0.0, 1.0, 0.01) = 0.05;
uniform float foam_roughness : hint_range(0.0, 1.0, 0.01) = 0.05;

#if USE_CAUSTICS
	group_uniforms Caustics;
	uniform sampler2D caustics_texture;
	uniform float caustics_strength = 2.0;
	uniform vec2 caustics_scale = vec2(0.5);
#endif

group_uniforms Wave;
uniform sampler2D wave_texture;
uniform float wave_softness : hint_range(0.0, 10.0, 0.1) = 3.0;
uniform vec2 wave_scale = vec2(0.2);
uniform vec2 wave_layer_scale = vec2(1.5);
uniform float wave_highlight : hint_range(0.0, 1.0, 0.05) = 0.5;

group_uniforms Wave.Motion;
uniform vec2 wave_velocity = vec2(0.02);
instance uniform vec2 river_flow_direction = vec2(0.0, 0.0);

group_uniforms Foam;
uniform sampler2D foam_texture;
uniform float edge_foam_depth_size = 1.0;
uniform float wave_foam_amount : hint_range(0.0, 1.0, 0.01) = 0.8;
uniform float foam_start : hint_range(0.0, 1.0, 0.05) = 0.15;
uniform float foam_end : hint_range(0.0, 1.0, 0.05) = 0.3;
uniform float foam_exponent = 2.0;

#if USE_REFRACTION
	group_uniforms Refraction;
	uniform float refraction_amount = 0.5;
	uniform float refraction_exponent = 0.5;
#endif

#if USE_DISPLACEMENT
	group_uniforms Displacement;
	uniform float displacement_amount = 0.3;
#endif

#if USE_STYLIZED_LIGHTING && !USE_UNSHADED
	group_uniforms Lighting;
	uniform float diffuse_steps = 12.0;
	uniform float diffuse_smoothness : hint_range(0.0, 1.0, 0.01) = 0.2;
	uniform float specular_steps = 12.0;
	uniform float specular_smoothness : hint_range(0.0, 1.0, 0.01) = 0.2;
#endif

uniform sampler2D screen_texture : hint_screen_texture;
varying vec3 world_pos;
varying vec2 world_wave_velocity;

#if USE_CAUSTICS
	vec3 sample_caustics(vec2 uv){
		vec2 caustics_uv = uv * caustics_scale;
		return vec3(
			texture(caustics_texture, caustics_uv).r,
			texture(caustics_texture, caustics_uv+vec2(0.02,0.02)).r,
			texture(caustics_texture, caustics_uv+vec2(0.03,0.01)).r
		);
	}
#endif

vec4 sample_world_dpos(vec2 screen_uv, mat4 inv_proj_mat, mat4 inv_view_mat){
	vec4 clip_pos = vec4(screen_uv * 2.0 - 1.0, texture(DEPTH_TEXTURE, screen_uv).r, 1.0);
	vec4 view_pos = inv_proj_mat * clip_pos;
	view_pos /= view_pos.w;
	vec4 world_dpos = inv_view_mat * view_pos;
	return world_dpos;
}

vec4 sample_wave(sampler2D tex, vec2 uv, vec2 velocity, float lod){
	vec2 base_uv = uv * wave_scale;
	vec2 wave_uv1 = (base_uv * wave_layer_scale) + (TIME * -velocity);
	float wave1 = textureLod(tex, wave_uv1, lod).r;
	
	vec2 wave_uv2 = base_uv + (TIME * velocity);
	vec4 wave2 = textureLod(tex, wave_uv2 - (wave1 * 0.1), lod);
	
	return wave2;
}

vec3 sample_wave_normal(vec2 uv, vec2 velocity, float center_wave) {
	vec2 normal_offset = vec2(0.25, 0.0);
	float wave_x = sample_wave(wave_texture, uv + normal_offset.xy, velocity, wave_softness).r;
	float wave_z = sample_wave(wave_texture, uv + normal_offset.yx, velocity, wave_softness).r;
	vec2 slope = vec2(center_wave - wave_x, center_wave - wave_z) * 0.35;
	return normalize(vec3(slope, 1.0)) * 0.5 + 0.5;
}

vec2 get_world_wave_velocity(mat4 model_matrix) {
	float velocity_length = length(wave_velocity);
	if (velocity_length <= 0.0001) {
		return vec2(0.0);
	}

	float flow_direction_length = length(river_flow_direction);
	if (flow_direction_length > 0.0001) {
		return (river_flow_direction / flow_direction_length) * velocity_length;
	}

	vec3 world_direction_3d = (model_matrix * vec4(0.0, 1.0, 0.0, 0.0)).xyz;
	vec2 world_direction = world_direction_3d.xz;
	float world_direction_length = length(world_direction);
	if (world_direction_length <= 0.0001) {
		return wave_velocity;
	}

	return (world_direction / world_direction_length) * velocity_length;
}

void vertex(){
	world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
	world_wave_velocity = get_world_wave_velocity(MODEL_MATRIX);
	
	#if USE_DISPLACEMENT
		float wave = sample_wave(wave_texture, world_pos.xz, world_wave_velocity, wave_softness).r;
		VERTEX.y += wave*displacement_amount;
	#endif
}

void fragment() {
	float wave = sample_wave(wave_texture, world_pos.xz, world_wave_velocity, wave_softness).r;
	wave = smoothstep(0.0,1.0,wave);
	
	vec2 screen_uv = SCREEN_UV;
	
	// Refraction
	#if USE_REFRACTION
		screen_uv += ((pow(wave, refraction_exponent)*2.0 - 0.5) * 0.01 * refraction_amount);
		
		vec4 world_dpos = sample_world_dpos(screen_uv, INV_PROJECTION_MATRIX, INV_VIEW_MATRIX);
		
		float pre_depth = pow(clamp((world_dpos.y - world_pos.y + depth_size)/depth_size, 0.0, 1.0), 4.0);
		screen_uv = mix(screen_uv, SCREEN_UV, pre_depth);
		if(world_dpos.y - world_pos.y > 0.0){
			screen_uv = SCREEN_UV;
		}
	#else
		vec4 world_dpos = sample_world_dpos(screen_uv, INV_PROJECTION_MATRIX, INV_VIEW_MATRIX);
	#endif
	
	world_dpos = sample_world_dpos(screen_uv, INV_PROJECTION_MATRIX, INV_VIEW_MATRIX);
	
	vec2 surface_uv = world_dpos.xz * 0.2;
	
	float depth = pow(clamp((world_dpos.y - world_pos.y + depth_size)/depth_size, 0.0, 1.0), 4.0);
	
	// Caustics
	#if USE_CAUSTICS
		vec3 caustics1 = sample_caustics(surface_uv + (TIME * -world_wave_velocity));
		vec3 caustics2 = sample_caustics((surface_uv + (caustics1.r*0.05)) + (TIME * (world_wave_velocity*0.5)));
		vec3 caustics = caustics2 * (1.0 - depth);
	#endif
	
	// Edge Foam
	float edge_foam_depth = clamp((world_dpos.y - world_pos.y + edge_foam_depth_size)/edge_foam_depth_size, 0.0, 1.0);
	
	// Wave Foam
	float wave_foam = wave;
	float foam = max(edge_foam_depth, wave_foam * wave_foam_amount);
	
	float foam_shape = 1.0 - texture(foam_texture, world_pos.xz* 0.5).r;
	foam = clamp((foam - foam_start) / (foam_end - foam_start), 0.0, 1.0);
	foam = clamp((foam - foam_shape) / (1.0 - foam_shape), 0.0, 1.0);
	foam = pow(foam, foam_exponent);
	
	vec3 flat_color = mix(depth_color, surface_color, depth).rgb;
	
	vec4 screen = texture(screen_texture, screen_uv);
	vec3 color = screen.rgb;
	#if USE_CAUSTICS
		color += vec3(pow(caustics * caustics_strength, vec3(2.0)));
	#endif
	color = mix(flat_color, color, 0.4 * depth);
	color = mix(color, surface_color.rgb, wave * wave_highlight);
	color = mix(color, foam_color.rgb, foam);
	
	#if !USE_UNSHADED
		vec3 wave_normal_map = sample_wave_normal(world_pos.xz, world_wave_velocity, wave);
		NORMAL_MAP = wave_normal_map;
	#endif
	
	ROUGHNESS = mix(surface_roughness, foam_roughness, foam);
	
	ALBEDO = color;
}

#if USE_STYLIZED_LIGHTING && !USE_UNSHADED
	void light(){
		float ndotl = dot(NORMAL, LIGHT) * ATTENUATION;
		//ndotl = smoothstep(0.0,1.0-ROUGHNESS,ndotl);
		float light = ndotl;
		
		float light_mult = light * diffuse_steps;
		float light_step_base = floor(light_mult);
		float light_factor = light_mult - light_step_base;
		
		light_factor = smoothstep(0.5 - diffuse_smoothness * 0.5, 0.5 + diffuse_smoothness * 0.5, light_factor);
		light = (light_step_base + light_factor) / diffuse_steps;
		
		DIFFUSE_LIGHT += (LIGHT_COLOR+ALBEDO) * light / PI;
		
		float roughness = mix(0.01, 0.99, ROUGHNESS);
		vec3 h = normalize(VIEW + LIGHT);
		float ndoth = clamp(dot(NORMAL, h), 0.0, 1.0) * ATTENUATION;
		float specular = clamp(pow(ndoth, 16.0/(roughness)), 0.1, 0.99);
		specular = mix(pow(specular, 2.0-roughness),0.00,pow(roughness, 0.1));
		
		float specular_mult = specular * specular_steps;
		float specular_step_base = floor(specular_mult);
		float specular_factor = specular_mult - specular_step_base;
		
		specular_factor = smoothstep(0.5 - specular_smoothness * 0.5, 0.5 + specular_smoothness * 0.5, specular_factor);
		specular = (specular_step_base + specular_factor) / specular_steps;
		
		SPECULAR_LIGHT += (LIGHT_COLOR + ALBEDO) * specular;
	}
#endif
