class_name WeatherController
extends Node

var thunder_audio_player: AudioStreamPlayer
var thunder_sounds: Array[AudioStream]
var rain_sounds: Array[AudioStream]
var rain_audio_player: AudioStreamPlayer

var particles_wind: GPUParticles3D
var particles_snow: GPUParticles3D
var particles_fireflies: GPUParticles3D
var particles_rain: GPUParticles3D
var godray: PackedScene
var environment_dust: ColorRect
var blur: ColorRect
var environment_shadows: MeshInstance3D

var sun: DirectionalLight3D
var environment: WorldEnvironment
var environment_config: EnvironmentConfig
var camera: Camera3D
var camera_pivot: Node3D

var day_time: float = 0.0

var lightning_sprite: Sprite3D
var lightning_light = DirectionalLight3D.new();
var timer_next_lightning: float = 0.0

var is_raining: bool = false
var rain_intensity: float = 0.0 
var rain_tween: Tween
var rain_audio_tween: Tween
var puddle_tween: Tween
var puddle_amount: float = 0.0

var snow_tween: Tween
var snow_particles_tween: Tween
var is_snowing: bool = false
var is_snow_accumulated: bool = false 
var actual_snow_amount: float = 0.0

var is_storm: bool = false
var cold_tween: Tween
var is_windy: bool = false
var thereare_fireflies: bool = false

func _init(wind: GPUParticles3D, snow: GPUParticles3D, 
	fireflies: GPUParticles3D, rain: GPUParticles3D, ray: PackedScene,
	dust: ColorRect, blurr: ColorRect, envshadows: MeshInstance3D,
	thesun: DirectionalLight3D, theenvironment: WorldEnvironment, environmentconfig: EnvironmentConfig,
	thecamera: Camera3D, thecamerapivot: Node3D, thundersounds: Array[AudioStream], rainsounds: Array[AudioStream]) -> void:
	particles_wind = wind
	particles_snow = snow
	particles_fireflies = fireflies
	particles_rain = rain
	godray = ray
	sun = thesun
	environment_dust = dust
	blur = blurr
	environment_shadows = envshadows
	environment = theenvironment
	environment_config = environmentconfig
	camera = thecamera
	camera_pivot = thecamerapivot
	thunder_sounds = thundersounds
	rain_sounds = rainsounds
	
	init_rain()

	init_fireflies()
	
	init_wind()
	
	init_snow()
	
	init_lightning()
	
	create_sound_players()
	
	toggle_dust(environment_config.enable_dust)
	toggle_blur(environment_config.enable_blur)
	
	#set camera pivot if available
	if camera_pivot:
		camera_pivot.get_node("pivot").camera_changed.connect(_set_camera)

func _ready() -> void:
	
	#connect events
	UIEvents.toggle_rain.connect(toggle_rain)
	UIEvents.toggle_snow.connect(toggle_snow)
	UIEvents.toggle_wind.connect(toggle_wind)
	UIEvents.toggle_fireflies.connect(toggle_fireflies)
	UIEvents.toggle_storm.connect(toggle_storm)
	UIEvents.toggle_dust.connect(toggle_dust)
	UIEvents.toggle_blur.connect(toggle_blur)
	UIEvents.toggle_shadows.connect(toggle_shadows)

