
local storage = bb_loop.storage
-- Functions init (defined at end of file in order)

-- must always return a number, the number of connected players. cannot assume that there are any, and should return 0
bb_loop.get_player_count = function() end
bb_loop.clear_players_inv = function() end
bb_loop.get_alive_playercount = function() return 0 end
bb_loop.show_waiting_formspec = function(p_name) end
-- sets the board and moves players there
bb_loop.Start = function()  end
--should return nil if no winner, and winner name if winner
bb_loop.check_for_winner = function() return nil end
-- function to get the inventory formspec, overridden in bb_player, but callable here
bb_loop.get_inv_formspec = function(p_name) return "" end
-- returns the status of the game for player huds (used in bb_player)
function bb_loop.get_hud_status() end


function bb_loop.register_on_load(func) end


function bb_loop.register_on_start(func) end


--return true to end game
function bb_loop.register_playstep(func) end


function bb_loop.register_on_suddendeath(func) end

-- bb_loop.register_on_celebrate(function(winner)), winner can be nil
function bb_loop.register_on_celebrate(func) end





--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- waiting globalstep

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



minetest.register_globalstep(function(dtime)

    -- this globalstep only applies to the waiting phase
    if bb_loop.gamestate ~= "waiting" then return end
    
    -- keep track of waiting time, may be useful
    bb_loop.waitingtimer = bb_loop.waitingtimer + dtime

    local player_count = bb_loop.get_player_count()

    if bb_loop.debug then
        minetest.log("PLAYER_COUNT(1):".. player_count)
        minetest.log("Current_state(1):".. bb_loop.gamestate)
    end

    if player_count == 0 then return end

    if player_count > 1 then 
        --start loading
        bb_loop.gamestate = "loading"
        bb_loop.waitingtimer = 0
        for _,func in ipairs(bb_loop.registered_on_loads) do
            func()
        end

    end
end)


--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- loading globalstep

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


minetest.register_globalstep(function(dtime)

    -- reset the firstrun indicator
    if bb_loop.loadingtimer == 0 then
        bb_loop.firstload = true
    end
    -- this globalstep only applies to the loading phase
    if bb_loop.gamestate ~= "loading" then return end

    -- keep track of loading time, may be useful
    bb_loop.loadingtimer = bb_loop.loadingtimer + dtime
    
    if bb_loop.loadingtimer > bb_loop.loading_time then
        --loading is over, reset and move on to starting phase
        bb_loop.loadingtimer = 0
        bb_loop.gamestate = "running"
        for _,func in ipairs(bb_loop.registered_on_starts) do
            func()
        end

    end
end)





--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- running globalstep

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


minetest.register_globalstep(function(dtime)

    -- this globalstep only applies to the running phase
    
    if bb_loop.gamestate ~= "running" then return end

    -- keep track of running time, start suddendeath if we reach the timer
    bb_loop.runningtimer = bb_loop.runningtimer + dtime

    -- run playstep callbacks
    for _,func in ipairs(bb_loop.registered_playsteps) do
        local end_phase = func()
        if end_phase then 
            bb_loop.gamestate = "celebrate" 
            for _,func in ipairs(bb_loop.registered_on_celebrates) do
                func(bb_loop.winner)
            end
            return
        end
    end


end)






--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- celebrate globalstep

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

local celebrate_first_time = true
minetest.register_globalstep(function(dtime)

    -- this globalstep only applies to the celebrate phase
    if bb_loop.gamestate ~= "celebrate" then return end


    -- keep track of celebrate time, stop the celebration at the end
    bb_loop.celebratetimer = bb_loop.celebratetimer + dtime

    if bb_loop.celebratetimer > bb_loop.celebrate_time then
        bb_loop.winner = nil
        bb_loop.celebratetimer = 0
        minetest.after(0,function()
            --set gamestate to waiting
            bb_loop.gamestate = "waiting"
            --update players inv
            for _,player in pairs(minetest.get_connected_players()) do
                local p_name = player:get_player_name()
                player:set_inventory_formspec(bb_loop.get_inv_formspec(p_name))
            end
            
        end)
        
    end

end)







--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- Functions Defined

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



function bb_loop.register_on_load(func) 
    table.insert(bb_loop.registered_on_loads,func)
end
function bb_loop.register_on_start(func) 
    table.insert(bb_loop.registered_on_starts,func)
end
function bb_loop.register_playstep(func) 
    table.insert(bb_loop.registered_playsteps,func)
end
function bb_loop.register_on_suddendeath(func) 
    table.insert(bb_loop.registered_on_suddendeaths,func)
end

function bb_loop.register_on_celebrate(func) 
    table.insert(bb_loop.registered_on_celebrates,func)
end




-- returns the number of connected players
-- must always return a number, the number of connected players. cannot assume that there are any, and should return 0
bb_loop.get_player_count = function() 
    local count = 0
    local connected = minetest.get_connected_players()
    if connected then
        if #connected then
            count = #connected
        end
    end
    return count
end


--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


-- clears the inventory of all players online
bb_loop.clear_players_inv = function() 

    local connected = minetest.get_connected_players()
    for _ , player in ipairs( connected ) do
        local p_meta = player:get_meta()
        if p_meta:get_string("status") ~= "admin" then
            local inv = player:get_inventory()
            local list = inv:get_list("main")
            for k, v in pairs(list) do
                inv:remove_item("main", v)
            end
            inv:add_item("main", ItemStack("bb_powerup:extra_bomb")) 
            --inv:add_item("main", ItemStack("bb_powerup:extra_speed"))
            inv:add_item("main", ItemStack("bb_powerup:extra_power"))
        end
    end

end


--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


