local S = core.get_translator(core.get_current_modname())

local craftable_grass
if minetest.get_modpath("aom_settings") then
    if aom_soil.has_aom_settings then
        -- TL: setting name to allow or disallow (gameplay_grass_craftable)
        aom_settings.register_setting("gameplay_grass_craftable", true, S("Craftable Grass"), "server")
    end
    craftable_grass = aom_settings.get_setting(nil, "gameplay_grass_craftable", true)
end

local function register_grass_craft(def)
    if not craftable_grass then return end
    aom_tcraft.register_craft(def)
end

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 aom_soil.can_be_grass_suffocated(pos)
    local anode = minetest.get_node(vector.offset(pos, 0, 1, 0))
    if minetest.get_item_group(anode.name, "liquid") > 0 then
        return true
    end
    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 grass_type = (minetest.registered_nodes[node_name] or {})._grass_type or to_dirt_type
    local if_dirt = string.gsub(node_name, grass_type, 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 grass_type = (minetest.registered_nodes[grass_node_name] or {})._grass_type or ""
    local dirt_type = (minetest.registered_nodes[dirt_node_name] or {})._grass_type or "dirt"
    local if_grass = string.gsub(dirt_node_name, dirt_type, grass_type, 1)
    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 aom_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, user, data)
    if cause ~= "place" then return end
    local nt = minetest.get_node_timer(pos)
    if not nt:is_started() then
        if aom_soil.can_be_grass_suffocated(pos) then
            nt:start(node_to_dirt_timeout())
        end
    end
end

local function stop_timer(pos)
    local nt = core.get_node_timer(pos)
    if nt:is_started() then nt:stop() 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 aom_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 aom_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 aom_soil.can_be_grass_suffocated(pos) then return false 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)
    return true
end

minetest.register_node("aom_soil:sand", {
    description = S("Sand"),
    groups = {
        full_solid = 1, nature = 1, material_sand = 1, sand = 1, solid = 1, suffocates = 2,
        oddly_breakable_by_hand = 2, crumbly = 1, falling_node = 1, },
    tiles = { "aom_sand.png" },
    sounds = aom_sounds.default_soil(),
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})
aom_util.register_slab("aom_soil:sand", {drop="aom_soil:dirt_slab"})
aom_util.register_stair("aom_soil:sand", {drop="aom_soil:dirt_stair"})

minetest.register_node("aom_soil:gravel", {
    description = S("Gravel"),
    groups = {
        full_solid = 1, nature = 1, material_gravel = 1, solid = 1, suffocates = 2,
        oddly_breakable_by_hand = 2, crumbly = 1, falling_node = 1, },
    tiles = { "aom_gravel.png" },
    sounds = aom_sounds.default_soil(),
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})
aom_util.register_slab("aom_soil:gravel", {drop="aom_soil:dirt_slab"})
aom_util.register_stair("aom_soil:gravel", {drop="aom_soil:dirt_stair"})

minetest.register_node("aom_soil:dirt", {
    description = S("Dirt"),
    _grass_type = "dirt",
    groups = {
        full_solid = 1, nature = 1, material_dirt = 1, solid = 1, suffocates = 2, oddly_breakable_by_hand = 2,
        crumbly = 1, soil = 1, dirt = 1, grass_can_grow = 1, },
    tiles = { "aom_dirt.png" },
    sounds = aom_sounds.default_soil(),
    _on_node_update = function(pos, cause, user, data)
        if cause == "place" or cause == "dig" or cause == "liquid" then
            local above = core.get_node(vector.offset(pos, 0, 1, 0))
            if core.get_item_group(above.name, "water_source") > 0 then
                local node = core.get_node(pos)
                local sdef = core.registered_nodes[node.name]
                node.name = "aom_soil:mud" .. (sdef and sdef._shape_name and ("_" .. sdef._shape_name) or "")
                if not core.registered_nodes[node.name] then return end
                core.set_node(pos, node)
                return false, true
            end
        end
        if cause == "place" or cause == "dig" or cause == "aom_grass_spread" then
            dirt_check_and_start_grass_spread(pos)
        end
    end,
    on_timer = function(pos, elapsed)
        finish_grass_spread(pos)
    end,
    on_destruct = stop_timer,
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})
aom_util.register_slab("aom_soil:dirt", {drop="aom_soil:dirt_slab"})
aom_util.register_stair("aom_soil:dirt", {drop="aom_soil:dirt_stair"})

local function mud_to_clay_timeout()
    return math.random(180, 200) -- 3 to 3.3 min
end