func _process(delta: float) -> void:
	
	_follow_camera()
	
	var is_night = day_time >= 2.5
	if particles_fireflies:
		particles_fireflies.emitting = thereare_fireflies and is_night
			
	var base_tint: Color
	var base_sky_top: Color
	var base_sky_horizon: Color
	var base_fog_color: Color
	var base_fog_density: float
	var base_bloom: float
	var base_exposure: float
	var base_grad_top: Color
	var base_grad_bot: Color
	var base_grad_intensity: float
	var base_rotation_sun: Vector3

	if is_raining:
		var target_rain = environment_config.storm_rain_intensity_multiplier if is_storm else 1.0
		rain_intensity = move_toward(rain_intensity, target_rain, delta * 0.5)
		if is_storm:
			timer_next_lightning -= delta
			if timer_next_lightning <= 0.0:
				start_lightning()
				init_lightning_timer()
	else:
		rain_intensity = move_toward(rain_intensity, 0.0, delta * 0.5)

	RenderingServer.global_shader_parameter_set("global_rain_intensity", rain_intensity)

	if day_time <= 2.0:
		var t = day_time - 1.0
		base_tint = environment_config.morning_color.lerp(environment_config.afternoon_color, t)
		base_sky_top = environment_config.sky_top_morning.lerp(environment_config.sky_top_afternoon, t)
		base_sky_horizon = environment_config.sky_horizon_morning.lerp(environment_config.sky_horizon_afternoon, t)
		base_fog_color = environment_config.fog_color_morning.lerp(environment_config.fog_color_afternoon, t)
		base_fog_density = lerp(environment_config.fog_density_morning, environment_config.fog_density_afternoon, t)
		base_bloom = lerp(environment_config.glow_morning, environment_config.glow_afternoon, t)
		base_exposure = lerp(environment_config.exposure_morning, environment_config.exposure_afternoon, t) 
		base_grad_top = environment_config.grad_top_morning.lerp(environment_config.grad_top_afternoon, t)
		base_grad_bot = environment_config.grad_top_morning.lerp(environment_config.grad_bot_afternoon, t)
		base_grad_intensity = lerp(environment_config.grad_intensity_morning, environment_config.grad_intensity_afternoon, t)
		base_rotation_sun = environment_config.sun_rotation_morning.lerp(environment_config.sun_rotation_afternoon, t)
	else:
		var t = day_time - 2.0
		base_tint = environment_config.afternoon_color.lerp(environment_config.night_color, t)
		base_sky_top = environment_config.sky_top_afternoon.lerp(environment_config.sky_top_night, t)
		base_sky_horizon = environment_config.sky_horizon_afternoon.lerp(environment_config.sky_horizon_night, t)
		base_fog_color = environment_config.fog_color_afternoon.lerp(environment_config.fog_color_night, t)
		base_fog_density = lerp(environment_config.fog_density_afternoon, environment_config.fog_density_night, t)
		base_bloom = lerp(environment_config.glow_afternoon, environment_config.glow_night, t)
		base_exposure = lerp(environment_config.exposure_afternoon, environment_config.exposure_night, t) 
		base_grad_top = environment_config.grad_top_afternoon.lerp(environment_config.grad_top_night, t)
		base_grad_bot = environment_config.grad_bot_afternoon.lerp(environment_config.grad_bot_night, t)
		base_grad_intensity = lerp(environment_config.grad_intensity_afternoon, environment_config.grad_intensity_night, t)
		base_rotation_sun = environment_config.sun_rotation_afternoon.lerp(environment_config.sun_rotation_night, t)
		
	var weather_color = environment_config.rain_mode_color
	if is_storm:
		var storm_blend = clamp((rain_intensity - 1.0) / (environment_config.storm_rain_intensity_multiplier - 1.0), 0.0, 1.0)
		weather_color = weather_color.lerp(environment_config.storm_mode_color, storm_blend)
	var final_tint = base_tint.lerp(base_tint * weather_color, clamp(rain_intensity, 0.0, 1.0))
	var final_sky_top = base_sky_top.lerp(base_sky_top * weather_color, clamp(rain_intensity, 0.0, 1.0))
	var final_sky_horizon = base_sky_horizon.lerp(base_sky_horizon * weather_color, clamp(rain_intensity, 0.0, 1.0))
	var final_fog_color = base_fog_color.lerp(base_fog_color * weather_color, clamp(rain_intensity, 0.0, 1.0))
	var final_fog_density = lerp(base_fog_density, base_fog_density * 4.0, clamp(rain_intensity, 0.0, 1.0)) 

	final_tint = final_tint.lerp(final_tint * environment_config.snow_mode_color, actual_snow_amount)
	final_sky_top = final_sky_top.lerp(final_sky_top * environment_config.snow_mode_color, actual_snow_amount)
	final_sky_horizon = final_sky_horizon.lerp(final_sky_horizon * environment_config.snow_mode_color, actual_snow_amount)
	final_fog_color = final_fog_color.lerp(final_fog_color * environment_config.snow_mode_color, actual_snow_amount)
	
	#Shader parameters for global trunk_shader
	var final_grad_top = base_grad_top.lerp(base_grad_top * weather_color, clamp(rain_intensity, 0.0, 1.0))
	var final_grad_bot = base_grad_bot.lerp(base_grad_bot * weather_color, clamp(rain_intensity, 0.0, 1.0))
	
	RenderingServer.global_shader_parameter_set("global_gradient_color_top", final_grad_top)
	RenderingServer.global_shader_parameter_set("global_gradient_color_bot", final_grad_bot)
	RenderingServer.global_shader_parameter_set("global_gradient_intensity", base_grad_intensity)
	
	var night_val = clamp(day_time - 2.0, 0.0, 1.0)
	
	#Snow exposure compensation
	var snow_light_attenuation = lerp(1.0, 0.55, actual_snow_amount * (1.0 - night_val))
	var snow_glow_attenuation = lerp(1.0, 0.5, actual_snow_amount)
	var final_bloom = lerp(base_bloom, base_bloom * 0.5, rain_intensity) * snow_glow_attenuation
	
	# We calculate the final exposure by applying snow damping directly to the camera exposure
	var final_exposure = base_exposure * snow_light_attenuation
		
	var reflected_color = Color(0.4, 0.5, 0.6, 1.0)
	var drops_color = final_sky_horizon.lerp(reflected_color, 0.15)
	drops_color = drops_color.lerp(drops_color * 0.4, night_val)
	
	if environment_config.material_drops:
		environment_config.material_drops.albedo_color = drops_color

	if sun:
		sun.light_color = final_tint
		sun.rotation_degrees = base_rotation_sun
		var target_energy = lerp(1.0, environment_config.night_light_energy, night_val)

		# Sunlight scales based on weather/night
		sun.light_energy = lerp(target_energy, target_energy * 0.4, rain_intensity)
		
	if environment and environment.environment:
		environment.environment.ambient_light_color = final_tint
		
		# Apply fog
		environment.environment.fog_enabled = true
		environment.environment.fog_light_color = final_fog_color
		environment.environment.fog_density = final_fog_density
		
		# Apply glow
		environment.environment.glow_enabled = true
		environment.environment.glow_intensity = final_bloom
		environment.environment.tonemap_exposure = final_exposure
		
		var sky_mat = environment.environment.sky.sky_material as ShaderMaterial
		if sky_mat:
			sky_mat.set_shader_parameter("sky_top_color", final_sky_top)
			sky_mat.set_shader_parameter("sky_horizon_color", final_sky_horizon)
			sky_mat.set_shader_parameter("sun_color", final_tint)
			sky_mat.set_shader_parameter("night_intensity", night_val)
			
		if environment_config.material_clouds:
			var current_density = lerp(0.4, 1.0, rain_intensity)
			environment_config.material_clouds.set_shader_parameter("cloud_density", current_density)
			var current_sharpness = lerp(0.14, 0.0, rain_intensity)
			environment_config.material_clouds.set_shader_parameter("center_sharpness", current_sharpness)
		
		if environment_config.material_fog:
			environment_config.material_fog.set_shader_parameter("night_intensity", night_val)
			environment_config.material_fog.set_shader_parameter("sun_color", final_tint)

