function lobby.load()
   local file = io.open(minetest.get_worldpath() .. '/levels', 'r')
   if file then
      lobby.savedata = minetest.deserialize(file:read('*a'))
      file:close()
      --lobby.savedata.stats = {}
      --lobby.savedata.name_2_id = {}
   else
      lobby.savedata = {}
      lobby.savedata.IDs = {}
      lobby.savedata.data = {}
      lobby.savedata.stats = {}
      lobby.savedata.name_2_id = {}
   end
end

function lobby.save()
   local file = io.open(minetest.get_worldpath() .. '/levels', 'w')
   file:write(minetest.serialize(lobby.savedata))
   file:close()
end

function lobby.no_drop(itemstack, dropper, pos)
   return
end

function lobby.is_integer(input)
   local number = tonumber(input)
   if number then
      if math.floor(number) == number then
         return true
      end
   end
end

local storage = minetest.get_mod_storage()

function lobby.queue_xp(name, xp) --Queues XP for offline players
   storage:set_int(name, xp)
end

function lobby.check_xp_queue(name) --Checks if a player was award xp whilst offline.
   local xp = storage:get_int(name)
   if xp > 0 then
      local player = minetest.get_player_by_name(name)
      lobby.give_xp(player, xp)
      storage:set_int(name, 0)
      minetest.chat_send_player(name, 'Hey, you just got '..xp..' xp!')
   end
end

function lobby.give_xp(player, amount)
   local player_attributes = player:get_meta()
   local xp = player_attributes:get_float('xp') or 0
   xp = xp + amount
   player_attributes:set_float('xp', xp)
   sfinv.set_player_inventory_formspec(player)
end

function lobby.take_xp(player, amount)
   local player_attributes = player:get_meta()
   local xp = player_attributes:get_float('xp')
   if xp >= amount then
      local xp = xp - amount
      player_attributes:set_float('xp', xp)
      sfinv.set_player_inventory_formspec(player)
      return true
   else
      return false
   end
end

function lobby.corpse_entry(pos, map_id)
   local corpse_pos = lobby.corpses[map_id]
   table.insert(corpse_pos, pos)
   lobby.corpses[map_id] = corpse_pos
end

function lobby.corpse_removal(map_id)
   local corpse_pos = lobby.corpses[map_id]
   for key, value in pairs(corpse_pos) do
      minetest.set_node(value, {name='air'})
   end
end

function lobby.players_on_level(map_id)
   local survivors = ''
   for _, player in pairs(minetest.get_connected_players()) do
      local rname  = player:get_player_name()
      if lobby.game[rname] == map_id then
         survivors = survivors..rname..', '
      end
   end
   return survivors
end

function lobby.update_maps(map_id)
   local count = lobby.map[map_id] - 1 or 10
   if count <= 1 then
      lobby.map[map_id] = 0
      lobby.corpse_removal(map_id)
      for _, player in pairs(minetest.get_connected_players()) do
         local rname = player:get_player_name()
         if lobby.game[rname] == map_id or lobby.game[rname] == map_id..'_ghost' then
            local player_attributes = player:get_meta()
            player:set_nametag_attributes({
               color = {a = 255, r = 255, g = 255, b = 255}
            })
            player:set_properties({visual_size = {x = 10, y = 10}, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}})
            player:set_pos(lobby.spawn_pos)
            player_attributes:set_string('mode', 'solo')
            minetest.chat_send_player(rname, 'Kicking you to the lobby')
            player:set_physics_override({speed=1})
            local player_inv = player:get_inventory()
            player_inv:set_list('main', {})
            lobby.game[rname] = 'lobby'
         end
      end
   else
      lobby.map[map_id] = count
   end
end

function lobby.vote(map_id, force)
   local needed_votes = lobby.map[map_id] - 1
   local actual_votes = lobby.votes[map_id]
   if actual_votes >= needed_votes or force then
      lobby.vote_timer[map_id] = -1
      --Reset vote status and kick player with the highest score.
      local high_score = 0
      local kick = ''
      local traitor = lobby.traitors[map_id]
      for _, player in pairs(minetest.get_connected_players()) do
         local rname  = player:get_player_name()
         if lobby.game[rname] == map_id then
            local score = lobby.suspect[rname]
            local player_attributes = player:get_meta()
            if score > high_score then
               high_score = score
               kick = rname
            end
            lobby.suspect[rname] = 0
            player:set_physics_override({speed=1})
            player_attributes:set_string('voting', 'false')
         end
      end
      if kick == traitor then
         lobby.team_win(map_id)
      elseif needed_votes == 1 and kick ~= traitor then
         lobby.traitor_win(traitor, map_id)
      elseif kick ~= '' then
         lobby.message_to_level(map_id, 'The votes are in, '..kick..' will be kicked.')
         local player = minetest.get_player_by_name(kick)
         local player_attributes = player:get_meta()
         player:set_nametag_attributes({
            color = {a = 0, r = 255, g = 255, b = 255}
         })
         player:set_properties({visual_size = {x = 0, y = 0}, collisionbox = {-0.3, 0.0, -0.3, 0.3, .2, 0.3}})
         player:set_physics_override({speed=1})
         player_attributes:set_string('mode', 'ghost')
         minetest.chat_send_player(kick, 'You were kicked, and are now a ghost. Return to the lobby with the /lobby chat command.')
         lobby.game[kick] = map_id..'_ghost'
         lobby.update_maps(map_id)
      else
         lobby.message_to_level(map_id, 'Nobody was kicked this round.')
      end
   end
