
local adjacent = {
    vector.new( 0, 1, 0),
    vector.new( 0,-1, 0),
    vector.new( 0, 0, 1),
    vector.new( 0, 0,-1),
    vector.new( 1, 0, 0),
    vector.new(-1, 0, 0),
}

function aom_item_entity.get_liquid_flow_direction(pos, source)
    local dir = vector.new(0,0,0)
    local node = core.get_node(pos)
    if node.param2 >= 8 then node.param2 = 0 end
    for i = 3, #adjacent do
        local d = adjacent[i]
        local n = core.get_node(pos + d)
        local ndef = core.registered_nodes[n.name]
        if n.param2 >= 8 then n.param2 = 0 end
        if ndef.liquidtype == "flowing" then
            if n.param2 < ((source and 8) or node.param2) then
                dir = dir + d
            elseif n.param2 > ((source and 8) or node.param2) then
                dir = dir - d
            end
        end
    end
    return vector.normalize(dir)
end

function aom_item_entity.push_by_liquid(object, dtime, strength)
    local pos = object:get_pos()
    local node = core.get_node(pos)
    local ndef = core.registered_nodes[node.name]
    if (ndef.liquidtype == "flowing") or (ndef.liquidtype == "source") then
        local dir = aom_item_entity.get_liquid_flow_direction(pos, (ndef.liquidtype == "source"))
        if dir:equals(vector.new(0,0,0)) then return end
        local vel = (dir * strength)
        local rvel = object:get_velocity()
        local dist = vector.length(vel) - vector.length(rvel)
        dist = (dist + vector.distance(vel, rvel)) / 2
        vel = vel * (dist)
        object:add_velocity(vel * (dtime))
        return true
    end
end


-- pushed by water
aom_item_entity.LISTEN("on_step", function(self, dtime, moveresult, ...)
    local push_vel = aom_item_entity.get(nil, "item_water_push_vel", 4)
    if push_vel == 0 then return end
    self._is_pushed_by_water = aom_item_entity.push_by_liquid(self.object, dtime, push_vel)
end)

aom_item_entity.LISTEN("should_sleep", function(self, dtime, moveresult)
    if not aom_item_entity.get(nil, "item_float_in_water", false) then return end
    if (self._is_pushed_by_water or self._floating) and not self._on_surface_water then
        return false
    end
end)

-- floating on water
aom_item_entity.LISTEN("should_sleep", function(self, dtime, moveresult)
    if not aom_item_entity.get(nil, "item_float_in_water", false) then return end
    if self._on_surface_water then
        return true
    end
end)

local function ndef(p)
    local node = core.get_node(p)
    return node, core.registered_nodes[node.name]
end

function aom_item_entity.is_on_water_surface(pos)
    local p = vector.offset(pos, 0, 0.0, 0)
    local n, def = ndef(vector.offset(pos, 0, 0.2, 0))
    if (def.liquidtype == "source") or (def.liquidtype == "flowing") then return false end
    local bn, bdef = ndef(p)
    if (bdef.liquidtype == "source") or (bdef.liquidtype == "flowing") then return true end
    return false
end

aom_item_entity.LISTEN("on_enter_water", function(self)
    aom_item_entity.SIGNAL("enable_gravity", self, 0.5)
end)
aom_item_entity.LISTEN("on_leave_water", function(self)
    aom_item_entity.SIGNAL("enable_gravity", self)
end)

aom_item_entity.LISTEN("should_float", function(self)
    local iname = ItemStack(self.itemstring):get_name()
    local all_items = not aom_item_entity.get(nil, "item_float_only_if_set", true)
    if all_items or (core.get_item_group(iname, "floats") > 0)
    or (core.get_item_group(iname, "wood") > 0) then
        return true
    end
end)

aom_item_entity.LISTEN("on_step", function(self, dtime, moveresult, ...)
    if not aom_item_entity.get(nil, "item_float_in_water", false) then return end

    local pos = self.object:get_pos()
    local vel = self.object:get_velocity()

    local is_surface = aom_item_entity.is_on_water_surface(pos)

    if is_surface and (vector.length(vel) < 0.5) then
        self._on_surface_water = true
        self.object:set_velocity(vector.new(0,0,0))
        pos.y = math.floor(pos.y) + 0.4
        self.object:set_pos(pos)
    else
        self._on_surface_water = false
    end

    local was_in_water = self._is_in_water

    local node = core.get_node(pos)
    local liquidtype = core.registered_nodes[node.name].liquidtype
    self._is_in_water = (liquidtype == "source") or (liquidtype == "flowing")
    self._floating = (liquidtype == "source")
    self._floating = self._floating and not self._on_surface_water
    if not aom_item_entity.CONDITIONAL("should_float", self) then
        self._floating = false
    end

    if self._is_in_water then
        vel = vel * aom_item_entity.get(nil, "item_water_deceleration", 0.98)
        self.object:set_velocity(vel)
        if not was_in_water then
            aom_item_entity.SIGNAL("on_enter_water", self)
        end
    elseif was_in_water then
        aom_item_entity.SIGNAL("on_leave_water", self)
    end

    if self._floating then
        local factor = 1 - math.min(1, math.max(0, vel.y / 5))
        local add = 8 * dtime * factor
        self.object:add_velocity(vector.new(0, add, 0))
    end
end)