func create_sound_players():
	rain_audio_player = AudioStreamPlayer.new()
	rain_audio_player.name = "RainAudioPlayer"
	rain_audio_player.volume_db = environment_config.rain_audio_volume_db
	rain_audio_player.finished.connect(_on_rain_audio_player_finished)
	add_child(rain_audio_player)

	thunder_audio_player = AudioStreamPlayer.new()
	thunder_audio_player.name = "ThunderAudioPlayer"
	thunder_audio_player.volume_db = environment_config.thunder_audio_volume_db
	add_child(thunder_audio_player)
	
#region camera
func _set_camera(curr_camera: Camera3D):
	camera = curr_camera

func _get_weather_anchor_position() -> Variant:
	if camera_pivot and is_instance_valid(camera_pivot):
		return camera_pivot.global_position
	if camera and is_instance_valid(camera):
		return camera.global_position
	return null

func _follow_camera() -> void:
	var cam_pos = _get_weather_anchor_position()
	if cam_pos == null:
		return
	if particles_rain:
		particles_rain.global_position = Vector3(cam_pos.x, particles_rain.position.y, cam_pos.z)
	if particles_snow:
		particles_snow.global_position = Vector3(cam_pos.x, particles_snow.position.y, cam_pos.z)
	if particles_wind:
		particles_wind.global_position = Vector3(cam_pos.x, cam_pos.y, cam_pos.z)
	if particles_fireflies:
		particles_fireflies.global_position = Vector3(cam_pos.x, particles_fireflies.position.y, cam_pos.z)