end

function lobby.team_win(map_id)
   lobby.corpse_removal(map_id)
   for _, player in pairs(minetest.get_connected_players()) do
      local rname  = player:get_player_name()
      if lobby.game[rname] == map_id then
         lobby.give_xp(player, 25)
         minetest.chat_send_player(rname, 'The traitor was defeated.')
         player:set_pos(lobby.spawn_pos)
         player:set_physics_override({speed=1})
         local player_inv = player:get_inventory()
         player_inv:set_list('main', {})
         lobby.game[rname] = 'lobby'
      elseif lobby.game[rname] == map_id..'_ghost' then
         local player_attributes = player:get_meta()
         player:set_nametag_attributes({
            color = {a = 255, r = 255, g = 255, b = 255}
         })
         player:set_properties({visual_size = {x = 10, y = 10}, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}})
         player:set_pos(lobby.spawn_pos)
         player_attributes:set_string('mode', 'solo')
         minetest.chat_send_player(rname, 'The traitor was defeated.')
         player:set_physics_override({speed=1})
         local player_inv = player:get_inventory()
         player_inv:set_list('main', {})
         lobby.game[rname] = 'lobby'
      end
   end
   lobby.map[map_id] = 0
   lobby.update_stats(map_id, '', 'team')
   local game_data = lobby.savedata.data[map_id]
   local map_name = game_data['map_name'] or map_id
   minetest.chat_send_all('The traitor was defeated on this round in the '..map_name)
end

function lobby.traitor_win(traitor, map_id)
   local player = minetest.get_player_by_name(traitor)
   lobby.corpse_removal(map_id)
   lobby.give_xp(player, 25)
   minetest.chat_send_player(traitor, 'CONGRATULATIONS!!! You killed all the crew!')
   lobby.game[traitor] = 'lobby'
   player:set_pos(lobby.spawn_pos)
   player:set_physics_override({speed=1})
   local player_inv = player:get_inventory()
   player_inv:set_list('main', {})
   lobby.map[map_id] = 0
   for _, player in pairs(minetest.get_connected_players()) do
      local rname  = player:get_player_name()
      if lobby.game[rname] == map_id..'_ghost' or lobby.game[rname] == map_id then
         minetest.chat_send_player(rname, 'The traitor won.')
         local player_attributes = player:get_meta()
         player:set_nametag_attributes({
            color = {a = 255, r = 255, g = 255, b = 255}
         })
         player:set_properties({visual_size = {x = 10, y = 10}, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}})
         player:set_pos(lobby.spawn_pos)
         player_attributes:set_string('mode', 'solo')
         player:set_physics_override({speed=1})
         local player_inv = player:get_inventory()
         player_inv:set_list('main', {})
         lobby.game[rname] = 'lobby'
      end
   end
   lobby.map[map_id] = 0
   lobby.update_stats(map_id, '', 'traitor')
   local game_data = lobby.savedata.data[map_id]
   local map_name = game_data['map_name'] or map_id
   minetest.chat_send_all('The traitor was victorious on this round in the '..map_name)
end

--[[ is_builder(player: ObjectRef): bool

Return true if `player` has builder privs.
--]]
function lobby.is_builder(player)
    if player == nil then return nil end

    local name = player:get_player_name()
    return minetest.check_player_privs(name, {builder = true})
end

--[[ builder_to_player(player: ObjectRef)

Cast builder into regular player, including switching inventories, revoking privs, etc.
--]]
function lobby.builder_to_player(player)
    if player == nil then return end

    local pl_name = player:get_player_name()
    local pl_attr = player:get_meta()
    local pl_priv = minetest.get_player_privs(pl_name)
    local pl_mode = pl_attr:get_string('mode') or ''

    if pl_mode ~= 'builder' then return end
    pl_priv.creative = nil
    pl_priv.fly = nil
    pl_priv.fast = nil
    pl_priv.worldedit = nil
    pl_priv.areas = nil
    minetest.set_player_privs(pl_name, pl_priv)
    pl_attr:set_string('mode', 'solo')
    local inv = player:get_inventory()
    local old_inv = inv:get_list('main')
    inv:set_list('builder', old_inv)
    inv:set_size('main', 16)
    inv:set_list('main', {})
end

--[[ player_to_builder(player: ObjectRef)

Cast regular player into builder, including switching inventories, granting privs, etc.
Does NOT check if the player has acquired the builder priv.  Call lobby.is_builder()
beforehand to make sure player is allowed to be lifted.
--]]
function lobby.player_to_builder(player)
    if player == nil then return end

    local pl_name = player:get_player_name()
    local pl_attr = player:get_meta()
    local pl_priv = minetest.get_player_privs(pl_name)
    local pl_mode = pl_attr:get_string('mode') or ''

    if pl_mode == 'builder' then return end
    pl_priv.creative = true
    pl_priv.fly = true
    pl_priv.fast = true
    minetest.set_player_privs(pl_name, pl_priv)
    pl_attr:set_string('mode', 'builder')
    local inv = player:get_inventory()
    local old_inv = inv:get_list('builder')
    inv:set_list('builder', {})
    inv:set_size('main', 32)
    inv:set_list('main', old_inv)
end