-- returns the # of 
-- alive players as well as the player name of an alive player (will be the winner if #==1)
bb_loop.get_alive_playercount = function() 
    local count = 0
    local name = ""
    for _, player in pairs(minetest.get_connected_players()) do
        local pmeta = player:get_meta()
        local p_status = pmeta:get_string("status")

        if bb_loop.debug then
            minetest.log("error","P_status: "..p_status)
        end

        if p_status == "playing" then
            count = count + 1
            name = player:get_player_name()
        end
    end

    return count, name
end






--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



bb_loop.Start = function()  

    -- clear their inventories to reset any powerups
    bb_loop.clear_players_inv()

    -- update the inv formspec
    for _,player in pairs(minetest.get_connected_players()) do
        local p_name = player:get_player_name()
        player:set_inventory_formspec(bb_loop.get_inv_formspec(p_name))
    end

    
    -- clear the board of items
    for _,obj in pairs(minetest.get_objects_inside_radius(vector.new(0,0,0), 200)) do

        local ent = obj:get_luaentity()
        if ent and ent.name == "__builtin:item" then
            obj:remove()
        end
    end
    -- choose the arena

    local player_count = bb_loop.get_player_count()
    if bb_loop.debug then
        minetest.log("PLAYER_COUNT (2):"..player_count)
        minetest.log("Current_state(2):".. bb_loop.gamestate)
    end
    local arena = bb_schems.get_arena_by_playercount( player_count )
    bb_loop.current_arena = arena
    local arena_name = arena.name
    local arena_spectator_pos = arena.spectator_pos
    local start_locations = arena.spawns

    -- move players to the spectator pos
    for _, player in pairs(minetest.get_connected_players()) do
        local att = player:get_attach()
        if att then 
            att:set_pos(arena_spectator_pos)
        else
            local att = minetest.add_entity(arena_spectator_pos, "bb_player:att")
            player:set_attach(att)

        end
    end
    -- place the arena at the origin
    bb_schems.place_arena( arena_name )

    -- move the players to their starting locations after a time, mark them as playing
    minetest.after(bb_loop.loading_time - 2, function(start_locations)
        local len = #start_locations
        local iter = 1
        for _, player in ipairs(minetest.get_connected_players()) do 
            
            player:set_detach()
            --the iter is required to set extra players at spawn locations (tho that wont happen anyways)
            
            -- they need to be a little off the floor so they dont fall thru it
            local spawn_loc = vector.add(start_locations[iter],vector.new(0,0.1,0))
            local p_name = player:get_player_name()

            minetest.after(0.1,function(p_name)
                local player = minetest.get_player_by_name(p_name)
                if not player then return end
                player:set_pos( spawn_loc )
                
                --show spawning particles and play starting sound
                minetest.after(1,function()
                    minetest.add_particlespawner({
                        amount = 40,
                        time = 1,
                        minpos = vector.add(spawn_loc,vector.new(-.5,-.4,-.5)),
                        maxpos = vector.add(spawn_loc,vector.new(.5,1,.5)),
                        minvel = vector.new(0,1,0),
                        maxvel = vector.new(.01,4,.01),
                        minacc = vector.new(0,0,0),
                        maxacc = vector.new(.01,4,.01),
                        minexptime = 1,
                        maxexptime = 1,
                        minsize = 3,
                        maxsize = 4,
                        collisiondetection = false,            
                        collision_removal = false,
                        object_collision = false,
                        --attached = ObjectRef,
                        vertical = false,
                        texture = "spawn_particle.png",
                        glow = 2,
                    })

                    minetest.sound_play({
                        name = "respawn",
                        gain = 0.6,
                    }, {
                        pos = spawn_loc,
                        max_hear_distance = 10,  
                    }, true)
                end)

            end,p_name)
            
            iter = iter + 1
            if iter > len then 
                iter = 1
            end


            -- mark the player as playing
            local pmeta = player:get_meta()
            pmeta:set_string("status","playing")
            pmeta:set_string("spawn", minetest.serialize( spawn_loc ) )

        end
    
    end,start_locations)

end














--should return nil if no winner yet, "" if everyone is dead and no winner,and winner name if winner there is
bb_loop.check_for_winner = function() 
    
    local number_alive, a_name = bb_loop.get_alive_playercount()
    
    if bb_loop.debug then
        minetest.log("error","NUMBER ALIVE: "..number_alive)
        minetest.log("error","a_name: "..a_name)
    end

    if number_alive == 1 then
        return a_name
    end
    if number_alive == 0 then
        return ""
    end
    return nil 

end




function bb_loop.get_hud_status()
    local status = ""
    if bb_loop.gamestate == "waiting" then
        status = "WAITING FOR PLAYERS"
    end
    if bb_loop.gamestate == "loading" then
        status = "LOADING"
    end
    if bb_loop.gamestate == "running" then
        if bb_loop.suddendeath == false then
            local timer = bb_loop.suddendeath_timer - bb_loop.runningtimer
            local timermin = timer / 60
            local min = math.floor(timermin)
            local timersec = (timermin - min) * 60
            local sec = math.floor(timersec)
            local timerms = (timersec - sec) * 100
            local ms = math.floor(timerms)
            if string.len(tostring(min)) == 1 then
                min = "0".. tostring(min)
            end
            if string.len(tostring(sec)) == 1 then
                sec = "0".. tostring(sec)
            end
            if string.len(tostring(ms)) == 1 then
                ms = tostring(ms) .. "0"
            end
            status = tostring(min)..":"..tostring(sec)..":"..tostring(ms)
        else
            status = "SUDDEN DEATH"
        end
    end
    if bb_loop.gamestate == "celebrate" then
        status = "GAME OVER"
        if bb_loop.winner then
            status = bb_loop.winner .. " WINS!"
        end
    end
    return status
end
    




