--this minigame has a globalstep to update the board, because the arena_lib on_time_tick 
--is too slow for the fast-paced nature of snake. However, lag has been tested to be low 
--(please confirm as needed)





minetest.register_globalstep(function(dtime)
    

    --every server_step, check if the players in-game are attached to their worm heads. Iff they are not, then spawn a new entity and attach them
    --note, the entities have a 10 min timeout (wormball games last 5 min). A MT 5.3 engine bug can detach players unexpectedly, this is a workaround.

    
    for _,player in ipairs(minetest.get_connected_players()) do
        local pl_name = player:get_player_name()

        if arena_lib.is_player_in_arena(pl_name, "wormball") and not(arena_lib.is_player_spectating(pl_name)) then
            local arena = arena_lib.get_arena_by_player(pl_name)
            if not(arena.in_queue == true) and not (arena.in_loading == true) and not(arena.in_celebration == true) and arena.enabled == true then 
                local stats = arena.players[pl_name]
                if stats.alive == true then
                    if not player:get_attach() then
                        local pos_head = arena.players[pl_name].nodes[1]
                        local att = minetest.add_entity(pos_head, 'wormball:player_att')

                        player:set_attach(att, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
                    end
                end
            end
        end
        -- -- this makes spectating possible... Hopefully, and needs testing
        if arena_lib.is_player_in_arena(pl_name, "wormball") and arena_lib.is_player_spectating(pl_name) then
            local att = player:get_attach()

            if att and att:is_player() then
                local attatt = att:get_attach()
                if attatt and attatt:get_luaentity() and attatt:get_luaentity().name and attatt:get_luaentity().name == "wormball:player_att" then
                    player:set_attach(attatt)
                end
            end
        end

    end

    --clear HUDs when not needed
    
    for _,player in ipairs(minetest.get_connected_players()) do
        local p_name = player:get_player_name()

        if not arena_lib.is_player_in_arena(p_name, "wormball") then
            if wormball.HUD[p_name] then

                player:hud_remove(wormball.HUD[p_name].scores)

                wormball.HUD[p_name] = nil
            end
        end
    end

    --we will only run worm movement code about every 0.4 seconds, if not 0.4 seconds yet; return

    wormball.timer = wormball.timer+dtime
    if wormball.timer < .4 then return
    end
    --reset the timer
    wormball.timer = 0
    


    --because this is a global callback, I have to get the name from the player, and the arena from the name, and check that the
    --arena is in-game before doing anything

    --check all connected players
    for _,player in ipairs(minetest.get_connected_players()) do

        local pl_name = player:get_player_name()

        --only mess with stuff if they are in the wormball minigame
        if arena_lib.is_player_in_arena(pl_name, "wormball") and not(arena_lib.is_player_spectating(pl_name)) then

            local arena = arena_lib.get_arena_by_player(pl_name)

            --only mess with stuff if the arena is in-game, add they are supposed to be attached
            if not(arena.in_queue == true) and not (arena.in_loading == true) and not(arena.in_celebration == true) and arena.enabled == true and arena.players[pl_name].attached == true then 
                
                ----------------------------------------------
                ---- mimic the normal for pl_name, stats  ----
                ----------------------------------------------
                local stats = arena.players[pl_name]
                
                local color = stats.color

                local remove_tail = true

                local died --used to determine whether to elim player

                if stats.eliminated == true then
                    died = true
                    arena.players[pl_name].eliminated = false
                end



                -- if players are alive, move the worms (add to the length, subtract from the tail)

                if stats.alive == true then                    


                    local old_dir = arena.players[pl_name].old_direction or {x=0,y=1,z=0} --grab the old_dir info before its updated

                    local player = minetest.get_player_by_name(pl_name)

                    local control = player:get_player_control() --ref: {jump=bool, right=bool, left=bool, LMB=bool, RMB=bool, sneak=bool, aux1=bool, down=bool, up=bool}

                    local look_dir = wormball.get_look_dir(arena,player) --in globals file; returns a string, one of: px, nx, pz, nz for the approximation of player look direction
                    
                    died = false --used to determine whether to elim player

                    --get player direction from current input, first check up or down, then look direction

                    if control.jump == true then --if we are going up
                        arena.players[pl_name].direction = {x=0,y=1,z=0}
                    elseif control.sneak == true then --if we are going down
                        arena.players[pl_name].direction = {x=0,y=-1,z=0}
                    elseif look_dir == 'pz' then -- if we are looking in the +z direction, 
                        arena.players[pl_name].direction = {x=0,y=0,z=1}
                    elseif look_dir == 'nx' then -- if we are looking in the -x direction
                        arena.players[pl_name].direction = {x=-1,y=0,z=0}
                    elseif look_dir == 'nz' then -- if we are looking in the -z direction
                        arena.players[pl_name].direction = {x=0,y=0,z=-1}
                    elseif look_dir == 'px' then -- if we are looking in the +x direction
                        arena.players[pl_name].direction = {x=1,y=0,z=0}
                    end


                    --save the direction info for next round
                    arena.players[pl_name].old_direction = arena.players[pl_name].direction

                    
                    
                    
                    -- localize the direction for easy reference
                    local new_move = stats.direction
                    --was:stats.direction

                    --stats.nodes is the positions of the worm body parts, idx 1 is the head
                    local head_pos = stats.nodes[1]
                
                    --get the new head pos, from the old pos and the new move pos
                    local new_pos = {x = head_pos.x + new_move.x, y = head_pos.y + new_move.y, z = head_pos.z + new_move.z}

                    --get the node at the tenative new head location. 
                    local new_node = minetest.get_node(new_pos).name


                    ----------------------------------------------
                    --- if the new node is air, move the head, delete the tail, length remains the same
                    --- if the new node is a powerup of a different color, move the head, but keep the tail, so the snake grows 1
                    --- if the new node is a powerup of the same color, set move to false, so the next round, the head will not be moved as the tail is deleted to shrink it
                    --- if the new node is none of the above, it is an obstacle, and the player loses
                    ----------------------------------------------
                    --- note: if stats.move is set to false, then the next round, the head will not move, but move will be reset
                    --- note: if the local var remove_tail is true, then after checking the node, the tail is removed
                    -----------------
                    --- note: the function wormball.place_node is in the globals file, and performs the math to place the head and the 
                    ---      -- first body segment rotated according to the current and previous movement directions.
                    ----------------------------------------------
                    --- note: we ALWAYS move forward, unless there is an arena obstacle. However, for the dots of the same color, 
                    ---      -- we do not move forward NEXT turn. This is so that dots are always *eaten*
                    ----------------------------------------------
                    
                    if new_node == 'air' then

                        
                        if arena.players[pl_name].move == true then 

                            -- place the head location into the player's body locations table
                            table.insert(arena.players[pl_name].nodes,1,new_pos)

                            --draw the head and the first body segment
                            wormball.place_node(arena.players[pl_name].nodes,arena.players[pl_name].direction,old_dir,look_dir,color)


                            --move the player's attached entity (use invisible entities so movements are smoother)
                            local att = player:get_attach()

                            --nil check
                            if att then
                                att:move_to(new_pos, true)
                            end

                        else
                            arena.players[pl_name].move = true
                        end

                        
                    elseif new_node == "wormball:power_"..color then --oops, hit own color, remove 1 length

                        

                        if arena.players[pl_name].move == true then


                            --delete the memory of the dot that was 'eaten'
                            for _,dot in pairs(arena.dots) do
                                if dot == new_pos then
                                    table.remove(arena.dots,_)
                                end
                            end
                            

                            -- place the head location into the player's body locations table
                            table.insert(arena.players[pl_name].nodes,1,new_pos)


                            --draw the head and the first body segment
                            wormball.place_node(arena.players[pl_name].nodes,arena.players[pl_name].direction,old_dir,look_dir,color)

                            -- only remove a point if they are longer than 1.
                            if arena.players[pl_name].score ~= 1 then
                                --subtract 1 from the player's score
                                arena.players[pl_name].score = arena.players[pl_name].score - 1

                                --send an HUD message
                                arena_lib.HUD_send_msg('broadcast', pl_name, 'YUCK! You Lost a point.', 2, 'wormball_yuck',0xFF0000)

                                --we will be removing the tail, to shorten the worm
                                remove_tail = true
                                
                                --we will not be moving forward NEXT turn (after eating a same color dot)
                                arena.players[pl_name].move = false

                            else
                                --send an HUD message
                                arena_lib.HUD_send_msg('broadcast', pl_name, 'YUCK! Dont be a cannibal!', 2, 'wormball_yuck',0xFF0000)

                            end

                            --move the player's attached entity
                            local att = player:get_attach()
                            if att then
                                att:move_to(new_pos, true)
                            end

                            

                        else
                            arena.players[pl_name].move = true
                        end

                        
                        

                    elseif string.find(new_node,'wormball:power_') then --we found a powerup dot!



                        if arena.players[pl_name].move == true then

                            --delete the memory of the dot that was 'eaten'
                            for _,dot_pos in pairs(arena.dots) do
                                if dot_pos == new_pos then
                                    table.remove(arena.dots,_)
                                end
                            end

                            

                            -- place the head location into the player's body locations table
                            table.insert(arena.players[pl_name].nodes,1,new_pos)


                            --draw the head and the first body segment                            
                            wormball.place_node(arena.players[pl_name].nodes,arena.players[pl_name].direction,old_dir,look_dir,color)


                            -- add 1 to the player's score
                            arena.players[pl_name].score = arena.players[pl_name].score + 1


                            -- send HUD message
                            arena_lib.HUD_send_msg('broadcast', pl_name, 'Yay! You are now '..arena.players[pl_name].score..' long.', 2, 'wormball_powerup',0x00FF11)
                            

                            --move the player's attached entity
                            local att = player:get_attach()
                            if att then
                                att:move_to(new_pos, true)
                            end

                            --we will be not removing the tail this round, we are growing
                            remove_tail = false

                        else
                            arena.players[pl_name].move = true
                        end


                    else --we have run into an arena obstacle, another snake, or ourselves set died to true so the player will be removed and their peices converted



                        if not arena.in_celebration then

                            
                            arena.players[pl_name].alive = false 
                            
                            died = true


                        end


                        
                    end

                end


                ----------------------------------------------------------------------------
                --- the following code we run regardless of whether the player is alive or not
                ----------------------------------------------------------------------------
                
                if remove_tail == true then --true by default, only false when having eaten a 'good' dot

                    local len = #arena.players[pl_name].nodes --worm length

                    local tail_pos = arena.players[pl_name].nodes[len] --position to change

                    if tail_pos then --nil check

                        -- if the player is dead, then we will convert them into food (of their color)
                        -- if they are still alive, then we will place air there to delete the tail
                        if arena.players[pl_name].alive == false then 
                            local item = "wormball:power_"..color
                            minetest.set_node(tail_pos, {name=item}) 
                        else
                            minetest.set_node(tail_pos, {name="air"}) 
                        end
                        --forget the tail position in the player's body postions
                        table.remove(arena.players[pl_name].nodes,len)
                    end
                end

                -- if players have no nodes, eliminate them. (this can happen if they eat a 'bad' dot while only 1 long) if they have run into an obstacle, elim also
                if #arena.players[pl_name].nodes == 0 or died == true then   --if you eat a bad powerup with only 1 point, then you lose!
                    
                    if arena.mode and arena.mode == 'singleplayer' then
                        minetest.chat_send_player(pl_name, 'Game Over! Your score is '..arena.players[pl_name].score)
                    end

                    --esp for multiplayer games, save the score in the record for end of game documentation.
                    table.insert( arena.multi_scores , 1 , { arena.players[ pl_name ] .score , pl_name } ) 

                    for _,node_pos in pairs(arena.players[pl_name].nodes) do 
                        local item = "wormball:power_"..color
                        minetest.set_node(node_pos, {name=item})
                    end
                    arena.players[pl_name].alive = false
                    
                    wormball.detach(pl_name)
                    if arena.mode == 'multiplayer' then         
                        minetest.sound_play('sumo_lose', {
                            to_player = pl_name,
                            gain = 2.0,
                        })
                    end
                    minetest.after(.3,function(pl_name, arena)
                        if arena.mode == 'singleplayer' then
                            arena_lib.load_celebration('wormball', arena, pl_name)
                        else
                            arena_lib.remove_player_from_arena(pl_name, 1)
                        end
                    
                    end,pl_name, arena)                                 
                    
                end
            end
        end

    end
end)








