local up = vector.new(0, 1, 0)
function sbz_api.plant_grow(next_stage)
    return function(pos, node)
        if node.param2 > 0 then
            node.param2 = node.param2 - 1
        else
            node.name = next_stage
        end
        core.swap_node(pos, node)
    end
end

local warpshroom_family = {
    'sbz_bio:warpshroom',
    'sbz_bio:shockshroom',
    'sbz_bio:stemfruit_plant',
}
local pyrograss_family = {
    'sbz_bio:pyrograss',
    'sbz_bio:razorgrass',
    'sbz_bio:cleargrass',
    'sbz_bio:stemfruit_plant',
}
local fiberweed_family = {
    'sbz_bio:fiberweed',
}
local can_turn_into = {
    ['sbz_bio:stemfruit_plant'] = {
        'sbz_bio:pyrograss',
        'sbz_bio:razorgrass',
        'sbz_bio:cleargrass',
        'sbz_bio:warpshroom',
        'sbz_bio:shockshroom',
        --        "sbz_bio:fiberweed",
    },
    ['sbz_bio:warpshroom'] = warpshroom_family,
    ['sbz_bio:shockshroom'] = warpshroom_family,
    ['sbz_bio:pyrograss'] = pyrograss_family,
    ['sbz_bio:razorgrass'] = pyrograss_family,
    ['sbz_bio:cleargrass'] = pyrograss_family,
    ['sbz_bio:fiberweed'] = fiberweed_family,
}

local special_cases = { ['sbz_bio:fiberweed'] = true }

sbz_api.bio_can_turn_into = can_turn_into -- modding support :)
sbz_api.bio_special_cases = special_cases

-- i actually forgot what this is for
-- i assume it's for checking if the stuff above is good
core.after(0, function()
    for initial_node, node_list in pairs(can_turn_into) do
        if not core.registered_nodes[initial_node .. '_1'] and not special_cases[initial_node] then
            error('Uh oh: ' .. initial_node)
        end
        for k, v in pairs(node_list) do
            if not core.registered_nodes[v .. '_1'] and not special_cases[v] then error('Uh oh:' .. v) end
        end
    end
end)

local radiation_check = function(pos)
    local nodes =
        core.find_nodes_in_area(pos - vector.new(5, 5, 5), pos + vector.new(5, 5, 5), { 'group:radioactive' }, true)
    local rad = 0
    for nodename, poslist in pairs(nodes) do
        local val = core.get_item_group(nodename, 'radioactive')
        for _, radpos in ipairs(poslist) do
            local dist = vector.distance(pos, radpos)
            rad = rad + ((val ^ 2) / math.max(0.75, dist ^ 2))
        end
    end
    return rad -- so rad
end