minetest.register_node("aom_soil:mud", {
    description = S("Mud"),
    _tt_long_desc = S("Dries into dirt when exposed or clay when covered."),
    groups = {
        full_solid = 1, nature = 1, solid = 1, suffocates = 2,
        oddly_breakable_by_hand = 2, crumbly = 1, soil = 1, mud = 1, },
    tiles = { "aom_dirt_mud.png" },
    sounds = aom_sounds.default_soil(),
    _on_node_update = function(pos, cause, user, data)
        local nt = core.get_node_timer(pos)
        if nt:is_started() then return false end
        if cause == "place" or cause == "dig" or cause == "liquid" then
            core.get_node_timer(pos):start(mud_to_clay_timeout())
        end
    end,
    on_timer = function(pos, elapsed)
        -- do return end
        local above = core.get_node(vector.offset(pos, 0, 1, 0))
        if core.get_item_group(above.name, "solid") > 0 then
            local node = core.get_node(pos)
            local sdef = core.registered_nodes[node.name]
            node.name = "aom_soil:clay" .. (sdef and (sdef._shape_name and ("_" .. sdef._shape_name)) or "")
            if not core.registered_nodes[node.name] then return end
            core.set_node(pos, node)
        elseif core.get_item_group(above.name, "water") == 0 then
            local node = core.get_node(pos)
            local sdef = core.registered_nodes[node.name]
            node.name = "aom_soil:dirt" .. (sdef and (sdef._shape_name and ("_" .. sdef._shape_name)) or "")
            if not core.registered_nodes[node.name] then return end
            core.set_node(pos, node)
        end
    end,
    on_destruct = function(pos)
        local nt = core.get_node_timer(pos)
        if nt:is_started() then nt:stop() end
    end,
})
aom_util.register_slab("aom_soil:mud")
aom_util.register_stair("aom_soil:mud")

minetest.register_node("aom_soil:clay", {
    description = S("Clay"),
    groups = {
        full_solid = 1, nature = 1, material_clay = 1, solid = 1, suffocates = 2, oddly_breakable_by_hand = 2,
        crumbly = 1, soil = 1, clay = 1, },
    tiles = { "aom_clay.png" },
    sounds = aom_sounds.default_soil(),
})
aom_util.register_all_shapes("aom_soil:clay")

