local mod_name = minetest.get_current_modname()
local mod_path = minetest.get_modpath(mod_name)
local S = minetest.get_translator(mod_name)

aom_climate.wind = {}

if aom_climate.has_aom_settings then
    aom_settings.register_setting("climate_wind_change_speed", 1, S("Wind change speed"), "server")
end

aom_climate.wind._np = {
    wind_x = {
        offset = 0,
        scale = 1,
        spread = {x = 2000, y = 4000, z = 2000},
        seed = 9698 + (minetest.get_mapgen_setting("seed") or 0),
        octaves = 4,
        persist = 0.99,
        lucanarity = 1.7,
        vel = vector.new(1, 1.34, 1) * 0.5,
    },
    wind_x2 = {
        offset = 0,
        scale = 1,
        spread = vector.new(500, 1000, 500),
        seed = 934 + (minetest.get_mapgen_setting("seed") or 0),
        octaves = 1,
        persist = 0.9,
        lucanarity = 2.1,
        vel = vector.new(1.27, 1.1, 0.91) * 0.5,
    },
    wind_z = {
        offset = 0,
        scale = 1,
        spread = {x = 2000, y = 4000, z = 2000},
        seed = 7490 + (minetest.get_mapgen_setting("seed") or 0),
        octaves = 4,
        persist = 0.99,
        lucanarity = 1.7,
        vel = vector.new(1.23, -1.2, -0.86) * 0.5,
    },
    wind_z2 = {
        offset = 0,
        scale = 1,
        spread = vector.new(500, 1000, 500),
        seed = 585 + (minetest.get_mapgen_setting("seed") or 0),
        octaves = 1,
        persist = 0.9,
        lucanarity = 2.17,
        vel = vector.new(1.31, -1.6, -0.94) * 0.5,
    },
    wind_gust = {
        offset = 0.7,
        scale = 0.3,
        spread = {x = 300, y = 300, z = 300},
        seed = 8757 + (minetest.get_mapgen_setting("seed") or 0),
        octaves = 4,
        persist = 0.9,
        lucanarity = 2.84,
        vel = vector.new(3.82, -2.23, -4.39),
    },
}

aom_climate.wind._perlin = {}
aom_climate.wind._perlin_map = {}
for k, np in pairs(aom_climate.wind._np) do
    aom_climate.wind._perlin[k] = PerlinNoise(np)
    aom_climate.wind._perlin_map[k] = PerlinNoiseMap(np, {x=80,y=80,z=80})
end


aom_climate.wind.positions = minetest.deserialize(aom_climate.mod_storage:get_string("wind_positions"), true) or {}


function aom_climate.wind.get_large_scale_wind_dir(pos)
    local layers = aom_climate.wind.positions
    local perlin = aom_climate.wind._perlin
    local v = (vector.new(
        (perlin.wind_x:get_3d(pos + layers.wind_x)),
        0,
        (perlin.wind_z:get_3d(pos + layers.wind_z))
    ))
    v.x = math.min(1, math.max(v.x, -1))
    v.z = math.min(1, math.max(v.z, -1))
    return v
end

function aom_climate.wind.get_wind_dir(pos, ignore_gusts)
    local layers = aom_climate.wind.positions
    local perlin = aom_climate.wind._perlin
    local gust = ignore_gusts and 1 or perlin.wind_gust:get_3d(pos + layers.wind_gust)
    -- gust = 1
    local v = (vector.new(
        (perlin.wind_x:get_3d(pos + layers.wind_x) + perlin.wind_x2:get_3d(pos + layers.wind_x2)) * 0.5,
        0,
        (perlin.wind_z:get_3d(pos + layers.wind_z) + perlin.wind_z2:get_3d(pos + layers.wind_z2)) * 0.5
    ))
    v = v * gust
    local len = vector.length(v)
    if len > 1 then
        v = v * (1/len)
    end
    return v
end

-- only returns wind if there is line of sight for the wind to actually come from
function aom_climate.wind.get_wind_or_nil(pos, ignore_gusts)
    local dir = aom_climate.wind.get_wind_dir(pos, ignore_gusts)
    local light = minetest.get_natural_light(pos, 0.5)
    if ((light or 0) > 2) and minetest.line_of_sight(pos, pos - dir * 20) then
        return dir
    else
        return nil
    end
end

local _update_timer = 0
local _maxnum = 2^26
function aom_climate.wind.on_step(dtime)
    local layers = aom_climate.wind.positions
    local speedmult = (aom_climate.has_aom_settings and aom_settings.get_setting(nil, "climate_wind_change_speed", 1)) or 1
    for name, np in pairs(aom_climate.wind._np) do
        layers[name] = (layers[name] or vector.new(0, 0, 0)) + np.vel * (dtime * speedmult)
        for i, k in ipairs({"x","y","z"}) do
            if layers[name][k] > _maxnum then
                layers[name][k] = -_maxnum
            elseif layers[name][k] < -_maxnum then
                layers[name][k] = _maxnum
            end
        end
    end

    if _update_timer > 0 then _update_timer = _update_timer - dtime; return
    else _update_timer = _update_timer + 2 end

    aom_climate.mod_storage:set_string("wind_positions", minetest.serialize(aom_climate.wind.positions))
end

minetest.register_globalstep(aom_climate.wind.on_step)

-- for debug
minetest.register_globalstep(function(dtime)
    do return end
    for i, player in ipairs(minetest.get_connected_players()) do
        local dir = aom_climate.wind.get_wind_dir(aom_climate.get_eyepos(player), false)
        local dir_or_nil = aom_climate.wind.get_wind_or_nil(aom_climate.get_eyepos(player), false)
        local len = dir and vector.length(dir)
        len = len or 0
        local color = dir_or_nil and (len < 0.3 and "#0f0" or len < 0.5 and "#ff0" or "#f00") or "#333"
        aom_hud.add_hud(player, "aom_climate:wind", {
            type = "image",
            scale = vector.new(20, len*100, 1),
            text = "blank.png^[noalpha^[colorize:"..color..":255",
            position = {x=0.9, y=0.5}, alignment = {x=1, y=1},
            offset = {x=0, y=0},
            z_index = 199
        })
        aom_hud.add_hud(player, "aom_climate:windbg", {
            type = "image",
            scale = vector.new(20 + 4, 1*100, 1),
            text = "blank.png^[noalpha^[colorize:#000:255",
            position = {x=0.9, y=0.5}, alignment = {x=1, y=1},
            offset = {x=-2, y=0},
            z_index = 190
        }, true)
    end
end)