function sbz_api.plant_growth_tick(num_ticks, mutation_chance)
    return function(pos, node)
        local rad = radiation_check(pos)
        -- MUTATIONS
        -- mutation_chance% chance to mutate every second, when a basic neutron emitter is less than 1 node nearby (rad value of 9 i think?)
        local chance = ((rad / 9) * mutation_chance) / 100
        if math.random() < chance then
            local nn = node.name
            local basename = string.sub(nn, 1, -3)
            local stage = string.sub(nn, -2)
            local possibilities = can_turn_into[basename]
            local should_turn_into = possibilities[math.random(1, #possibilities)]
            local newnode = table.copy(node)
            local nodepos = vector.copy(pos)
            if special_cases[should_turn_into] then
                if should_turn_into == 'sbz_bio:fiberweed' then
                    if core.get_node(vector.subtract(nodepos, vector.new(0, 1, 0))).name == 'sbz_bio:dirt' then
                        nodepos = vector.subtract(nodepos, vector.new(0, 1, 0))
                        should_turn_into = 'sbz_bio:fiberweed'
                    else
                        return true -- can't mutate
                    end
                end
                newnode.name = should_turn_into
            else
                newnode.name = should_turn_into .. stage
            end
            core.swap_node(nodepos, newnode)
            return true -- false =>  too much and it will wilt?
        end
        if sbz_api.get_node_heat(pos) > 7 and sbz_api.is_hydrated(pos) then
            local meta = core.get_meta(pos)
            local count = meta:get_int 'count' + 1

            local under = vector.copy(pos)
            under.y = under.y - 1

            local growth_multiplier = 1
            local soil = core.get_item_group((sbz_api.get_or_load_node(under) or { name = '' }).name, 'soil')
            growth_multiplier = math.max(0, growth_multiplier + (soil - 1))

            iterate_around_pos(pos, function(ipos)
                local n = sbz_api.get_or_load_node(ipos)
                if n and core.get_item_group(n.name, 'growth_boost') > 0 then
                    growth_multiplier = growth_multiplier
                        + (growth_multiplier * (core.get_item_group(n.name, 'growth_boost') / 100))
                end
            end)

            meta:set_float('growth_multiplier', growth_multiplier)
            if count >= (num_ticks / growth_multiplier) then
                count = 0
                core.registered_nodes[node.name].grow(pos, node)
            end

            meta:set_int('count', count)
            return true
        end
    end
end

function sbz_api.plant_wilt(stages)
    return function(pos, node)
        if core.get_item_group(node.name, 'no_wilt') == 0 then
            node.param2 = node.param2 + 1
            core.swap_node(pos, node.param2 >= stages and { name = 'air' } or node)
        end
    end
end

function sbz_api.plant_plant(plant, nodes)
    return sbz_api.on_place_precedence(function(itemstack, user, pointed)
        for _, node in ipairs(nodes) do
            local use_pointed = 'above'
            if pointed.switched then use_pointed = 'under' end
            local soil_node = core.get_node(pointed[use_pointed] - up)
            if
                string.sub(node, 1, 6) == 'group:'
                    and core.get_item_group(soil_node.name, string.sub(node, 7)) > 0
                or soil_node.name == node
            then
                local _, pos = core.item_place_node(ItemStack(plant), user, pointed)
                if pos then
                    itemstack:take_item()
                    return itemstack
                end
                return
            end
        end
    end)
end

function sbz_api.register_plant(name, defs, modname)
    defs.description = defs.description or ''
    defs.drop = defs.drop
    defs.growth_rate = defs.growth_rate or 1
    defs.co2_demand = defs.co2_demand or 0
    defs.width = defs.width or 0.5
    defs.height_min = defs.height_min or 0.5
    defs.height_max = defs.height_max or 0.5
    defs.stages = defs.stages or 4

    if modname then
        modname = modname .. ':'
    else
        modname = 'sbz_bio:'
    end

    local growth_boost_base = (defs.growth_boost or 0) / defs.stages
    local power_per_co2_base = (defs.power_per_co2 or 0) / defs.stages

    for i = 1, defs.stages - 1 do
        local interpolant = (i - 1) / (defs.stages - 1)
        local height = defs.height_min * (1 - interpolant) + defs.height_max * interpolant
        core.register_node(modname .. name .. '_' .. i, {
            description = defs.description,
            drawtype = 'plantlike',
            tiles = { name .. '_' .. i .. '.png' },
            inventory_image = name .. '_' .. i .. '.png',
            selection_box = {
                type = 'fixed',
                fixed = { -defs.width, -0.5, -defs.width, defs.width, height, defs.width },
            },
            paramtype = 'light',
            sunlight_propagates = true,
            paramtype2 = 'color',
            palette = 'wilting_palette.png',
            walkable = false,
            groups = {
                dig_immediate = 2,
                attached_node = 1,
                plant = 1,
                needs_co2 = defs.co2_demand,
                habitat_conducts = 1,
                transparent = 1,
                not_in_creative_inventory = 1,
                burn = 1,
                nb_nodig = 1,
                no_wilt = defs.no_wilt and 1 or 0,
                growth_boost = growth_boost_base * i,
            },
            drop = {},
            growth_tick = sbz_api.plant_growth_tick(defs.growth_rate, defs.mutation_chance or 10),
            grow = sbz_api.plant_grow(modname .. name .. '_' .. (i + 1)),
            wilt = sbz_api.plant_wilt(2),
            sbz_player_inside = defs.sbz_player_inside,
            power_per_co2 = power_per_co2_base * i,
        })
    end
    core.register_node(modname .. name .. '_' .. defs.stages, {
        description = defs.description,
        drawtype = 'plantlike',
        tiles = { name .. '_' .. defs.stages .. '.png' },
        inventory_image = name .. '_' .. defs.stages .. '.png',
        selection_box = {
            type = 'fixed',
            fixed = { -defs.width, -0.5, -defs.width, defs.width, defs.height_max, defs.width },
        },
        paramtype = 'light',
        sunlight_propagates = true,
        paramtype2 = 'color',
        palette = 'wilting_palette.png',
        walkable = false,
        groups = {
            oddly_breakable_by_hand = 3,
            matter = 3,
            attached_node = 1,
            plant = defs.use_co2_in_final_stage and 1 or 0,
            habitat_conducts = 1,
            transparent = 1,
            not_in_creative_inventory = 1,
            burn = 1,
            needs_co2 = defs.use_co2_in_final_stage and defs.co2_demand or 0,
            growth_boost = defs.growth_boost,
        },
        drop = defs.drop,
        sbz_player_inside = defs.sbz_player_inside,
        power_per_co2 = defs.power_per_co2,
        growth_tick = defs.use_co2_in_final_stage and function()
            return true
        end,
        grow = defs.use_co2_in_final_stage and function()
            return true
        end,
        wilt = defs.use_co2_in_final_stage and sbz_api.plant_wilt(2),
    })
end

-- PYROGRASS FAMILY
--Pyrograss, hardy and quick to grow, highly flammable due to its carbon content
--To be used in rockets and explosives and stuff
sbz_api.register_plant('pyrograss', {
    description = 'Pyrograss Plant',
    drop = 'sbz_bio:pyrograss 2',
    growth_rate = 4,
    family = 'sbz_bio:pyrograss',
    width = 0.25,
    height_min = -0.375,
    height_max = 0,
    no_wilt = true,
})
core.register_craftitem('sbz_bio:pyrograss', {
    description = 'Pyrograss',
    inventory_image = 'pyrograss_4.png',
    groups = { burn = 30, eat = 1 },
    sound = {
        eat = {
            name = 'foley_crackle_chomp_simple', gain = 1, pitch = 1.0
        }
    },
    on_place = sbz_api.plant_plant('sbz_bio:pyrograss_1', { 'group:soil' }),
})

-- uses:
-- ingredient in powered dirt
-- 3 razorgrass = 10 fertilizer
-- poison bullets for later
-- compressed razorgrass could maybe look nice
-- grows slightly slower than pyro
-- no CO₂ demand

playereffects.register_effect_type(
    'poison',
    'Poisoned',
    'fx_poison.png',
    { 'clearable', 'negative', 'speed', 'health', 'negative_health' },
    function(player)
        unlock_achievement(player:get_player_name(), 'Poisoned')
        player:set_hp(math.max(0, player:get_hp() - 1))
        player_monoids.speed:add_change(player, 0.5, 'sbz_bio:poison')
    end,
    function(fx, player)
        player_monoids.speed:del_change(player, 'sbz_bio:poison')
    end,
    false,
    true,
    2
)

sbz_api.register_plant('razorgrass', {
    description = 'Razorgrass Plant',
    drop = 'sbz_bio:razorgrass 2',
    growth_rate = 8,
    family = 'pyrograss',
    width = 0.25,
    height_min = -0.375,
    height_max = 0,
    sbz_player_inside = function(pos, player)
        if not player:is_valid() then return end
        player:set_hp(player:get_hp() - 1)
        playereffects.apply_effect_type('poison', 2, player)
    end,
})

do -- Fertilizer bulk recipe scope
    local Fertilizer = 'sbz_bio:fertilizer'
    local amount = 20
    local Ra = 'sbz_bio:razorgrass'
    core.register_craft({
        type = 'shapeless',
        output = Fertilizer .. ' ' .. tostring(amount),
        recipe = { Ra, Ra, Ra },
    })
end

core.register_craftitem('sbz_bio:razorgrass', {
    description = 'Razorgrass',
    inventory_image = 'razorgrass_4.png',
    groups = { burn = 2, eat = -8 },
    sound = {
        eat = {
            name = 'foley_crackle_chomp_thin', gain = 1, pitch = 1.0
        }
    },
    eat_fx = { 'Poisoned', 'Slowed' },
    on_place = sbz_api.plant_plant('sbz_bio:razorgrass_1', { 'group:soil' }),
    on_use = function(stack, user, pointed)
        if user.is_fake_player then return end
        playereffects.apply_effect_type('poison', 10, user)
        return core.item_eat(-8)(stack, user, pointed)
    end,
})

-- cleargass, uses:
-- clearing effect
-- blinding bullets for later
-- could be used to "polish" decoblocks
-- boosts plants near it by 15% when in full growth stage, consumes CO₂, makes plants consume more CO₂
-- grows slowly

playereffects.register_effect_type('immune', 'Immune', 'fx_immunity.png', { 'immunity' }, function(player)
    unlock_achievement(player:get_player_name(), 'Immune')
    playereffects.cancel_effect_group('clearable', player:get_player_name())
end, function(fx, player) end, false, true, 0.1)

sbz_api.register_plant('cleargrass', {
    description = 'Cleargrass Plant',
    drop = 'sbz_bio:cleargrass 2',
    growth_rate = 4,
    co2_demand = 5,
    family = 'pyrograss',
    width = 0.25,
    height_min = -0.375,
    height_max = 0,
    sbz_player_inside = function(pos, player)
        playereffects.apply_effect_type('immune', 10, player)
    end,
    use_co2_in_final_stage = true,
    growth_boost = 25,
})

core.register_craftitem('sbz_bio:cleargrass', {
    description = 'Cleargrass',
    inventory_image = 'cleargrass_4.png',
    groups = { burn = 0, eat = 0 },
    sound = {
        eat = {
            name = 'foley_crackle_chomp_smooth', gain = 1, pitch = 1.0
        }
    },
    eat_fx = { 'Cleared' },
    on_place = sbz_api.plant_plant('sbz_bio:cleargrass_1', { 'group:soil' }),
    on_use = function(stack, user, pointed)
        playereffects.apply_effect_type('immune', 30 / 0.1, user)
        return core.item_eat(0)(stack, user, pointed)
    end,
})

-- STEMFRUIT: base of all plants
--Stemfruit, generic plant, quite versatile
--To be used to craft other plants
sbz_api.register_plant('stemfruit_plant', {
    description = 'Stemfruit Plant',
    drop = 'sbz_bio:stemfruit 3',
    family = 'stemfruit',
    growth_rate = 8,
    co2_demand = 1,
    width = 0.125,
    height_min = -0.25,
    height_max = 0.5,
    mutation_chance = 80,
})

core.register_craftitem('sbz_bio:stemfruit', {
    description = 'Stemfruit',
    inventory_image = 'stemfruit.png',
    groups = { burn = 12, eat = 5 },
    on_place = sbz_api.on_place_precedence(function(itemstack, user, pointed)
        local use_pointed = 'above'
        if pointed.switched then use_pointed = 'under' end
        local soil_node = core.get_node(pointed[use_pointed] - up)
        local water_node = core.get_node(pointed[use_pointed] + up)
        if core.get_item_group(soil_node.name, 'soil') > 0 then
            if core.get_item_group(water_node.name, 'water') > 0 then
                return core.registered_items['sbz_bio:fiberweed'].on_place(itemstack, user, pointed)
            end
            local _, pos = core.item_place_node(ItemStack 'sbz_bio:stemfruit_plant_1', user, pointed)
            if pos then
                itemstack:take_item()
                return itemstack
            end
            return
        end
    end), --sbz_api.plant_plant("sbz_bio:stemfruit_plant_1", { "group:soil" })
})

-- SHROOM FAMILY

--Warpshroom, grows slowly, has teleportation powers
--To be used later in teleporters
sbz_api.register_plant('warpshroom', {
    description = 'Warpshroom Plant',
    drop = 'sbz_bio:warpshroom 2',
    family = 'warpshroom',
    growth_rate = 16,
    co2_demand = 1,
    width = 0.25,
    height_min = -0.3125,
    height_max = 0.25,
})

local warpshroom_teleport_radius = 16
local function teleport_randomly(user)
    local user_pos = vector.round(user:get_pos())
    for _ = 1, 1000 do
        local pos = user_pos
            + vector.new(
                math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius),
                math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius),
                math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius)
            )
        if
            not core.registered_nodes[core.get_node(pos).name].walkable
            and not core.registered_nodes[core.get_node(pos + up).name].walkable
            and core.registered_nodes[core.get_node(pos - up).name].walkable
        then
            return user:set_pos(pos - up * 0.5) -- this was missing a "return"... yeah... bad bad
        end
    end