---------------
-- NORMAL GRASS
---------------
minetest.register_node("aom_soil:grass", {
    description = S("Grassy Dirt"),
    _tt_long_desc = S("Spreads to nearby dirt."),
    _grass_type = "grass",
    groups = {
        full_solid = 1, nature = 1, material_dirt_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,
        dirt = 1, },
    tiles = {
        "aom_grass.png",
        "aom_dirt.png",
        "aom_grass_side.png",
    },
    drop = "aom_soil:dirt",
    sounds = aom_sounds.default_soil(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    on_destruct = stop_timer,
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})
aom_util.register_slab("aom_soil:grass", {drop="aom_soil:dirt_slab", offset_textures=true})
aom_util.register_stair("aom_soil:grass", {drop="aom_soil:dirt_stair", offset_textures=true})

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

register_grass_craft({
    output = "aom_soil:grass 20",
    extra_items = {
        "aom_items:wooden_cup",
    },
    items = {
        ["aom_items:wooden_cup_water"] = 1,
        ["aom_soil:dirt"] = 20,
    },
})

---------------
-- GRASS VARIANTS
---------------
for i=1, 4 do
    minetest.register_node("aom_soil:grass_variant_"..i, {
        description = S("Grassy Dirt"),
        _tt_long_desc = S("Spreads to nearby dirt."),
        _grass_type = "grass_variant_"..i,
        groups = {
            full_solid = 1, nature = 1, material_dirt_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, 
            dirt = 1, },
        tiles = {
            "aom_grass_variant_"..i..".png",
            "aom_dirt.png",
            "aom_grass_side_variant_"..i..".png",
        },
        drop = "aom_soil:dirt",
        sounds = aom_sounds.default_soil(),
        on_timer = set_me_to_dirt,
        _on_node_update = check_and_do_suffocate_grass,
        on_destruct = stop_timer,
        _scrap = {
            ["aom_scrap:dirt"] = 8,
        },
    })
    aom_util.register_slab("aom_soil:grass_variant_"..i, {drop="aom_soil:dirt_slab", offset_textures=true})
    aom_util.register_stair("aom_soil:grass_variant_"..i, {drop="aom_soil:dirt_stair", offset_textures=true})

    register_grass_craft({
        output = "aom_soil:grass_variant_"..i.." 10",
        items = {["aom_soil:grass"] = 10},
        hide_if_not_craftable = true,
    })
    register_grass_craft({
        output = "aom_soil:dirt 1",
        items = {["aom_soil:grass_variant_"..i] = 1},
        hide_if_not_craftable = true,
    })
end
---------------
-- FOREST GRASS
---------------
minetest.register_node("aom_soil:forest_grass", {
    description = S("Forest Grass"),
    _tt_long_desc = S("Spreads to nearby dirt."),
    _grass_type = "forest_grass",
    groups = {
        full_solid = 1, nature = 1, material_dirt_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 = {
        "aom_forest_grass.png",
        "aom_dirt.png",
        "aom_forest_grass_side.png",
    },
    drop = "aom_soil:dirt",
    sounds = aom_sounds.default_soil(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    on_destruct = stop_timer,
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})
aom_util.register_slab("aom_soil:forest_grass", {drop="aom_soil:dirt_slab", offset_textures=true})
aom_util.register_stair("aom_soil:forest_grass", {drop="aom_soil:dirt_stair", offset_textures=true})
register_grass_craft({
    output = "aom_soil:forest_grass 20",
    extra_items = {
        "aom_items:wooden_cup",
    },
    items = {
        ["aom_items:wooden_cup_water"] = 1,
        ["aom_soil:forest_dirt"] = 20,
    },
})
register_grass_craft({
    output = "aom_soil:dirt 1",
    items = {["aom_soil:forest_grass"] = 1},
    hide_if_not_craftable = true,
})

minetest.register_node("aom_soil:forest_dirt", {
    description = S("Forest Dirt"),
    _tt_long_desc = S("Spreads to nearby dirt."),
    _grass_type = "forest_dirt",
    groups = {
        full_solid = 1, nature = 1, material_dirt_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 = {
        "aom_forest_dirt.png",
        "aom_dirt.png",
        "aom_forest_dirt_side.png",
    },
    drop = "aom_soil:dirt",
    sounds = aom_sounds.default_soil(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    on_destruct = stop_timer,
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})
aom_util.register_slab("aom_soil:forest_dirt", {drop="aom_soil:dirt_slab", offset_textures=true})
aom_util.register_stair("aom_soil:forest_dirt", {drop="aom_soil:dirt_stair", offset_textures=true})
register_grass_craft({
    output = "aom_soil:forest_dirt 10",
    items = {["aom_soil:grass"] = 10, ["aom_items:stick"] = 1,},
    hide_if_not_craftable = true,
})
register_grass_craft({
    output = "aom_soil:dirt 1",
    items = {["aom_soil:forest_dirt"] = 1},
    hide_if_not_craftable = true,
})

for i = 1, 2 do
    minetest.register_node("aom_soil:forest_grass_"..i, {
        description = S("Forest Grass"),
        _tt_long_desc = S("Spreads to nearby dirt."),
        _grass_type = "forest_grass_"..i,
        groups = {
            full_solid = 1, nature = 1, material_dirt_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 = {
            "aom_forest_grass_"..i..".png",
            "aom_dirt.png",
            "aom_forest_grass_side_"..i..".png",
        },
        drop = "aom_soil:dirt",
        sounds = aom_sounds.default_soil(),
        on_timer = set_me_to_dirt,
        _on_node_update = check_and_do_suffocate_grass,
    on_destruct = stop_timer,
        _scrap = {
            ["aom_scrap:dirt"] = 8,
        },
    })
    aom_util.register_slab("aom_soil:forest_grass_"..i, {drop="aom_soil:dirt_slab", offset_textures=true})
    aom_util.register_stair("aom_soil:forest_grass_"..i, {drop="aom_soil:dirt_stair", offset_textures=true})
    register_grass_craft({
        output = "aom_soil:forest_grass_"..i.." 10",
        items = {["aom_soil:forest_dirt"] = 10,},
        hide_if_not_craftable = true,
    })
    register_grass_craft({
        output = "aom_soil:dirt 1",
        items = {["aom_soil:forest_grass_"..i] = 1},
        hide_if_not_craftable = true,
    })
end
---------------
-- SNOW GRASS
---------------
minetest.register_node("aom_soil:grass_snow", {
    description = S("Snowy Grass"),
    _grass_type = "grass_snow",
    groups = {
        full_solid = 1, nature = 1, material_dirt_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 = {
        "aom_snow.png",
        "aom_dirt.png",
        "aom_grass_snow_side.png",
    },
    drop = "aom_soil:dirt",
    sounds = aom_sounds.default_snow(),
    on_timer = set_me_to_dirt,
    _on_node_update = check_and_do_suffocate_grass,
    on_destruct = stop_timer,
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})
aom_util.register_slab("aom_soil:grass_snow", {drop="aom_soil:dirt_slab", offset_textures=true})
aom_util.register_stair("aom_soil:grass_snow", {drop="aom_soil:dirt_stair", offset_textures=true})

---------------
-- SNOW BLOCK
---------------
minetest.register_node("aom_soil:snow", {
    description = S("Snow"),
    groups = {
        full_solid = 1, nature = 1, material_snow = 1, solid = 1,
        oddly_breakable_by_hand = 1, crumbly = 1, snow = 1, suffocates = 2, cold = 1, silk_touchable = 1, },
    tiles = {
        "aom_snow.png",
    },
    drop = "aom_soil:snow",
    sounds = aom_sounds.default_snow(),
    _scrap = {
        ["aom_scrap:dirt"] = 8,
    },
})


minetest.register_alias("aom_nodes_surface:sand", "aom_soil:sand")
minetest.register_alias("aom_nodes_surface:dirt", "aom_soil:dirt")

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