shader_type spatial;
render_mode blend_mix, depth_draw_never;

//Snow globals
global uniform float global_snow_start_time        = -1.0;
global uniform float global_snow_accumulation_speed = 0.005;
global uniform float global_snow_melt_time         = -1.0;
global uniform float global_snow_melt_speed        = 0.1;
global uniform float global_snow_amount            = 0.0;
global uniform float global_snow_threshold         = 0.5;
global uniform vec4  global_snow_color             = vec4(0.92, 0.96, 1.0, 1.0);
global uniform float global_snow_max_accumulation  = 0.72;
global uniform bool global_snow_cap_enabled       = true;
global uniform float global_snow_cap_height        = 0.06;
global uniform float global_snow_cap_flatness_start = 0.72;
global uniform float global_snow_cap_flatness_end  = 0.96;
global uniform float global_snow_cap_noise_scale   = 0.22;
global uniform float global_snow_cap_noise_strength = 0.18;
uniform float snow_noise_scale : hint_range(0.01, 1.0) = 0.15;
uniform float snow_edge_softness : hint_range(0.01, 0.5) = 0.2;
uniform float snow_roughness_variation : hint_range(0.0, 0.3) = 0.15;
uniform float snow_color_variation : hint_range(0.0, 0.15) = 0.05;

//Rain globals
global uniform float global_rain_intensity;
global uniform float global_rain_puddle_amount;
uniform float ripple_scale : hint_range(0.1, 5.0) = 1.5;
uniform float ripple_speed : hint_range(0.5, 5.0) = 2.0;
uniform float ripple_layers : hint_range(1.0, 4.0) = 3.0;
uniform float streak_scale : hint_range(0.1, 5.0) = 2.0;
uniform float streak_speed : hint_range(0.1, 3.0) = 0.8;
uniform float wetness_darkening : hint_range(0.0, 0.5) = 0.25;
uniform float wet_roughness : hint_range(0.0, 0.3) = 0.05;
uniform float rain_normal_strength : hint_range(0.0, 1.0) = 0.4;
uniform float puddle_noise_scale : hint_range(0.01, 0.5) = 0.08;
uniform float puddle_threshold : hint_range(0.0, 0.8) = 0.45;

uniform sampler2D noise_texture : filter_linear_mipmap, repeat_enable;

varying float v_snow_amount;
varying float v_snow_cap_mask;

float hash(vec2 p) {
    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}

float value_noise(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    vec2 u = f * f * (3.0 - 2.0 * f);

    float a = hash(i);
    float b = hash(i + vec2(1.0, 0.0));
    float c = hash(i + vec2(0.0, 1.0));
    float d = hash(i + vec2(1.0, 1.0));

    return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

float fbm(vec2 p) {
    float value = 0.0;
    float amplitude = 0.5;
    float frequency = 1.0;

    for (int i = 0; i < 3; i++) {
        value += value_noise(p * frequency) * amplitude;
        frequency *= 2.0;
        amplitude *= 0.5;
    }

    return clamp(value, 0.0, 1.0);
}

float get_snow_progress() {
    float snow_progress = clamp(global_snow_amount, 0.0, 1.0);

    if (global_snow_start_time >= 0.0) {
        float timed_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
        snow_progress = max(snow_progress, timed_progress);
    }
    if (global_snow_melt_time >= 0.0) {
        float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
        snow_progress = min(snow_progress, 1.0 - melt);
    }

    return snow_progress;
}

float ripple_ring(vec2 uv, float time_offset) {
    float d = length(uv);
    float ring = sin(d * 30.0 - TIME * ripple_speed + time_offset) * 0.5 + 0.5;
    float fade = smoothstep(0.5, 0.0, d);
    return ring * fade;
}

void vertex() {
    float snow_progress = get_snow_progress();
    float snow_accumulation = snow_progress * global_snow_max_accumulation;
    vec3 world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    vec3 world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);

    float flat_surface = smoothstep(
        max(global_snow_threshold, global_snow_cap_flatness_start),
        global_snow_cap_flatness_end,
        world_normal.y
    );
    float accumulation_growth = smoothstep(0.08, 0.65, snow_progress);
    float cap_noise = fbm(world_pos.xz * global_snow_cap_noise_scale + vec2(11.3, 4.7));
    float cap_variation = mix(1.0 - global_snow_cap_noise_strength, 1.0 + global_snow_cap_noise_strength, cap_noise);

    v_snow_amount = snow_progress;
    v_snow_cap_mask = flat_surface * accumulation_growth * cap_variation * (global_snow_cap_enabled ? 1.0 : 0.0);

    VERTEX += NORMAL * (global_snow_cap_height * snow_accumulation * v_snow_cap_mask);
}

