
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),
}

local function get_next_adjacent(pos, min_i)
    for i, p in ipairs(adjacent) do
        if i >= (min_i or 0) then
            local pp = pos + p
            local node = minetest.get_node(pp)
            if minetest.get_item_group(node.name, "full_solid") > 0 then
                return pp, p
            end
        end
    end
end

local function get_adjacent_facedir(pos, node)
    local adj, dir = get_next_adjacent(pos, 3)
    if not dir then return end
    return minetest.dir_to_facedir(dir, false)
end

function aom_lights.has_wall(pos, node)
    node = node or minetest.get_node(pos)
    local dir = minetest.facedir_to_dir(node.param2) or vector.new(0,0,1)
    if dir.y ~= 0 then return false end
    local solid_wall = aom_util.has_pointable_node_at(pos + (dir * 0.51), "solid")
    if solid_wall then return true end
    return false
end

function aom_lights.has_floor(pos)
    local solid_below = aom_util.has_pointable_node_at(vector.offset(pos, 0, -0.51, 0), "solid")
    if solid_below then return true end
    return false
end

function aom_lights.has_ceiling(pos)
    local solid_above = aom_util.has_pointable_node_at(vector.offset(pos, 0, 0.51, 0), "solid")
    if solid_above then return true end
    return false
end

function aom_lights.can_place(pos, node)
    return aom_lights.fix_wall_floor_placement(pos, node, true)
end

-- Be careful not to assume this only gets called when a torch like node is present; it could be used to predict placement
function aom_lights.fix_wall_floor_placement(pos, node, dry)
    node = node or minetest.get_node(pos)
    local ndef = minetest.registered_nodes[node.name]
    local has_floor = aom_lights.has_floor(pos)
    local has_ceiling = aom_lights.has_ceiling(pos)
    local has_wall = aom_lights.has_wall(pos, node)
    local has_state = false

    has_state = not (
        (node.name == ndef._wall and not has_wall) or
        (node.name == ndef._floor and not has_floor) or
        (node.name == ndef._ceiling and not has_ceiling)
    )
    if has_state then return true end

    -- case of rotate to new wall
    if (not has_state) and (node.name == ndef._wall) then
        local adj, dir = get_next_adjacent(pos, 3)
        if dir then
            node.param2 = minetest.dir_to_facedir(dir, false)
            if not dry then minetest.swap_node(pos, node) end
            return true
        end
    end

    if ndef._wall and (node.name ~= ndef._wall) then
        local adj, dir = get_next_adjacent(pos, 3)
        if dir then
            node.name = ndef._wall
            node.param2 = minetest.dir_to_facedir(dir, false)
            if not dry then minetest.swap_node(pos, node) end
            return true
        end
    elseif (has_floor) and ndef._floor and (node.name ~= ndef._floor) then
        has_state = true
        node.name = ndef._floor
        node.param2 = 0
        if not dry then minetest.swap_node(pos, node) end
    elseif (has_ceiling) and ndef._ceiling and (node.name ~= ndef._ceiling)then
        has_state = true
        node.name = ndef._ceiling
        if not dry then minetest.swap_node(pos, node) end
    end

    if not has_state then
        if not dry then minetest.dig_node(pos) end
        return false
    end
end

function aom_lights.on_node_update(pos, cause, user, count, payload)
    if (cause ~= "dig") then return end
    aom_lights.fix_wall_floor_placement(pos)
end
