
local level_sound = {}
local tag_from_level = {}

local pl = {}

function br_core.register_level_sound(def)
    level_sound[def.level] = def
end

local function get_centre_of_segment(pos)
    local level_height = br_core.level_height
    local level = math.round(pos.y / level_height)
    local level_def = br_core.level[level]
    local segsize = (level_def and level_def.segsize) or 20
    pos = vector.multiply(vector.round(vector.divide(pos, segsize)), segsize)
    pos.y = pos.y + 20
    return pos
end

function br_core.sound_stop_level(level, player, spec)
end


function br_core.sound_stop_tag(tag, player, spec)
    if not pl[player][tag] then return end
    for i, sound in pairs(pl[player][tag]) do
        if spec and spec.fade and not sound.fading then
            minetest.sound_fade(sound.handle, spec.fade, 0)
            sound.time = 1
            sound.fading = true
        else
            minetest.sound_stop(sound.handle)
            table.remove(pl[player][tag], i)
        end
    end
end
function br_core.sound_get_tag(tag, player)
    return pl[player][tag] or nil
end
function br_core.sound_play(tag, player, spec)
    -- make a list of tags that play on each level
    if not tag_from_level[spec.level] then tag_from_level[spec.level] = {} end
    tag_from_level[spec.level][#tag_from_level[spec.level]+1] = tag

    if not pl[player] then pl[player] = {} end
    if not pl[player][tag] then pl[player][tag] = {} end
    local ta = pl[player][tag]
    if spec.single_sound and #ta >= 1 then br_core.sound_stop_tag(tag, player, spec) end
    local spec_copy = table.copy(spec)
    if spec.fade then
        spec_copy.gain = 0.001
        spec_copy.handle = minetest.sound_play(spec.name, spec_copy)
        minetest.sound_fade(spec_copy.handle, spec.fade, spec.gain)
    else
        spec_copy.handle = minetest.sound_play(spec.name, spec_copy)
    end
    ta[#ta+1] = spec_copy
    -- minetest.log("playing sound " .. (spec.name or "nil"))
end

local function countdown(dtime, player)
    -- go through each tag
    for tagname, sound_list in pairs(pl[player]) do
        -- go through each sound in the tag
        for k, sound in pairs(sound_list) do
            if sound.time == nil then sound.time = 1 end
            sound.time = sound.time - dtime
            if (not sound.fading) and sound.fade and sound.time < 2 then
                minetest.sound_fade(sound.handle, sound.fade, 0)
                -- minetest.log("FADING sound because "..sound.time.." < 2")
                sound.fading = true
            elseif sound.time < 0 then
                minetest.sound_stop(sound.handle)
                table.remove(sound_list, k)
                -- minetest.log("stopping sound because "..sound.time.." < 0")
            end
        end
    end
end

local timer = 0
minetest.register_globalstep(function(dtime)
    if br_core.dev_mode then return end
    if timer < 1 then timer = timer + dtime return
    else timer = 0 end
    for i, player in pairs(minetest.get_connected_players()) do
        if not pl[player] then pl[player] = {} end
        countdown(1, player)
        local pli = br_spawn.player[player:get_player_name()]
        if level_sound[pli.level] then
            level_sound[pli.level]:on_play(1, player)
        end
    end
end)


br_core.register_on_changed_level(function(player, from_level, to_level)
    if tag_from_level[from_level] then
        for i, tag in pairs(tag_from_level[from_level]) do
            br_core.sound_stop_tag(tag, player, nil)
        end
    end
end)


br_core.register_level_sound({
    level = 0,
    time = 8,
    on_play = function(self, dtime, player)
        if player:get_pos().y < -9 then return end
        local can_play = false
        local playing = br_core.sound_get_tag("level0_hum", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("level0_hum", player, {
                name = "br_fluorescent_hum",
                level = 0,
                gain = (br_sounds.master or 1) * (0.02 * math.random() + 0.005),
                fade = 0.02,
                to_player = player:get_player_name(),
                time = 8,
            })
        end
        -- shhhhhhh don't tell anyone
        local pos = player:get_pos()
        local dist = vector.distance(pos, vector.new(3000,pos.y,0)) / 5000
        if 0.2 - (dist) > 0 then
            can_play = false
            local playing2 = br_core.sound_get_tag("level0_beep", player)
            if (playing2 == nil or #playing2 < 1)
            or (playing2 and (playing2[1].time <= 2) and #playing2 == 1) then can_play = true end
            if can_play then
                br_core.sound_play("level0_beep", player, {
                    name = "br_level_0_beep",
                    level = 0,
                    gain = (br_sounds.master or 1) * math.max(0.2 - (dist), 0),
                    to_player = player:get_player_name(),
                    time = 18,
                })
            end
        end
    end
})


br_core.register_level_sound({
    level = 1,
    time = 8,
    scare_timer = math.random(60, 80),
    on_play = function(self, dtime, player)
        local can_play = false
        local playing = br_core.sound_get_tag("level1_hum", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("level1_traffic", player, {
                name = "br_traffic_distant_cc0_klangfabrik",
                level = 1,
                gain = (br_sounds.master or 1) * (0.1 * math.random() + 0.01),
                fade = 0.02,
                to_player = player:get_player_name(),
                time = 8,
            })
            br_core.sound_play("level1_hum", player, {
                name = "br_fluorescent_hum",
                level = 1,
                gain = (br_sounds.master or 1) * (0.002 * math.random() + 0.001),
                fade = 0.01,
                to_player = player:get_player_name(),
                time = 8,
            })
        end

        self.scare_timer = self.scare_timer - dtime
        playing = br_core.sound_get_tag("level1_footsteps", player)
        if (self.scare_timer < 0) and (playing == nil or #playing < 1) then
            self.scare_timer = math.random(60, 240)
            local dir = player:get_look_horizontal() + ((math.random(-1,1)) * 1.5) + 3.14
            dir = vector.multiply(minetest.yaw_to_dir(dir), 20)
            local pos = vector.add(player:get_pos(), dir)
            if not minetest.line_of_sight(pos, player:get_pos()) then
                br_core.sound_play("level2_footsteps", player, {
                    name = "br_m_trigger_footsteps_concrete",
                    level = 1,
                    gain = (br_sounds.master or 1) * 1,
                    max_hear_distance = 100,
                    to_player = player:get_player_name(),
                    time = 6,
                    pos = pos
                })
            else
                self.scare_timer = 2
            end
        end
    end
})


br_core.register_level_sound({
    level = 2,
    time = 8,
    scare_timer = math.random(60, 120),
    on_play = function(self, dtime, player)
        local can_play = false
        local playing = br_core.sound_get_tag("level2_hum", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("level2_hum", player, {
                name = "br_fluorescent_hum",
                level = 2,
                gain = (br_sounds.master or 1) * (0.003 * math.random() + 0.001),
                fade = 0.01,
                to_player = player:get_player_name(),
                time = 8,
            })
            br_core.sound_play("2_dripsandwater", player, {
                name = "br_dripsandwater_WTFPL_Sumianvoice",
                level = 2,
                gain = (br_sounds.master or 1) * (math.random()*0.3),
                fade = 0.2,
                max_hear_distance = 50,
                to_player = player:get_player_name(),
                time = 40,
            })
        end

        self.scare_timer = self.scare_timer - dtime
        playing = br_core.sound_get_tag("level2_footsteps", player)
        if (self.scare_timer < 0) and (playing == nil or #playing < 1) then
            self.scare_timer = math.random(60, 240)
            local dir = player:get_look_horizontal() + ((math.random(-1,1)) * 1.5) + 3.14
            dir = vector.multiply(minetest.yaw_to_dir(dir), 20)
            br_core.sound_play("level2_footsteps", player, {
                name = "br_m_trigger_footsteps_concrete",
                level = 1,
                gain = 1,
                max_hear_distance = 160,
                to_player = player:get_player_name(),
                time = 6,
                pos = vector.add(player:get_pos(), dir)
            })
        end
    end
})


br_core.register_level_sound({
    level = 5,
    time = 8,
    on_play = function(self, dtime, player)
        local can_play = false
        local playing = br_core.sound_get_tag("level5_hum", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("level5_hum", player, {
                name = "br_fluorescent_hum",
                level = 5,
                gain = (br_sounds.master or 1) * (0.02 * math.random() + 0.001),
                fade = 0.01,
                to_player = player:get_player_name(),
                time = 8,
            })
        end
    end
})


br_core.register_level_sound({
    level = 31,
    time = 10,
    on_play = function(self, dtime, player)
        local can_play = false
        local playing = br_core.sound_get_tag("level31_hum", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("level31_hum", player, {
                name = "br_traffic_distant_cc0_klangfabrik",
                level = 31,
                gain = (br_sounds.master or 1) * (0.5 * math.random() + 0.3),
                fade = 0.3,
                -- single_sound = true,
                max_hear_distance = 50,
                to_player = player:get_player_name(),
                time = 10,
            })
        end
    end
})



br_core.register_level_sound({
    level = 37,
    time = 40,
    on_play = function(self, dtime, player)
        local can_play = false
        local playing = br_core.sound_get_tag("37_dripsandwater", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("37_dripsandwater", player, {
                name = "br_dripsandwater_WTFPL_Sumianvoice",
                level = 37,
                gain = (br_sounds.master or 1) * math.random(),
                fade = 0.2,
                -- single_sound = true,
                max_hear_distance = 50,
                to_player = player:get_player_name(),
                -- pos = get_centre_of_segment(player:get_pos()),
                time = 40,
            })
        end
    end
})



br_core.register_level_sound({
    level = 45,
    time = 7,
    on_play = function(self, dtime, player)
        local can_play = false
        local playing = br_core.sound_get_tag("level45_wind", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("level45_wind", player, {
                name = "br_wind_noise_WTFPL_Sumianvoice",
                level = 45,
                gain = (br_sounds.master or 1) * (0.2 * math.random() + 0.1),
                fade = 0.15,
                to_player = player:get_player_name(),
                time = 7,
            })
        end
    end
})



br_core.register_level_sound({
    level = 62,
    time = 8,
    on_play = function(self, dtime, player)
        local can_play = false
        local playing = br_core.sound_get_tag("level62_hum", player)
        if playing == nil or #playing < 1
        or (playing and playing[1].fading and #playing <= 1) then can_play = true end
        if can_play then
            br_core.sound_play("level62_hum", player, {
                name = "br_fluorescent_hum",
                level = 62,
                gain = (br_sounds.master or 1) * (0.04 * math.random() + 0.001),
                fade = 0.01,
                to_player = player:get_player_name(),
                time = 8,
            })
        end
    end
})