#endregion

#region Fireflies

func toggle_fireflies(value: bool):
	thereare_fireflies = value
	var is_night = day_time >= 2.5
	if particles_fireflies:
		particles_fireflies.emitting = thereare_fireflies and is_night

#disable fireflies and set default values and materials
func init_fireflies():
	if particles_fireflies:
		particles_fireflies.visible = true
		particles_fireflies.emitting = false
		particles_fireflies.amount = environment_config.fireflies_amount
		var proc_mat = particles_fireflies.process_material as ParticleProcessMaterial
		if proc_mat:
			proc_mat.emission_box_extents = Vector3(environment_config.fireflies_spawn_ray, environment_config.fireflies_spawn_height, environment_config.fireflies_spawn_ray)
			
#endregion

#region Wind

func toggle_wind(value: bool):
	is_windy = value
	if particles_wind:
		particles_wind.emitting = is_windy

	_apply_wind_state()

#disable wind and set default values and materials
func init_wind():
	if particles_wind:
		particles_wind.visible = true
		particles_wind.emitting = false
		particles_wind.amount = environment_config.wind_amount
		var proc_mat_wind = particles_wind.process_material as ParticleProcessMaterial
		if proc_mat_wind:
			proc_mat_wind.emission_box_extents = Vector3(environment_config.wind_spawn_ray, environment_config.wind_spawn_height, environment_config.wind_spawn_ray)

	if environment_config:
		_apply_cloud_config()
		_apply_wind_config()
		_apply_dust_config()
		
func _apply_wind_config() -> void:
	RenderingServer.global_shader_parameter_set("global_wind_direction", environment_config.wind_direction)
	RenderingServer.global_shader_parameter_set("global_wind_scale", environment_config.wind_scale)
	if particles_wind:
		var proc_mat_wind = particles_wind.process_material as ParticleProcessMaterial
		if proc_mat_wind:
			var wind_direction_3d := Vector3(environment_config.wind_direction.x, 0.0, environment_config.wind_direction.y)
			if wind_direction_3d.length_squared() > 0.0:
				var wind_axis := wind_direction_3d.normalized()
				var wind_cross := wind_axis.cross(Vector3.UP)
				if wind_cross.length_squared() == 0.0:
					wind_cross = Vector3.FORWARD
				else:
					wind_cross = wind_cross.normalized()
				var wind_up := wind_cross.cross(wind_axis).normalized()
				proc_mat_wind.direction = wind_axis
				particles_wind.global_transform = Transform3D(Basis(wind_cross, wind_axis, wind_up), particles_wind.global_position)

	_apply_wind_state()

func _apply_wind_state() -> void:
	if environment_config == null:
		return

	var active_wind_speed := environment_config.wind_speed if is_windy else 0.0
	var active_wind_strength := environment_config.wind_strength if is_windy else 0.0
	RenderingServer.global_shader_parameter_set("global_wind_speed", active_wind_speed)
	RenderingServer.global_shader_parameter_set("global_wind_strength", active_wind_strength)

func _apply_cloud_config() -> void:
	var dir = environment_config.wind_direction
	var cloud_scale = environment_config.cloud_scale
	var cloud_speed = environment_config.cloud_speed
	var envshadows_scale_rate = environment_config.cloud_scale_envshadows_rate
	var envshadows_speed_rate = environment_config.cloud_speed_envshadows_rate

	if environment and environment.environment and environment.environment.sky:
		var sky_mat = environment.environment.sky.sky_material as ShaderMaterial
		if sky_mat:
			sky_mat.set_shader_parameter("cloud_direction", dir)
			sky_mat.set_shader_parameter("cloud_scale", cloud_scale)
			sky_mat.set_shader_parameter("cloud_speed", cloud_speed)
			
	var env_shadow_mat = environment_shadows.get_surface_override_material(0) if environment_shadows else null
	if env_shadow_mat:
		env_shadow_mat.set_shader_parameter("direction", dir)
		env_shadow_mat.set_shader_parameter("cloud_scale", cloud_scale * envshadows_scale_rate)
		env_shadow_mat.set_shader_parameter("cloud_speed", cloud_speed * envshadows_speed_rate)

	if environment_config.material_clouds:
		environment_config.material_clouds.set_shader_parameter("direction", dir)
		environment_config.material_clouds.set_shader_parameter("cloud_scale", cloud_scale * envshadows_scale_rate)
		environment_config.material_clouds.set_shader_parameter("cloud_speed", cloud_speed * envshadows_speed_rate)
			
