
local function light_gain(player)
    local light = minetest.get_natural_light(vector.offset(player:get_pos(), 0, 0.5, 0), 0.5)
    if not light then return 0 end
    light = math.min(light, 14)
    light = math.max(light / 14, 0.0001)
    return light
end

local function do_rain_sound(player, meta)
    meta.light_gain = light_gain(player)
    if meta.sound_rain then
        minetest.sound_fade(meta.sound_rain, 0.2, 0)
    end
    meta.sound_rain = minetest.sound_play("pmb_weather_rain_medium", {
        gain = 0.0001,
        to_player = player:get_player_name(),
        loop = true,
        pitch = (meta.light_gain or 1) * 0.5 + 0.5,
        start_time = math.random() * 2 + 1
    })
    minetest.sound_fade(meta.sound_rain, 0.3, meta.light_gain * 0.7)
end

local function do_lightning(player, meta)
    local pos = player:get_pos()
    local yaw = math.random() * math.pi * 2
    local lpos = vector.add(pos, vector.multiply(minetest.yaw_to_dir(yaw), 30))

    -- minetest.log("lighting!")

    local dist = math.random() * 0.3 + 0.7

    pmb_weather.force_set_lighting(player, 0.7 * dist)
    pmb_weather.request_lighting(player, 0.05)
    pmb_weather.fix_timeofday(player, 4 * dist, true)

    minetest.after(0.5, function()
        if not player then return end
        pmb_weather.fix_timeofday(player, 1, true)
    end)
    minetest.after(math.max(dist * 3 - 2, 0.1), minetest.sound_play, "pmb_weather_thunder", {
        gain = 5 * dist * (meta.light_gain ^ 2),
        pitch = (math.random() * 0.2 + 0.8) * dist * math.max(meta.light_gain, 0.7),
        pos = lpos,
        max_hear_distance = 100,
    })
end

local rain_spawn_size = 30

local function start_weather(player, meta, from_weather)
    if meta.active then return end
    meta.active = true

    meta.particlespawners[#meta.particlespawners+1] = minetest.add_particlespawner({
        amount = 100,
        time = 0,
        collisiondetection = true,
        collision_removal = true,
        object_collision = false,
        attached = player, -- to object ref
        vertical = true, -- If true face player using y axis only:-- If true face player using y axis only:
        texture = "pmb_weather_rain_heavy.png",
        playername = player:get_player_name(), -- only to this player
        glow = 0,
        minpos = vector.new(-rain_spawn_size, 15, -rain_spawn_size),
        maxpos = vector.new(rain_spawn_size, 20, rain_spawn_size),
        minvel = vector.new(-1, -20, -1),
        maxvel = vector.new(1, -10, 1),
        minacc = {x=0, y=0, z=0},
        maxacc = {x=0, y=0, z=0},
        minexptime = 3,
        maxexptime = 5,
        minsize = 50,
        maxsize = 50,
    })
    meta.particlespawners[#meta.particlespawners+1] = minetest.add_particlespawner({
        amount = 200,
        time = 0,
        collisiondetection = true,
        collision_removal = true,
        object_collision = false,
        attached = player, -- to object ref
        vertical = true, -- If true face player using y axis only:-- If true face player using y axis only:
        texture = "pmb_weather_rain_medium.png",
        playername = player:get_player_name(), -- only to this player
        glow = 0,
        minpos = vector.new(-rain_spawn_size*0.5, 15, -rain_spawn_size*0.5),
        maxpos = vector.new(rain_spawn_size*0.5, 20, rain_spawn_size*0.5),
        minvel = vector.new(-1, -20, -1),
        maxvel = vector.new(1, -10, 1),
        minacc = {x=0, y=0, z=0},
        maxacc = {x=0, y=0, z=0},
        minexptime = 3,
        maxexptime = 5,
        minsize = 10,
        maxsize = 10,
    })
    meta.particlespawners[#meta.particlespawners+1] = minetest.add_particlespawner({
        amount = 100,
        time = 0,
        collisiondetection = true,
        collision_removal = true,
        object_collision = false,
        attached = player, -- to object ref
        vertical = true, -- If true face player using y axis only:-- If true face player using y axis only:
        texture = "pmb_weather_rain.png",
        playername = player:get_player_name(), -- only to this player
        glow = 0,
        minpos = vector.new(-rain_spawn_size*0.2, 15, -rain_spawn_size*0.2),
        maxpos = vector.new(rain_spawn_size*0.2, 20, rain_spawn_size*0.2),
        minvel = vector.new(-1, -20, -1),
        maxvel = vector.new(1, -10, 1),
        minacc = {x=0, y=0, z=0},
        maxacc = {x=0, y=0, z=0},
        minexptime = 3,
        maxexptime = 5,
        minsize = 8,
        maxsize = 8,
    })
    pmb_weather.request_sky(player, {
        base_color = "#e3e3e3",
        type = "regular",
        clouds = true,
        sky_color = {
            day_sky = pmb_weather.color(51, 51, 57),
            day_horizon = pmb_weather.color(51, 51, 57),
            dawn_sky = pmb_weather.color(95, 95, 125),
            dawn_horizon = pmb_weather.color(80, 70, 112),
            night_sky = pmb_weather.color(50, 41, 72),
            night_horizon = pmb_weather.color(50, 41, 71),
            indoors = pmb_weather.color(59, 59, 69),
            fog_sun_tint = pmb_weather.color(38, 57, 101),
            fog_moon_tint = pmb_weather.color(38, 57, 141),
            fog_tint_type = "custom",
        }
    })
    -- player:set_sky()
    player:set_clouds({
        density = 0.9,
        thickness = 20,
        ambient = "#000",
        color = "#555560ff",
        height = 100,
        speed = {x=4, z=2}
    })
    player:set_sun({
        visible = true,
        texture = "blank.png",
        sunrise_visible = true,
    })
    player:set_moon({
        visible = true,
        texture = "blank.png",
    })
    player:set_stars({
        visible = false,
    })
    pmb_weather.request_lighting(player, 0.05)
    do_rain_sound(player, meta)
    minetest.sound_fade(meta.sound_rain, 0.2, light_gain(player))