void fragment() {
    vec3 world_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
    vec3 world_normal = normalize((INV_VIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
    float facing_up = clamp(world_normal.y, 0.0, 1.0);
    float noise_val = texture(noise_texture, world_pos.xz * snow_noise_scale).r;

    //Snow
    float snow_progress = v_snow_amount;
    float snow_accumulation = snow_progress * global_snow_max_accumulation;

    float snow_edge = smoothstep(
        global_snow_threshold - snow_edge_softness,
        global_snow_threshold + snow_edge_softness,
        facing_up
    );
    float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation;
    float snow_variation = mix(0.75, 1.15, noise_val);
    float snow_coverage = clamp(snow_progress * snow_variation + flat_accumulation * 0.25, 0.0, 1.0);
    float snow_factor = max(snow_edge * snow_coverage, flat_accumulation);
    float snow_opacity = clamp(snow_factor, 0.0, 1.0);
    snow_opacity = max(snow_opacity, flat_accumulation * 0.9);

    float shade = mix(-snow_color_variation, snow_color_variation, noise_val);
    vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0);
    snow_albedo = mix(snow_albedo, vec3(1.0), 0.18 + snow_opacity * 0.12);
    float snow_rough = mix(0.15 - snow_roughness_variation, 0.15 + snow_roughness_variation, noise_val);

    //Rain
    float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
    float rain_factor = 0.0;
    vec3 rain_albedo = vec3(0.0);
    float rain_rough = wet_roughness;
    float rain_metallic = 0.0;
    vec3 rain_normal = vec3(0.5, 0.5, 1.0);

    if (rain_int > 0.01 && facing_up > 0.3) {
        float surface_mask = smoothstep(0.3, 0.6, facing_up);

        //Slope detection: 0 = flat, 1 = peak at ~30-45 degrees
        float slope_angle = 1.0 - facing_up;
        float slope_mask = smoothstep(0.1, 0.25, slope_angle) * smoothstep(0.75, 0.5, slope_angle);

        //Ripples on horizontal surfaces
        float ripple = 0.0;
        for (float i = 0.0; i < ripple_layers; i += 1.0) {
            float phase = i * 2.17;
            vec2 cell_offset = vec2(
                texture(noise_texture, world_pos.xz * ripple_scale * 0.3 + vec2(phase)).r,
                texture(noise_texture, world_pos.xz * ripple_scale * 0.3 + vec2(0.0, phase)).r
            );
            vec2 ripple_uv = fract(world_pos.xz * ripple_scale + cell_offset) - 0.5;
            ripple += ripple_ring(ripple_uv, phase * 3.7);
        }
        ripple /= ripple_layers;

        //Flowing water on inclined surfaces (30-45 degrees)
        vec3 down_dir = normalize(world_normal - vec3(0.0, 1.0, 0.0) * facing_up);
        float flow_speed = streak_speed * (1.0 + slope_angle * 2.0);

        //Main flow rivulets
        float flow_coord = dot(world_pos, down_dir);
        float lateral_coord = dot(world_pos.xz, vec2(-down_dir.z, down_dir.x)) * streak_scale;
        vec2 flow_uv = vec2(lateral_coord, flow_coord * streak_scale - TIME * flow_speed);
        float flow_noise = texture(noise_texture, flow_uv * 0.4).r;
        float flow2 = texture(noise_texture, flow_uv * 0.7 + vec2(0.3, TIME * flow_speed * 0.3)).r;
        float rivulets = smoothstep(0.3, 0.7, flow_noise) * smoothstep(0.25, 0.6, flow2);

        //Accumulation at bottom (lower world_pos.y gets more water)
        float height_factor = texture(noise_texture, world_pos.xz * 0.05).r;
        float accumulation = smoothstep(0.6, 0.2, fract(world_pos.y * 0.3 + height_factor * 0.5));
        rivulets = mix(rivulets, rivulets + accumulation * 0.3, slope_mask);

        float streak = rivulets * slope_mask;

        //Blend ripples (flat) and streaks (inclined)
        float flat_weight = smoothstep(0.3, 0.15, slope_angle);
        float effect = mix(streak, ripple, flat_weight) * rain_int * surface_mask;
        float darkness = wetness_darkening * rain_int * surface_mask;

        vec2 normal_offset = vec2(dFdx(effect), dFdy(effect)) * rain_normal_strength;
        rain_normal = vec3(normal_offset.x + 0.5, normal_offset.y + 0.5, 1.0);
        rain_albedo = vec3(0.05, 0.05, 0.06);
        rain_metallic = 0.15 * rain_int;
        rain_factor = darkness + effect * 0.15;
    }

	//Puddles
    float puddle_factor = 0.0;
    float puddle_amt = clamp(global_rain_puddle_amount, 0.0, 1.0);

    if (puddle_amt > 0.01 && facing_up > 0.85) {
        float puddle_mask = smoothstep(0.85, 0.95, facing_up);

        // Large-scale distribution: sparse zones where puddles can form
        float distribution = texture(noise_texture, world_pos.xz * puddle_noise_scale * 0.15 + vec2(13.7, 7.3)).r;
        float sparse_mask = smoothstep(0.55 - puddle_amt * 0.3, 0.35 - puddle_amt * 0.2, distribution);

        // Multi-scale noise for organic puddle shapes
        float n1 = texture(noise_texture, world_pos.xz * puddle_noise_scale).r;
        float n2 = texture(noise_texture, world_pos.xz * puddle_noise_scale * 2.7 + vec2(5.7, 3.1)).r;
        float n3 = texture(noise_texture, world_pos.xz * puddle_noise_scale * 5.3 + vec2(11.2, 8.9)).r;
        float puddle_noise = n1 * 0.5 + n2 * 0.3 + n3 * 0.2;

        // Puddles expand as puddle_amount increases
        float threshold = puddle_threshold + (1.0 - puddle_amt) * 0.35;
        float puddle_shape = smoothstep(threshold, threshold - 0.08, puddle_noise);
        puddle_factor = puddle_shape * sparse_mask * puddle_mask * puddle_amt;

        // Ripples inside puddles
        if (puddle_factor > 0.1 && rain_int > 0.01) {
            float puddle_ripple = 0.0;
            for (float i = 0.0; i < 2.0; i += 1.0) {
                float phase = i * 3.14;
                vec2 r_uv = fract(world_pos.xz * ripple_scale * 0.8 + vec2(phase * 0.7)) - 0.5;
                puddle_ripple += ripple_ring(r_uv, phase * 2.3);
            }
            puddle_ripple *= 0.5 * rain_int;

            vec2 p_normal_offset = vec2(dFdx(puddle_ripple), dFdy(puddle_ripple)) * 0.6;
            rain_normal = vec3(p_normal_offset.x + 0.5, p_normal_offset.y + 0.5, 1.0);
        }
    }

    //Combine
    //Puddles override rain when stronger
    if (puddle_factor > rain_factor) {
        rain_albedo = vec3(0.01, 0.01, 0.02);
        rain_rough = 0.02;
        rain_metallic = 0.5 * puddle_amt;
        rain_factor = puddle_factor;
    }

    if (snow_factor > rain_factor) {
        ALBEDO    = snow_albedo;
        ROUGHNESS = snow_rough;
        METALLIC  = 0.0;
        ALPHA     = snow_opacity;
    } else {
        ALBEDO    = rain_albedo;
        ROUGHNESS = rain_rough;
        METALLIC  = rain_metallic;
        NORMAL_MAP = rain_normal;
        ALPHA     = rain_factor;
    }
}