#endregion

#region Lightning

func toggle_storm(value: bool):
	is_storm = value

#set lightning
func init_lightning():
	lightning_light.name = "Lightning Light"
	lightning_light.transform = Transform3D(
		Vector3(-0.6238797, -0.342596,   0.7024259),   # asse X
		Vector3(0.0,         0.89879405,  0.43837115),  # asse Y
		Vector3(-0.7815204,  0.27349085, -0.56073934),  # asse Z
		Vector3(0.0,         0.0,         30.411049)    # origine
	)

	lightning_light.light_color = Color(0.8, 1.0, 1.0, 1.0)
	lightning_light.light_energy = 0.0
	lightning_light.shadow_enabled = true
	add_child(lightning_light)

	create_lightning_sprite()
	init_lightning_timer()

func create_lightning_sprite():
	lightning_sprite = Sprite3D.new()
	lightning_sprite.name = "Lightning Sprite"
	
	lightning_sprite.transform = Transform3D(
		Basis(
			Vector3(4, 0, 0),
			Vector3(0, 7.32, 0),
			Vector3(0, 0, 7.3)
		),
		Vector3(0, 17.324, 30.411049)
	)
	
	lightning_sprite.modulate = Color(1, 1, 1, 0)
	lightning_sprite.billboard = BaseMaterial3D.BILLBOARD_ENABLED
	lightning_sprite.alpha_cut = SpriteBase3D.ALPHA_CUT_DISCARD
	
	#Add Texture
	lightning_sprite.texture = load(environment_config.lightning_texture)
	
	add_child(lightning_sprite)

#set next lightning timer
func init_lightning_timer():
	timer_next_lightning = randf_range(environment_config.lightning_min_time, environment_config.lightning_max_time)

func start_lightning():
	if lightning_light:
		var tween_lighting = create_tween()
		lightning_light.light_energy = 5.0
		
		tween_lighting.tween_interval(0.05)
		tween_lighting.tween_callback(func(): lightning_light.light_energy = 0.0)
		tween_lighting.tween_interval(0.05)
		tween_lighting.tween_callback(func(): lightning_light.light_energy = 2.0)
		tween_lighting.tween_method(func(val): lightning_light.light_energy = val, 2.0, 0.0, 0.2)

	var lightning_number = randi_range(environment_config.lightning_min, environment_config.lightning_max)
	
	for i in lightning_number:
		if lightning_sprite:
			var random_angle = randf() * TAU 
			var distance = randf_range(10.0, 40.0) 
			
			lightning_sprite.position = Vector3(cos(random_angle) * distance, 10.0, sin(random_angle) * distance)
			
			lightning_sprite.modulate = environment_config.lightning_color
			lightning_sprite.modulate.a = 1.0 
			
			var s = randf_range(environment_config.lightning_scale_min, environment_config.lightning_scale_max)
			lightning_sprite.scale = Vector3(s, s, s)
			
			var tween_sprite = create_tween()
			tween_sprite.tween_interval(0.2) 
			tween_sprite.tween_property(lightning_sprite, "modulate:a", 0.0, 0.4)
		
		#Thunder sound; separated routine to do not stop the loop
		_play_thunder_delayed(randf_range(0.5, 2.5))

		if lightning_number > 1:
			await get_tree().create_timer(randf_range(0.05, 0.15)).timeout
			
func _play_thunder_delayed(delay: float):
	await get_tree().create_timer(delay).timeout

	if thunder_sounds.is_empty() or not thunder_audio_player:
		return

	thunder_audio_player.stream = thunder_sounds[randi() % thunder_sounds.size()]
	thunder_audio_player.pitch_scale = randf_range(0.9, 1.1)
	thunder_audio_player.play()

#endregion

#region Rain