end

local function stop_weather(player, meta, to_weather)
    if not meta.active then return end
    meta.active = false
    for i, id in ipairs(meta.particlespawners) do
        minetest.delete_particlespawner(id)
    end
    minetest.sound_fade(meta.sound_rain, 0.1, 0)
    meta.particlespawners = {}
    meta.sound_rain = nil
end

pmb_weather.register_weather("heavy_rain", {
    biomes = {
        -- "tundra",
        -- "underworld_bridges",
        "ocean",
        "ash_forest",
        "oak_forest_dense_1",
        "ruined_valley",
        "oak_forest",
        "taiga_tall",
        -- "underworld_arlior_growth",
        "taiga_tall_dense",
        "grasslands_valley",
        "oak_forest_dense",
        "grasslands",
        -- "underworld",
        "ash_forest_dense",
        -- "desert_dunes",
        -- "underworld_coral",
        "boulder_valley",
        -- "underworld_arlior_meadow",
        -- "tundra_shore",
    },
    chance = 100,
    min_duration = 120, -- sec
    max_duration = 600,
    light_factor = 0.4,
    on_enter_weather = function(player, meta, from_weather)
        local light = light_gain(player)
        if light < 0.1 then stop_weather(player, meta, "nil") return
        else start_weather(player, meta, "nil") end
    end,
    on_leave_weather = function(player, meta, to_weather)
        stop_weather(player, meta, to_weather)
        player:set_clouds()
        player:set_moon()
        player:set_sun()
        player:set_stars()
    end,
    on_step = function(player, meta, dtime)
        meta.update_tick = (meta.update_tick or 1) - dtime
        if meta.update_tick <= 0 then
            meta.update_tick = 1
            local light = light_gain(player)
            if light < 0.1 then stop_weather(player, meta, "nil") return
            else
                if math.random() < 0.1 then
                    do_lightning(player, meta)
                end
                start_weather(player, meta, "nil")
            end
            if light ~= meta.light_gain then
                do_rain_sound(player, meta)
            end
        end
    end,
    next_weather = {
        ["light_rain"] = 0.8
    },
    fallbacks = {
        "light_rain",
        "clear",
    },
})