end

local eat = core.item_eat(6)

core.register_craftitem('sbz_bio:warpshroom', {
    description = 'Warpshroom',
    inventory_image = 'warpshroom_4.png',
    on_place = sbz_api.plant_plant('sbz_bio:warpshroom_1', { 'group:matter' }),
    on_use = function(itemstack, user, pointed)
        teleport_randomly(user)
        unlock_achievement(user:get_player_name(), 'Not Chorus Fruit')
        return eat(itemstack, user, pointed)
    end,
    groups = { ui_bio = 1, eat = 6 },
})
--[[
core.register_craft({
    type = "shapeless",
    output = "sbz_bio:warpshroom",
    recipe = { "sbz_bio:stemfruit", "sbz_meteorites:neutronium" }
})
]]
-- Shockshroom, +40 power, needs 2 CO₂
-- ingredient in powered dirt
playereffects.register_effect_type('shocked', 'Shocked', 'fx_shocked.png', { 'clearable', 'speed' }, function(player)
    ---modifies the vector, returns it
    local abs_y = function(vec)
        vec.y = math.abs(vec.y)
        return vec
    end

    -- mild loss of control and bad combo with wet
    unlock_achievement(player:get_player_name(), 'Shocked')
    player:add_velocity(vector.multiply(vector.random_direction(), 2))
    player_monoids.speed:add_change(player, 1.2, 'sbz_bio:shocked')
    if playereffects.has_effect_type(player:get_player_name(), 'wet') then
        player:set_hp(math.max(0, player:get_hp() - 8))
        local pos = player:get_pos()
        core.add_particlespawner {
            amount = 50,
            time = 0.1,
            radius = { min = 0.1, max = 0.5 },
            pos = pos,
            vel = { min = { x = -5, y = -5, z = -5 }, max = { x = 5, y = 5, z = 5 } },
            exptime = { min = 15, max = 25 },
            size = { min = 0.5, max = 1 },
            collisiondetection = false,
            vertical = false,
            texture = 'star.png^[colorize:yellow:255',
            glow = 14,
        }
        player:add_velocity(abs_y(vector.multiply(vector.random_direction(), 8)))
        playereffects.cancel_effect_type('shocked', false, player:get_player_name())
    end
end, function(fx, player)
    player_monoids.speed:del_change(player, 'sbz_bio:shocked')
end, false, true, 0.5)

