
attachto_player = {}
attachto_player.children_of = {} -- lists all the attached objects
attachto_player.groups = {} -- lists total of all groups attached
local atp = attachto_player -- shorter alias


-- Default groups to use for cross mod compatability
---------------------------------
-- armor_head, armor_chest, armor_legs, armor_feet
-- armor_shoulder_right, armor_hand_left etc.
-- backpack, flight, wings
-- pouch_left, pouch_right, toolbelt, belt
---------------------------------
-- Prioritise using these groups for compatibility

-- this is what the data looks like
atp.children_of = {
  player_object = { -- object ref of the player
    child = { -- object ref to the child
      override = false, -- will not be detached unless explicitly told to
      on_detach = function() end, -- func to call
      groups = {flight = 1, backpack = 1}, -- groups for this object
    }
  }
}
atp.children_of = {}

-- do not use, this is intentionally kept local so people can't break things as easily
local function atp_add_groups(player, groups, mult)
  -- if not _groups then return end
  for key, val in pairs(groups) do
    if not atp.groups[player] then atp.groups[player] = {} end
    if not atp.groups[player][key] then
      atp.groups[player][key] = 0
    end
    atp.groups[player][key] = atp.groups[player][key] + val * mult
  end
end


----------------------------------------------------------
-------------- GLOBAL EXPOSED FUNCTIONS ------------------
----------------------------------------------------------
--------USE THESE FOR IMPLEMENTING IN YOUR MOD------------
----------------------------------------------------------


-- Returns total value of all groups attached to this player, 0 for none
-- TAKE     player object ref, group string
-- RETURN   number >= 0
function attachto_player.get_group(player, group)
  if atp.groups[player]
  and atp.groups[player][group] ~= nil then
    return math.max(atp.groups[player][group], 0)
  end
  return 0
end


-- Let the system know that this child is attached to the player
-- the flags and groups should be left out unless you have a good reason
-- TAKE     child obj ref, player obj ref, (flags), (groups override)
-- RETURN   nil
function attachto_player.set_attach(child, player, flags, groups)
  -- make sure the key is set
  if not atp.children_of[player] then
    atp.children_of[player] = {}
  end
  -- allow some params to be optional, and override them if not available
  if not flags then flags = {override = false} end
  if not groups then
    if child._attach_groups then groups = child._attach_groups
    else groups = {nogroup = 1}
    end
  end

  atp.children_of[player][child] = {
    groups = groups,
    on_detach = child._on_detach,
    override = flags.override,}
  atp_add_groups(player, groups, 1)
end


-- Detaches all children of these groups
-- attachto_player.detach_groups(player, {armor_head = 1, hat = 1})
-- TAKE     player obj ref, groups table
-- RETURN   nil
function attachto_player.detach_groups(player, groups)
  for child, info in pairs(atp.children_of[player]) do
    if not info.override then
      for group_check, threshold in pairs(groups) do
        if info.groups[group_check]
        and info.groups[group_check] >= threshold then
          atp.detach_child(player, child)
        end
      end -- loop
    end -- override
  end -- loop
end


-- detach a specific child object
-- TAKE     player obj ref, child obj ref, optional call boolean
-- RETURN   nil
function attachto_player.detach_child(player, child, call)
  local player_key = atp.children_of[player]
  if player_key
  and player_key[child] then
    -- call the function the child has set
    if call and type(player_key.on_detach) == "function" then
      player_key.on_detach(child)
    end
    -- update the totals in the group list
    atp_add_groups(player, player_key[child].groups, -1)
    player_key[child] = nil
  end
end


-- returns all the groups attached to the player and their total sum group value
-- {group = 2, group = 1}
-- TAKE     player obj ref
-- RETURN   table
function attachto_player.get_all_groups(player)
  return atp.groups[player]
end


-- returns all objects that are attached to the player including their groups and functions in a table
-- TAKE     player obj ref
-- RETURN   table
function attachto_player.get_all_attached(player)
  return atp.children_of[player]
end


-- returns a list of all attached entities of the groups
-- local list = attachto_player.get_attached_by_group(player, {flight = 1, pouch = 2})
-- TAKE     player obj ref, groups table
-- RETURN   table
function attachto_player.get_attached_by_group(player, groups)
  local list = {}
  for group, threshold in pairs(groups) do
    for child, info in pairs(atp.children_of[player]) do
      if info.groups and info.groups[group]
      and info.groups[group] >= threshold then
        -- add the child to the end of the list
        list[#list + 1] = child
      end
    end
  end
  return list
end


-- remove all attachments from this player, unless the attachment is override = true
-- TAKE     player obj ref
-- RETURN   nil
function attachto_player.detach_all(player, force)
  if not atp.children_of[player] then atp.children_of[player] = {} return end
  for child, info in pairs(atp.children_of[player]) do
    if force or (not info.flags.override) then
      atp.detach_child(player, child)
    end
  end

  if force then
    atp.children_of[player] = nil
    atp.groups[player] = nil
  end
end


minetest.register_on_dieplayer(atp.detach_all)

minetest.register_on_joinplayer(function(player)
  attachto_player.detach_all(player, true) end)
