local mod_name = minetest.get_current_modname()
local S = minetest.get_translator(mod_name)

local function node_to_dirt_timeout()
    return math.random(4, 6)
end

local function dirt_to_grass_timeout()
    return math.random(10, 90)
end

function pmb_soil.can_be_grass_suffocated(pos)
    local anode = minetest.get_node(vector.offset(pos, 0, 1, 0))
    if minetest.get_item_group(anode.name, "full_solid") > 0 then
        return true
    end
    if minetest.get_item_group(anode.name, "slab") + minetest.get_item_group(anode.name, "stair") > 0
    and anode.param2 <= 4 then
        return true
    end
    return false
end

local function get_name_if_i_were_dirt(node_name, to_dirt_type)
    local grtype = minetest.registered_nodes[node_name]._grass_type or to_dirt_type
    local if_dirt = string.gsub(node_name, grtype, to_dirt_type, 1)
    if minetest.registered_nodes[if_dirt] ~= nil then
        return if_dirt
    else
        return node_name
    end
end

local function get_name_if_i_were_grass(dirt_node_name, grass_node_name)
    local grtype = minetest.registered_nodes[grass_node_name]._grass_type or ""
    local dirttype = minetest.registered_nodes[dirt_node_name]._grass_type or "dirt"
    local if_grass = string.gsub(dirt_node_name, dirttype, grtype, 1)
    -- minetest.log("ifgrass: "..if_grass.."   grtype: ".. grtype .. "   grname" .. grass_node_name .. "   fromdirt: ".. grass_node_name)
    if minetest.registered_nodes[if_grass] ~= nil then
        return if_grass
    else
        return dirt_node_name
    end
end

local function set_me_to_dirt(pos, elapsed)
    if not pmb_soil.can_be_grass_suffocated(pos) then return end
    local node = minetest.get_node(pos)
    node.name = get_name_if_i_were_dirt(node.name, "dirt")
    minetest.set_node(pos, node)
end

local function check_and_do_suffocate_grass(pos, cause)
    if cause ~= "place" then return end
    local nt = minetest.get_node_timer(pos)
    if not nt:is_started() then
        if pmb_soil.can_be_grass_suffocated(pos) then
            nt:start(node_to_dirt_timeout())
        end
    end
end