#disable rain
func init_rain():
	if particles_rain:
		particles_rain.visible = true
		particles_rain.emitting = false
	if rain_audio_player:
		rain_audio_player.volume_db = environment_config.rain_audio_volume_db
		rain_audio_player.stop()
	RenderingServer.global_shader_parameter_set("global_rain_puddle_amount", 0.0)

func set_puddle_amount(value: float):
	puddle_amount = value
	RenderingServer.global_shader_parameter_set("global_rain_puddle_amount", value)

func toggle_rain(value: bool):
	is_raining = value
	
	if not particles_rain:
		return
	
	if is_raining and is_snowing:
		toggle_snow(false)
	
	if rain_tween and rain_tween.is_valid():
		rain_tween.kill()
	
	if puddle_tween and puddle_tween.is_valid():
		puddle_tween.kill()
	
	if is_raining:
		particles_rain.amount_ratio = 0.0
		particles_rain.emitting = true
		if rain_audio_player:
			if not rain_audio_player.playing:
				rain_audio_player.volume_db = -80.0
				_play_random_rain_sound()
			_fade_rain_audio(environment_config.rain_audio_volume_db, false)
		rain_tween = create_tween()
		rain_tween.tween_property(particles_rain, "amount_ratio", 1.0, environment_config.rain_fade_time)
		puddle_tween = create_tween()
		puddle_tween.tween_method(set_puddle_amount, puddle_amount, 1.0, environment_config.puddle_form_time)
	else:
		if rain_audio_player:
			_fade_rain_audio(-80.0, true)
		rain_tween = create_tween()
		rain_tween.tween_property(particles_rain, "amount_ratio", 0.0, environment_config.rain_fade_time)
		rain_tween.tween_callback(func():
			particles_rain.emitting = false
			trigger_after_rain())
		puddle_tween = create_tween()
		puddle_tween.tween_method(set_puddle_amount, puddle_amount, 0.0, environment_config.puddle_dry_time)

func trigger_morning() -> void:
	spawn_single_godray()

func trigger_after_rain() -> void:
	if day_time < 2.5:
		var count: int = randi_range(1, environment_config.godray_max_rain)
		for i in count:
			spawn_single_godray()

func spawn_single_godray() -> void:
	var ray: Node3D = godray.instantiate()
	get_tree().current_scene.add_child(ray)

	var random_x: float = randf_range(-environment_config.godray_spawn_radius, environment_config.godray_spawn_radius)
	var random_z: float = randf_range(-environment_config.godray_spawn_radius, environment_config.godray_spawn_radius)

	var center := Vector3.ZERO
	var anchor_pos = _get_weather_anchor_position()
	if anchor_pos != null:
		center = anchor_pos
	ray.global_position = Vector3(center.x + random_x, environment_config.godray_spawn_height, center.z + random_z)
	if ray.has_method("set_visual_rotation_degrees"):
		ray.call("set_visual_rotation_degrees", environment_config.godray_rotation_degrees)
	else:
		ray.rotation_degrees = environment_config.godray_rotation_degrees
	if ray.has_method("set_visual_scale"):
		ray.call("set_visual_scale", environment_config.godray_scale)
	else:
		ray.scale = environment_config.godray_scale
	if ray.has_method("orient_to_camera"):
		ray.call("orient_to_camera")
	
func _play_random_rain_sound() -> void:
	if rain_sounds.is_empty() or rain_audio_player == null:
		return

	rain_audio_player.stream = rain_sounds[randi() % rain_sounds.size()]
	rain_audio_player.pitch_scale = 1.0
	rain_audio_player.volume_db = minf(rain_audio_player.volume_db, environment_config.rain_audio_volume_db)
	rain_audio_player.play()

func _on_rain_audio_player_finished() -> void:
	if is_raining:
		_play_random_rain_sound()

func _fade_rain_audio(target_volume_db: float, stop_after_fade: bool) -> void:
	if rain_audio_player == null:
		return

	if rain_audio_tween and rain_audio_tween.is_valid():
		rain_audio_tween.kill()

	rain_audio_tween = create_tween()
	rain_audio_tween.tween_property(
		rain_audio_player,
		"volume_db",
		target_volume_db,
		environment_config.rain_fade_time	#same value of the rain particles
	)
	if stop_after_fade:
		rain_audio_tween.tween_callback(func(): rain_audio_player.stop())

