-- Localise functions
local floor, min, max = math.floor, math.min, math.max

--Should we use new collisionbox detection
local colbox = false
local radius = colbox and 3.0 or 1.0

--= Functions inspired from Kaeza's Firearms mod

local function minmax(x, y)
   return min(x, y), max(x, y)
end

local function pos_in_box(p, b1, b2)
   local xmin, xmax = minmax(b1.x, b2.x)
   local ymin, ymax = minmax(b1.y, b2.y)
   local zmin, zmax = minmax(b1.z, b2.z)
   return p.x >= xmin and p.x <= xmax
         and p.y >= ymin and p.y <= ymax
         and p.z >= zmin and p.z <= zmax
end

local function get_obj_box(obj)
   local box
   if obj:is_player() then
      box = {-.5, -.5, -.5, .5, 1.5, .5}
   else
      box = obj:get_luaentity().collisionbox
            or {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
   end
   local p = obj:get_pos()
   local x1, y1, z1, x2, y2, z2 = unpack(box)
   return {x = x1 + p.x, y = y1 + p.y, z = z1 + p.z},
         {x = x2 + p.x, y = y2 + p.y, z = z2 + p.z}
end
--= END (Thanks Kaeza :)

local on_hit_remove = function(self)
   minetest.sound_play(
      bows.registered_arrows[self.name].on_hit_sound, {
         pos = self.object:get_pos(),
         gain = 1.0,
         max_hear_distance = 7
      })
   -- chance of dropping arrow
   local chance = bows.registered_arrows[self.name].drop_chance
   if math.random(0, chance) == 1 then
      minetest.add_item(self.object:get_pos(), self.name)
   end
   self.object:remove()
   return self
end

local on_hit_object = function(self, target, hp, user, lastpos)
   target:punch(user, 0.1, {
      full_punch_interval = 0.1,
      damage_groups = {fleshy = hp},
   }, nil)
   if bows.registered_arrows[self.name].on_hit_object then
      bows.registered_arrows[self.name].on_hit_object(
         self, target, hp, user, lastpos)
   end
   on_hit_remove(self)
   return self
end

minetest.register_entity("bows:arrow",{
   hp_max = 10,
   visual = "wielditem",
   visual_size = {x = .20, y = .20},
   collisionbox = {-0.1,-0.1,-0.1,0.1,0.1,0.1},
   physical = true,
   textures = {"air"},
   _is_arrow = true,
   timer = 20,
   oldvel = {x = 0, y = 0, z = 0},
   on_activate = function(self, staticdata)
      if not self then
         self.object:remove()
         return
      end
      if bows.tmp and bows.tmp.arrow ~= nil then
         self.arrow = bows.tmp.arrow
         self.user = bows.tmp.user
         self.name = bows.tmp.name
         self.dmg = bows.registered_arrows[self.name].damage
         self.text = bows.registered_arrows[self.name].disp_entity
         bows.tmp = nil
         self.object:set_properties({textures = {self.text}})
      else
         self.object:remove()
      end
   end,
   on_step = function(self, dtime, ...)
      self.timer = self.timer - dtime
      if self.timer < 0 then
         self.object:remove()
         return
      end
      local what_is, what_obj, ent
      for i, ob in pairs(minetest.get_objects_inside_radius(
            self.object:get_pos(), radius)) do
         what_obj = nil
         what_is = ""
         -- player
         if ob
         and bows.pvp
         and ob:is_player()
         and ob:get_player_name() ~= self.user:get_player_name() then
            what_obj = ob
            what_is = "player"
         end
         -- entity/mob
         if ob and not what_obj then
            ent = ob:get_luaentity()
            if ent
            and ent.physical
            and not ent._is_arrow
            and ent.name ~= "__builtin:item" then
               what_obj = ob
               what_is = "entity"
            end
         end
         if what_obj then
            if colbox then
               -- Object specific collision detection
               local p1, p2 = get_obj_box(what_obj)
               if pos_in_box(self.object:get_pos(), p1, p2) then
                  on_hit_object(self, what_obj, self.dmg, self.user,
                        self.object:get_pos())
                  return self
               end
            else
               on_hit_object(self, what_obj, self.dmg, self.user,
                     self.object:get_pos())
               return self
            end
         end
      end
      local vel = self.object:get_velocity()
      if vel.x == 0 or vel.y == 0 or vel.z == 0 then
         if bows.registered_arrows[self.name].on_hit_node then
            local pos = self.object:get_pos()
            local lastpos = {x = pos.x, y = pos.y, z = pos.z}
            pos.x = pos.x + (self.oldvel.x / 100)
            pos.y = pos.y + (self.oldvel.y / 100)
            pos.z = pos.z + (self.oldvel.z / 100)
            self.node = minetest.get_node(pos)
            bows.registered_arrows[self.name].on_hit_node(
                  self, pos, self.user, lastpos)
         end
         on_hit_remove(self)
         return self
      end
      self.oldvel = vel
   end,
})
