local NAME = minetest.get_current_modname()

local players_scanning = {}
local players_hud = {}

local RADIUS = 7
local USAGE = 2
local SPEED = 0.4
local CAPACITY = 500

-- Returns an iterator over the sorted keys
-- or if the ord_fun is given, ordered by the ord_fun
-- ord_fun takes a table and two keys a and b
-- and should return a boolean whether a < b
local function sorted_pairs(tab, ord_fun)
  local sorted_keys = {}
  for k, _ in pairs(tab) do
    sorted_keys[#sorted_keys + 1] = k
  end

  if ord_fun then
    table.sort(sorted_keys, function (a, b)
        return ord_fun(tab, a, b)
    end)
  else
    table.sort(sorted_keys)
  end

  local i = 0
  return function()
    i = i + 1
    return sorted_keys[i], tab[sorted_keys[i]]
  end
end

local function scan(pos, radius)
  local found = {}

  local low = math.floor(radius / 2)

  for x_d = -low,radius-low-1 do
    for y_d = -low,radius-low-1 do
      for z_d = -low,radius-low-1 do
        local name = minetest.get_node({
            x = pos.x + x_d,
            y = pos.y + y_d,
            z = pos.z + z_d
        }).name
        found[name] = (found[name] or 0) + 1
      end
    end
  end
  
  found.air = nil
  found.ignore = nil
  return found
end

local function use_fun(itemstack, user, pointed_thing)
  local name = user:get_player_name()
  if players_scanning[name] ~= nil then
    players_scanning[name] = nil
    local hud_num = players_hud[name]
    if hud_num then
      user:hud_change(hud_num, "text", "")
    end
    --minetest.chat_send_player(name, "Disabled scanner.")
  else
    players_scanning[name] = {}
    --minetest.chat_send_player(name, "Enabled scanner.")
  end

  return itemstack
end

local total_time = 0
minetest.register_globalstep(function (dtime)
  total_time = total_time + dtime
  if total_time >= SPEED then
    total_time = 0
    for name, last in pairs(players_scanning) do
      if last ~= nil then
        local player = minetest.get_player_by_name(name)
        if player == nil then
          players_scanning[name] = nil
          players_hud[name] = nil
          return
        end

        local inv = player:get_inventory()
        local index
        for i, e in ipairs(inv:get_list("main")) do
          if e:get_name() == NAME .. ":scanner" and e:get_meta():get_int("energy") >= USAGE then
            index = i
            break
          end
        end

        local hud_num = players_hud[name]
        if index == nil then
          --minetest.chat_send_player(name, "Scanner disabled due to lack of energy or Scanner.")
          player:hud_change(hud_num, "text", "")
          players_scanning[name] = nil
          return
        end
        local stack = inv:get_stack("main", index, ItemStack(NAME .. ":scanner"))
        local meta = stack:get_meta()
        local energy = meta:get_int("energy")
        meta:set_int("energy", energy - USAGE)
        inv:set_stack("main", index, stack:get_definition()._sparktech_update_energy(stack))
          
        local new = scan(player:get_pos(), RADIUS)

        local hud_s = ""
        -- for oname, ocount in pairs(last) do
        --   if new[oname] == nil then
        --     minetest.chat_send_player(name, "Lost " .. oname)
        --   end
        -- end
        for nname, ncount in sorted_pairs(new, function (t, a, b)
          if t[a] == t[b] then
            return a > b
          else
            return t[a] > t[b]
          end
        end) do
          local desc = ""
          if minetest.registered_nodes[nname] then
          	desc = minetest.registered_nodes[nname].description
          	
          end
          if desc == "" then desc = nname end
          
          hud_s = hud_s .. "\n" .. ncount .. " " .. desc
          -- if last[nname] ~= ncount then
          --   minetest.chat_send_player(name, "Found " .. nname .. " count: " .. tostring(ncount))
          -- end
        end

        if hud_num then
          player:hud_change(hud_num, "text", hud_s)
        end

        players_scanning[name] = new
      end
    end
  end
end)

minetest.register_on_joinplayer(function(player)
  players_hud[player:get_player_name()] = player:hud_add({
    hud_elem_type = "text",
    scale = {
      x = 100,
      y = 100
    },
    text = "",
    number = 0xFFFFFF,
    alignment = -1,
    position = {
      x = 0.1,
      y = 0.9
    }
  })
end)

minetest.register_tool(NAME .. ":scanner", {
  description = "Scanner",
  inventory_image = NAME .. "_scanner.png",
  stack_max = 1,
  on_place = use_fun,
  on_secondary_use = use_fun,
  groups = {
    sparktech_chargeable = CAPACITY
  },
  _sparktech_update_energy = function (itemstack)
    local meta = itemstack:get_meta()
    local c = meta:get_int("energy") or 0

    if c <= 0 then
      itemstack:set_wear(65535)

      return itemstack
    end

    itemstack:set_wear(65535 - math.ceil(c / CAPACITY * 65535))
    return itemstack
  end
})

minetest.register_craft({ --TODO [crafting]
  output = NAME .. ":scanner",
  recipe = {
    { "orioncraft:gold_bar", "orioncraft:copper_bar", "orioncraft:gold_bar" },
    { "orioncraft:iron_bar", "", "orioncraft:iron_bar" },
    { "", "orioncraft:iron_bar", "" }
  }
})