#endregion

#region Snow

func toggle_snow(value: bool):
	is_snowing = value
	
	if is_snowing and is_raining:
		toggle_rain(false)
	
	# Fade particelle neve
	if particles_snow:
		if snow_particles_tween and snow_particles_tween.is_valid():
			snow_particles_tween.kill()
		
		if is_snowing:
			particles_snow.amount_ratio = 0.0
			particles_snow.visible = true
			particles_snow.emitting = true
			snow_particles_tween = create_tween()
			snow_particles_tween.tween_property(particles_snow, "amount_ratio", 1.0, environment_config.snow_fade_time)
			
			start_snow_accumulation()
		else:
			snow_particles_tween = create_tween()
			snow_particles_tween.tween_property(particles_snow, "amount_ratio", 0.0, environment_config.snow_fade_time)
			snow_particles_tween.tween_callback(func():
				particles_snow.emitting = false)
			start_snow_melt()

	var target_snow_amount: float = 1.0 if is_snowing else 0.0
	if snow_tween and snow_tween.is_valid():
		snow_tween.kill()
	snow_tween = create_tween()
	snow_tween.tween_method(init_snow_amount, actual_snow_amount, target_snow_amount, environment_config.snow_transaction_time)

#disable snow and set default values and shader
func init_snow(value: float = 0.0):
	actual_snow_amount = value
	RenderingServer.global_shader_parameter_set("global_snow_amount", value)
	
	if particles_snow:
		particles_snow.emitting = false
		
	if environment_config:
		RenderingServer.global_shader_parameter_set("global_snow_threshold", environment_config.snow_threshold)
		RenderingServer.global_shader_parameter_set("global_snow_color", environment_config.snow_color)
		RenderingServer.global_shader_parameter_set("global_snow_accumulation_speed", environment_config.snow_accumulation_speed)
		RenderingServer.global_shader_parameter_set("global_snow_melt_speed", environment_config.snow_melt_speed)
		RenderingServer.global_shader_parameter_set("global_snow_start_time", -1.0)
		RenderingServer.global_shader_parameter_set("global_snow_melt_time", -1.0)
		
func init_snow_amount(value: float):
	actual_snow_amount = value
	RenderingServer.global_shader_parameter_set("global_snow_amount", value)

func start_snow_accumulation() -> void:
	RenderingServer.global_shader_parameter_set("global_snow_melt_time", -1.0)
	RenderingServer.global_shader_parameter_set("global_snow_start_time", Time.get_ticks_msec() / 1000.0)

func start_snow_melt() -> void:
	RenderingServer.global_shader_parameter_set("global_snow_melt_time", Time.get_ticks_msec() / 1000.0)
	RenderingServer.global_shader_parameter_set("global_snow_melt_speed", environment_config.snow_melt_speed)

#endregion

#region Post-Process

func toggle_dust(value: bool) -> void:
	if environment_dust:
		environment_dust.visible = value

func toggle_blur(value: bool) -> void:
	if blur:
		blur.visible = value

	ApplyPostProcessBlurConfig()

func toggle_shadows(value: bool) -> void:
	if environment_shadows:
		environment_shadows.visible = value

func ApplyPostProcessBlurConfig() -> void:
	if blur == null:
		return

	var blur_material := blur.material as ShaderMaterial
	if blur_material:
		blur_material.set_shader_parameter("blur_amount", environment_config.blur_amount)

func _apply_dust_config() -> void:
	var dust_color: Color = environment_config.dust_color
	var dust_speed: float = environment_config.dust_speed
	var dust_density: float = environment_config.dust_density
	var dust_size_min: float = environment_config.dust_size_min
	var dust_size_max: float = environment_config.dust_size_max

	if environment_dust:
		var dust_mat = environment_dust.material as ShaderMaterial
		if dust_mat:
			dust_mat.set_shader_parameter("dust_color", dust_color)
			dust_mat.set_shader_parameter("dust_speed", dust_speed)
			dust_mat.set_shader_parameter("dust_density", dust_density)
			dust_mat.set_shader_parameter("dust_size_min", dust_size_min)
			dust_mat.set_shader_parameter("dust_size_max", dust_size_max)

#endregion
