pmb_fluid_api = {}

pmb_fluid_api.enabled = false

pmb_fluid_api.box = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }

function pmb_fluid_api.register_fluid(name, nodedef)
    nodedef.groups.dynamic_fluid = 1
    nodedef.leveled = 64

    nodedef.drawtype = "nodebox"
    nodedef.node_box = {
        type = "leveled",
        fixed = pmb_fluid_api.box,
    }

    minetest.register_node(name, nodedef)
end

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

function pmb_fluid_api.flow(pos, to_name)
    local node = minetest.get_node(pos)

    local flow = vector.new(0, -1, 0)
    local flow_pos = vector.add(flow, pos)
    local flow_node = minetest.get_node(flow_pos)
    if flow_node.name == to_name or flow_node.name == "air" then
        minetest.set_node(flow_pos, node)
        minetest.set_node(pos, flow_node)
        return
    end
    local offset = math.random(4)
    for i = 1, 4 do
        flow = flows[(offset + i) % 4 + 1]
        flow_pos = vector.add(flow, pos)
        flow_node = minetest.get_node(flow_pos)
        if flow_node.name == to_name or flow_node.name == "air" then
            minetest.set_node(flow_pos, node)
            minetest.set_node(pos, flow_node)
            return
        end
    end
end

function pmb_fluid_api.flow_down(pos, from_name, to_name)
    local uppos = vector.offset(pos, 0, 1, 0)
    local node = minetest.get_node(uppos)
    if node.name == from_name then
        minetest.set_node(uppos, minetest.get_node(pos))
        minetest.set_node(pos, node)
        return
    end
    if node.name ~= to_name then
        return
    end

    node = minetest.get_node(pos)
    local cur_level = minetest.get_node_level(uppos)
    local best_pos = true
    local flow, flow_pos, flow_node

    while true do
        best_pos = true
        for i = 1, 4 do
            flow = flows[i]
            flow_pos = vector.add(flow, uppos)
            flow_node = minetest.get_node(flow_pos)
            if flow_node.name == from_name then
                minetest.set_node(flow_pos, node)
                minetest.set_node(pos, flow_node)
                return
            elseif minetest.get_node_level(flow_pos) > cur_level then
                best_pos = false
                uppos = flow_pos
                cur_level = minetest.get_node_level(flow_pos)
            end
        end
        if best_pos then
            return
        end
    end
end

function pmb_fluid_api.flow_maybe(chance)
    return function(pos)
        if math.random() < chance then
            pmb_fluid_api.flow(pos)
        end
    end
end

function pmb_fluid_api.register_flow(from_name, to_name)
    if not pmb_fluid_api.enabled then return end
    local nodedef = minetest.registered_nodes[from_name]

    minetest.register_abm({
        label = "Fluid flow " .. from_name,
        nodenames = { from_name },
        neighbors = { to_name },
        interval = 1,
        chance = 1 + nodedef.liquid_viscosity,
        action = function(pos)
            minetest.after(0, pmb_fluid_api.flow, pos, to_name)
        end
    })
end

function pmb_fluid_api.register_flow_down(from_name, to_name)
    if not pmb_fluid_api.enabled then return end
    local nodedef = minetest.registered_nodes[from_name]

    minetest.register_abm({
        label = "Fluid down flow " .. from_name,
        nodenames = { to_name },
        neighbors = { to_name },
        interval = 1,
        chance = 1 + nodedef.liquid_viscosity,
        action = function(pos)
            minetest.after(0, pmb_fluid_api.flow_down, pos, from_name, to_name)
        end
    })
end
