
local gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81

function aom_item_entity.raycast(pos, d)
    local ray = minetest.raycast(pos, vector.offset(pos, 0, d, 0), false, false)
    for pointed_thing in ray do
        if pointed_thing.type == "node" then
            local n = minetest.get_node(pointed_thing.under)
            local ndef = minetest.registered_nodes[n.name]
            if ndef.walkable ~= false then return pointed_thing, n end
        end
    end
end

aom_item_entity.LISTEN("should_sleep", function(self, dtime, moveresult)
    local vel = self.object:get_velocity()
    if vector.length(vel) > 0.2 then
        return end
    local s = self._collisionbox and (math.abs(self._collisionbox[1] - self._collisionbox[4])) or 0.3
    local floor = aom_item_entity.raycast(self.object:get_pos(), -s-0.2)
    if floor then
        return true
    end
end)

aom_item_entity.LISTEN("enable_gravity", function(self, mult)
    self.object:set_acceleration({x = 0, y = -gravity*(mult or 1), z = 0})
end)

aom_item_entity.LISTEN("disable_gravity", function(self)
    self.object:set_acceleration({x = 0, y = 0, z = 0})
end)

aom_item_entity.LISTEN("on_sleep", function(self)
    self.object:set_velocity(vector.new(0,0,0))
    aom_item_entity.SIGNAL("disable_gravity", self)
    aom_item_entity.debug("SLEEP")
end)
aom_item_entity.LISTEN("on_wake", function(self)
    aom_item_entity.SIGNAL("enable_gravity", self)
    aom_item_entity.debug("WOKE UP")
end)

-- friction
function aom_item_entity.apply_friction(self, dtime)
    local vel = self.object:get_velocity()
    vel = vel * (-(self._friction or 0.1) * dtime)
    vel.y = -0.01
    self.object:add_velocity(vel)
end

aom_item_entity.LISTEN("on_step", function(self, dtime, moveresult, ...)
    local rot_speed = aom_item_entity.get(nil, "item_rotation", 2)
    if (rot_speed > 0) and (not aom_item_entity.get(nil, "item_lay_flat", true)) then
        local yaw = self.object:get_yaw()
        yaw = yaw + rot_speed * dtime
        self.object:set_yaw(yaw)
    end
    -- if it should sleep
    local should_sleep = aom_item_entity.CONDITIONAL("should_sleep", self, dtime, moveresult)
    if should_sleep and (not self._sleep_state) then
        self._sleep_state = true
        aom_item_entity.SIGNAL("on_sleep", self)
    elseif (not should_sleep) and self._sleep_state then
        self._sleep_state = false
        aom_item_entity.SIGNAL("on_wake", self)
    end
    if self._sleep_state then return end

    self._friction = 0
    if not moveresult then return end
    local was_on_floor = self._is_on_floor
    local max_vel_change = 0
    self._is_on_floor = false
    for i, collision in ipairs(moveresult.collisions or {}) do
        if (collision.type == "node") and (collision.axis == "y")
        and (collision.node_pos ~= nil) then
            local node = minetest.get_node(collision.node_pos)
            local old_vel_len = vector.length(collision.old_velocity) - vector.length(collision.new_velocity)
            if old_vel_len > max_vel_change then
                max_vel_change = old_vel_len
            end
            self._is_on_floor = true
            -- get slipperyness of this node
            local slip = math.max(
                minetest.get_item_group(node.name, "slippery"),
                minetest.get_item_group(node.name, "slippery_item"))
            if slip > 0 then slip = slip*(0.2) + 4 end
            -- if it's more friction then use that
            self._friction = math.max(self._friction, math.max(0, 6 - slip))
        end
    end

    if (not was_on_floor) and self._is_on_floor and (max_vel_change > 1) then
        aom_item_entity.SIGNAL("on_landed", self, dtime, moveresult, ...)
    elseif was_on_floor and (not self._is_on_floor) then
        aom_item_entity.SIGNAL("on_fall", self, dtime, moveresult, ...)
    end

    if self._friction > 0 then
        aom_item_entity.apply_friction(self, dtime)
    end
end)

aom_item_entity.LISTEN("enable_physics", function(self)
    if not self.physical_state then
        self.physical_state = true
        self.object:set_properties({physical=true})
        self.object:set_velocity({x=0, y=0, z=0})
        self.object:set_acceleration({x=0, y=-gravity, z=0})
    end
end)

aom_item_entity.LISTEN("disable_physics", function(self)
    if self.physical_state then
        self.physical_state = false
        self.object:set_properties({physical=false})
        self.object:set_velocity({x=0, y=0, z=0})
        self.object:set_acceleration({x=0, y=0, z=0})
    end
end)