-- get a list of offsets to check, this being the 3x3x3 around the center being checked
local near_nodes = {}
for x = - 1, 1 do
    for y = - 1, 1 do
        for z = - 1, 1 do
            -- if x == 0 and y == 0 and z == 0 then minetest.log(#near_nodes+1) end
            near_nodes[#near_nodes+1] = vector.new(x,y,z)
        end
    end
end
-- remove the reference pos, since you don't neeed it. This is (0,0,0)
table.remove(near_nodes, 14)

local function get_node_near(pos, group)
    local offset = math.random(#near_nodes)
    for i = 1, #near_nodes do
        local p = vector.add(pos, near_nodes[(i + offset) % #near_nodes + 1])
        local n = minetest.get_node(p)
        if minetest.get_item_group(n.name, group) > 0 then
            return n
        end
    end
end

local function dirt_check_and_start_grass_spread(pos)
    local nt = minetest.get_node_timer(pos)
    if nt:is_started() then return end
    if pmb_soil.can_be_grass_suffocated(pos) then return end
    local node = minetest.find_node_near(pos, 1.74, "group:spreads_to_dirt", false)
    if (not node) then return end
    nt:start(dirt_to_grass_timeout())
    return {}
end

local function check_nearby_dirt_to_spread_to(pos)
    for i = 1, #near_nodes do
        local p = vector.add(pos, near_nodes[i])
        local n = minetest.get_node(p)
        if minetest.get_item_group(n.name, "grass_can_grow") > 0
        and not pmb_soil.can_be_grass_suffocated(p) then
            local nt = minetest.get_node_timer(p)
            if not nt:is_started() then
                nt:start(dirt_to_grass_timeout())
            end
        end
    end
end

local function finish_grass_spread(pos)
    local grass_node = get_node_near(pos, "spreads_to_dirt")
    if (not grass_node) or pmb_soil.can_be_grass_suffocated(pos) then return end
    local node = minetest.get_node(pos)
    local if_grass = get_name_if_i_were_grass(node.name, grass_node.name)
    node.name = if_grass
    minetest.swap_node(pos, node)

    check_nearby_dirt_to_spread_to(pos)
end

minetest.register_node('pmb_soil:sand', {
    description = pmb_util.desc(S("Sand"), "grey"),
    groups = {
        full_solid = 1, nature = 1, item_sand = 1, sand = 1, solid = 1, suffocates = 2,
        oddly_breakable_by_hand = 2, crumbly = 1, falling_node = 1, },
    tiles = { 'pmb_sand.png' },
    sounds = pmb_sounds.default_soil(),
})
pmb_util.register_slab("pmb_soil:sand", {drop="pmb_soil:dirt_slab"})
pmb_util.register_stair("pmb_soil:sand", {drop="pmb_soil:dirt_stair"})

minetest.register_node('pmb_soil:gravel', {
    description = pmb_util.desc(S("Gravel"), "grey"),
    groups = {
        full_solid = 1, nature = 1, item_gravel = 1, solid = 1, suffocates = 2,
        oddly_breakable_by_hand = 2, crumbly = 1, falling_node = 1, },
    tiles = { 'pmb_gravel.png' },
    sounds = pmb_sounds.default_soil(),
})
pmb_util.register_slab("pmb_soil:gravel", {drop="pmb_soil:dirt_slab"})
pmb_util.register_stair("pmb_soil:gravel", {drop="pmb_soil:dirt_stair"})

minetest.register_node('pmb_soil:dirt', {
    description = pmb_util.desc(S("Dirt"), "grey"),
    _tt_long_desc = S("Dirty."),
    _grass_type = "dirt",
    groups = {
        full_solid = 1, nature = 1, item_dirt = 1, solid = 1, suffocates = 2, oddly_breakable_by_hand = 2,
        crumbly = 1, soil = 1, dirt = 1, grass_can_grow = 1, },
    tiles = { 'pmb_dirt.png' },
    sounds = pmb_sounds.default_soil(),
    _on_node_update = function(pos, cause, user, count, payload)
        if cause == "place" or cause == "dig" or cause == "pmb_grass_spread" then
            dirt_check_and_start_grass_spread(pos)
        end
    end,
    on_timer = function(pos, elapsed)
        finish_grass_spread(pos)
    end
})
pmb_util.register_slab("pmb_soil:dirt", {drop="pmb_soil:dirt_slab"})
pmb_util.register_stair("pmb_soil:dirt", {drop="pmb_soil:dirt_stair"})

minetest.register_node('pmb_soil:clay', {
    description = pmb_util.desc(S("Clay"), "grey"),
    groups = {
        full_solid = 1, nature = 1, item_clay = 1, solid = 1, suffocates = 2, oddly_breakable_by_hand = 2,
        crumbly = 1, soil = 1, clay = 1, },
    tiles = { 'pmb_clay.png' },
    sounds = pmb_sounds.default_soil(),
})

---------------
-- NORMAL GRASS
---------------
minetest.register_node("pmb_soil:grass", {
    description = pmb_util.desc(S("Grassy Dirt"), "grey"),
    _tt_long_desc = S("Spreads to dirt"),
    _grass_type = "grass",
    groups = {
        full_solid = 1, nature = 1, item_dirt_with_grass = 1, solid = 1, suffocates = 2, topsoil = 1, grass_block = 1,
        oddly_breakable_by_hand = 2, crumbly = 1, soil = 1, grass = 1, silk_touchable = 1, spreads_to_dirt = 1, },
    tiles = {
        'pmb_grass.png',
        'pmb_dirt.png',
        'pmb_grass_side.png',
    },
    drop = 'pmb_soil:dirt',
    sounds = pmb_sounds.default_soil(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    -- on_construct = trigger_dirt_to_check_for_grass,
})
pmb_util.register_slab("pmb_soil:grass", {drop="pmb_soil:dirt_slab", offset_textures=true})
pmb_util.register_stair("pmb_soil:grass", {drop="pmb_soil:dirt_stair", offset_textures=true})

minetest.register_alias("pmb_soil:dirt_with_grass", "pmb_soil:grass")

---------------
-- GRASS VARIANTS
---------------
for i=1, 4 do
    minetest.register_node('pmb_soil:grass_variant_'..i, {
        description = pmb_util.desc(S("Grassy Dirt"), "grey"),
        _tt_long_desc = S("Spreads to dirt"),
        _grass_type = 'grass_variant_'..i,
        groups = {
            full_solid = 1, nature = 1, ["item_grass_"..i] = 1, solid = 1, suffocates = 2, topsoil = 1, grass_block = 1,
            oddly_breakable_by_hand = 2, crumbly = 1, soil = 1, grass = 1, silk_touchable = 1, spreads_to_dirt = 1, },
        tiles = {
            'pmb_grass_variant_'..i..'.png',
            'pmb_dirt.png',
            'pmb_grass_side_variant_'..i..'.png',
        },
        drop = 'pmb_soil:dirt',
        sounds = pmb_sounds.default_soil(),
        on_timer = set_me_to_dirt,
        _on_node_update = check_and_do_suffocate_grass,
        -- on_construct = trigger_dirt_to_check_for_grass,
    })
    pmb_util.register_slab("pmb_soil:grass_variant_"..i, {drop="pmb_soil:dirt_slab", offset_textures=true})
    pmb_util.register_stair("pmb_soil:grass_variant_"..i, {drop="pmb_soil:dirt_stair", offset_textures=true})
end
---------------
-- FOREST GRASS
---------------
minetest.register_node('pmb_soil:forest_grass', {
    description = pmb_util.desc(S("Forest Grass"), "grey"),
    _grass_type = 'forest_grass',
    groups = {
        full_solid = 1, nature = 1, item_forest_grass = 1, solid = 1, suffocates = 2, topsoil = 1, forest = 1,
        oddly_breakable_by_hand = 2, crumbly = 1, soil = 1, dirt = 1, silk_touchable = 1, spreads_to_dirt = 1 },
    tiles = {
        'pmb_forest_grass.png',
        'pmb_dirt.png',
        'pmb_forest_grass_side.png',
    },
    drop = 'pmb_soil:dirt',
    sounds = pmb_sounds.default_soil(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    -- on_construct = trigger_dirt_to_check_for_grass,
})
pmb_util.register_slab("pmb_soil:forest_grass", {drop="pmb_soil:dirt_slab", offset_textures=true})
pmb_util.register_stair("pmb_soil:forest_grass", {drop="pmb_soil:dirt_stair", offset_textures=true})

minetest.register_node('pmb_soil:forest_dirt', {
    description = pmb_util.desc(S("Forest Dirt"), "grey"),
    _grass_type = 'forest_dirt',
    groups = {
        full_solid = 1, nature = 1, item_forest_dirt = 1, solid = 1, suffocates = 2, topsoil = 1, forest = 1,
        oddly_breakable_by_hand = 2, crumbly = 1, soil = 1, dirt = 1, silk_touchable = 1, spreads_to_dirt = 1, },
    tiles = {
        'pmb_forest_dirt.png',
        'pmb_dirt.png',
        'pmb_forest_dirt_side.png',
    },
    drop = 'pmb_soil:dirt',
    sounds = pmb_sounds.default_soil(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    -- on_construct = trigger_dirt_to_check_for_grass,
})
pmb_util.register_slab("pmb_soil:forest_dirt", {drop="pmb_soil:dirt_slab", offset_textures=true})
pmb_util.register_stair("pmb_soil:forest_dirt", {drop="pmb_soil:dirt_stair", offset_textures=true})

for i = 1, 2 do
    minetest.register_node('pmb_soil:forest_grass_'..i, {
        description = pmb_util.desc(S("Forest Grass"), "grey"),
        _grass_type = 'forest_grass_'..i,
        groups = {
            full_solid = 1, nature = 1, item_forest_grass = 1, solid = 1, suffocates = 2, topsoil = 1, forest = 1,
            oddly_breakable_by_hand = 2, crumbly = 1, soil = 1, dirt = 1, silk_touchable = 1, spreads_to_dirt = 1 },
        tiles = {
            'pmb_forest_grass_'..i..'.png',
            'pmb_dirt.png',
            'pmb_forest_grass_side_'..i..'.png',
        },
        drop = 'pmb_soil:dirt',
        sounds = pmb_sounds.default_soil(),
        on_timer = set_me_to_dirt,
        _on_node_update = check_and_do_suffocate_grass,
        -- on_construct = trigger_dirt_to_check_for_grass,
    })
    pmb_util.register_slab("pmb_soil:forest_grass_"..i, {drop="pmb_soil:dirt_slab", offset_textures=true})
    pmb_util.register_stair("pmb_soil:forest_grass_"..i, {drop="pmb_soil:dirt_stair", offset_textures=true})
end
---------------
-- SNOW GRASS
---------------
minetest.register_node('pmb_soil:grass_snow', {
    description = pmb_util.desc(S("Snowy Grass"), "grey"),
    _grass_type = 'grass_snow',
    groups = {
        full_solid = 1, nature = 1, item_grass_snow = 1, solid = 1, suffocates = 2, topsoil = 1, snow = 1,
        oddly_breakable_by_hand = 2, crumbly = 1, soil = 1, dirt = 1, spreads_to_dirt = 1 },
    tiles = {
        'pmb_snow.png',
        'pmb_dirt.png',
        'pmb_grass_snow_side.png',
    },
    drop = 'pmb_soil:dirt',
    sounds = pmb_sounds.default_snow(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    -- on_construct = trigger_dirt_to_check_for_grass,
})
pmb_util.register_slab("pmb_soil:grass_snow", {drop="pmb_soil:dirt_slab", offset_textures=true})
pmb_util.register_stair("pmb_soil:grass_snow", {drop="pmb_soil:dirt_stair", offset_textures=true})

---------------
-- SNOW BLOCK
---------------
minetest.register_node('pmb_soil:snow', {
    description = pmb_util.desc(S("Snow"), "grey"),
    groups = {
        full_solid = 1, nature = 1, item_snow = 1, solid = 1,
        oddly_breakable_by_hand = 1, crumbly = 1, snow = 1, suffocates = 2, cold = 1, silk_touchable = 1, },
    tiles = {
        'pmb_snow.png',
    },
    drop = 'pmb_soil:snow',
    sounds = pmb_sounds.default_snow(),
})


minetest.register_alias('pmb_nodes_surface:sand', 'pmb_soil:sand')
minetest.register_alias('pmb_nodes_surface:dirt', 'pmb_soil:dirt')

if minetest.get_modpath("pmb_stone") then
    pmb_stone.register_ores_for_node("pmb_soil:sand")
    pmb_stone.register_ores_for_node("pmb_soil:dirt", {groups={grass_can_grow=0}})
end