sbz_api.register_plant('shockshroom', {
    description = 'Shockshroom Plant',
    drop = 'sbz_bio:shockshroom 2',
    family = 'warpshroom',
    growth_rate = 6,
    co2_demand = 2,
    width = 0.25,
    height_min = -0.3125,
    height_max = 0.25,
    sbz_player_inside = function(pos, player)
        playereffects.apply_effect_type('shocked', 2 / 0.5, player)
    end,
    power_per_co2 = 30,
    use_co2_in_final_stage = true,
})

core.register_craftitem('sbz_bio:shockshroom', {
    description = 'Shockshroom',
    inventory_image = 'shockshroom_4.png',
    on_place = sbz_api.plant_plant('sbz_bio:shockshroom_1', { 'group:soil' }),
    groups = { ui_bio = 1, eat = -1 },
    on_use = function(stack, user, pointed)
        if user.is_fake_player then return end
        playereffects.apply_effect_type('shocked', 180 / 0.5, user, 0.5)
        return core.item_eat(-1)(stack, user, pointed)
    end,
})

-- STARFRUIT FAMILY (unlike the others, there is no base "starfruit" plant)
-- todo

--Fiberweed, multi-node seaweed which grows vertically, requires water along its entire stem
--To be used in making string and fabric for various uses
local function is_all_water(pos, leveled)
    for i = 1, math.ceil(leveled / 16) do
        local nodename = core.get_node(pos + up * i).name
        if nodename ~= 'sbz_resources:water_source' and nodename ~= 'sbz_resources:water_flowing' then return false end
    end
    return true
end

core.register_node('sbz_bio:fiberweed', {
    description = 'Fiberweed',
    drawtype = 'plantlike_rooted',
    tiles = { 'dirt.png^fiberweed_overlay.png', 'dirt.png' },
    special_tiles = { { name = 'fiberweed_plant.png', tileable_vertical = true } },
    inventory_image = 'fiberweed_plant.png',
    selection_box = {
        type = 'fixed',
        fixed = { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, { -0.25, 0.5, -0.25, 0.25, 1.25, 0.25 } },
    },
    paramtype2 = 'leveled',
    place_param2 = 8,
    groups = { matter = 1, plant = 1, needs_co2 = 1, transparent = 1, burn = 2 },
    drop = {},
    node_dig_prediction = 'sbz_bio:dirt',
    node_placement_prediction = '',
    on_place = function(itemstack, user, pointed)
        if pointed.type ~= 'node' or core.get_node(pointed.under).name ~= 'sbz_bio:dirt' then return end
        core.set_node(pointed.under, { name = 'sbz_bio:fiberweed', param2 = 8 })
        itemstack:take_item()
        return itemstack
    end,
    after_dig_node = function(pos, node, meta, user)
        core.set_node(pos, { name = 'sbz_bio:dirt' })
        unlock_achievement(user:get_player_name(), 'Fiberweed')
        local inv = user:get_inventory()
        local drop = inv:add_item('main', 'sbz_bio:fiberweed ' .. math.floor(node.param2 / 8))
        if drop then core.add_item(pos + up, drop) end
    end,
    growth_tick = function(pos, node)
        if sbz_api.get_node_heat(pos + up) > 7 and is_all_water(pos, node.param2) then
            local meta = core.get_meta(pos)
            local count = meta:get_int 'count' + 1
            if count >= 4 then
                count = 0
                core.registered_nodes[node.name].grow(pos, node)
            end
            meta:set_int('count', count)
            return true
        end
    end,
    grow = function(pos, node)
        if is_all_water(pos, node.param2 + 8) then
            node.param2 = node.param2 + 8
            core.swap_node(pos, node)
        end
    end,
    wilt = function(pos, node)
        node.param2 = node.param2 - 8
        core.swap_node(pos, node.param2 <= 0 and { name = 'sbz_bio:dirt' } or node)
    end,
})
--[[
core.register_craft({
    output = "sbz_bio:fiberweed",
    recipe = {
        { "sbz_bio:algae", "sbz_bio:algae",     "sbz_bio:algae" },
        { "sbz_bio:algae", "sbz_bio:stemfruit", "sbz_bio:algae" },
        { "sbz_bio:algae", "sbz_bio:algae",     "sbz_bio:algae" }
    }
})

]]
