BioInd.writeDebug("Entered control_arboretum.lua")

---Arboretum Stuff

local Event = require('__kry_stdlib__/stdlib/event/event').set_protected_mode(false)

-- If a recipe with NORMAL FERTILIZER is used, don't fertilize tiles set have "true"
-- set! (Fertile tiles set to true in this table can't be made more fertile with
-- normal fertilizer, and nothing should grow on the other tiles.)
local Terrain_Check_1 = {
    ["grass-1"] = true,                  -- Fertility: 100%
    ["grass-3"] = true,                  -- Fertility: 85%
    ["vegetation-green-grass-1"] = true, -- Fertility: 100%
    ["vegetation-green-grass-3"] = true, -- Fertility: 85%
}

-- If a recipe with ADVANCED FERTILIZER is used, don't fertilize tiles set have "true" set!
-- (Fertile tiles in this table can't be made more fertile, and nothing should grow on the
--  the others tiles!)
local Terrain_Check_2 = {
    ["grass-1"] = true,                  -- Fertility: 100%
    ["vegetation-green-grass-1"] = true, -- Fertility: 100%
}

local plant_radius = 75

-- Different tiles are used if AlienBiomes is active
local AB, terrain_name_g1, terrain_name_g3

-- OmniFluid replaces all fluids with items, so the arboretum won't have a fluidbox!


local function get_new_position(pos)
    pos = BioInd.normalize_position(pos) or BioInd.arg_err("nil", position)
    local xxx = math.random(-plant_radius, plant_radius)
    local yyy = math.random(-plant_radius, plant_radius)

    return { x = pos.x + xxx, y = pos.y + yyy }
end


-- Check that all ingredients are available!
local function check_ingredients(arboretum)
    local recipe = arboretum.get_recipe()
    if not recipe then
        --game.print("No recipe set on arboretum")
        return nil
    end
    --game.print("Recipe name: " .. recipe.name)
    local need = storage.bi_arboretum_recipe_table[recipe.name]
    if not need then
        --game.print("No recipe data found for " .. recipe.name)
        return nil
    end

    local function check(need, have)
        for name, amount in pairs(need or {}) do
            if not (have and have[name]) or (have[name] < amount) then
                --game.print("Missing ingredient " .. name .. " (have " .. (have[name] or 0) .. " of " .. amount .. ")")
                return false
            end
        end
        return true
    end

    local inventory = arboretum.get_inventory(defines.inventory.assembling_machine_input)
    local inv_contents_raw = inventory and inventory.get_contents() or {}

    -- Check if inv_contents_raw is a map or list, convert if needed
    local function is_map(t)
        if type(t) ~= "table" then return false end
        for k, v in pairs(t) do
            if type(k) ~= "string" or type(v) ~= "number" then
                return false
            end
        end
        return true
    end

    local inv_contents
    if is_map(inv_contents_raw) then
        inv_contents = inv_contents_raw
    else
        -- Convert list of item stacks to map
        inv_contents = {}
        for _, item in pairs(inv_contents_raw) do
            inv_contents[item.name] = (inv_contents[item.name] or 0) + item.count
        end
    end

    local fluid_contents = arboretum.get_fluid_contents() or {}

    --game.print("Inventory contents (map): " .. serpent.line(inv_contents))
    --game.print("Fluid contents: " .. serpent.line(fluid_contents))

    return need and
        check(need.items, inv_contents) and
        check(need.fluids, fluid_contents) and
        { ingredients = need, name = recipe.name } or nil
end


local function consume_ingredients(arboretum, need)
    local inventory = arboretum.get_inventory(defines.inventory.assembling_machine_input)
    for item, i in pairs(need.items or {}) do
        inventory.remove({ name = item, count = i })
        BioInd.writeDebug("Removed %s (%s)", { item, i })
    end
    BioInd.show("Inventory", inventory.get_contents() or "nil")

    for fluid, f in pairs(need.fluids or {}) do
        arboretum.remove_fluid({ name = fluid, amount = f })
        BioInd.writeDebug("Removed %s (%s)", { fluid, f })
    end
    BioInd.show("Fluid contents", arboretum.get_fluid_contents() or "nil")
end


local function set_tile(current, target, surface, position)
    if current ~= target then
        surface.set_tiles(
            { { name = target, position = position } },
            true, -- correct_tiles
            true, -- remove_colliding_entities
            true, -- remove_colliding_decoratives
            true  -- raise_event
        )
    end
end

function Get_Arboretum_Recipe(ArboretumTable, event)
    BioInd.writeDebug("Entered function Get_Arboretum_Recipe(%s, %s)", { ArboretumTable, event })
    if not ArboretumTable then
        BioInd.writeDebug("%s is not a valid ArboretumTable. Leaving immediately!")
        return
    end

    local arboretum = ArboretumTable.base
    local new_position, currentTilename
    local pos, surface, Inventory, stack

    -- 'AlienBiomes' is a bool value -- we don't want to read it again if it's false,
    -- but only if it hasn't been set yet!
    AB = storage.compatible.AlienBiomes
    terrain_name_g1 = terrain_name_g1 or (AB and "vegetation-green-grass-1" or "grass-1")
    terrain_name_g3 = terrain_name_g3 or (AB and "vegetation-green-grass-3" or "grass-3")


    local check = check_ingredients(arboretum)
    local ingredients, recipe_name
    if check then
	--game.print("There are ingredients")
        ingredients, recipe_name = check.ingredients, check.name
	else
	--game.print("No ingredients")
    end

    if ingredients then
	
        local create_seedling, new_plant
        pos = BioInd.normalize_position(arboretum.position) or
            BioInd.arg_err("nil", "position")
        surface = arboretum.surface

        -- Just plant a tree and hope the ground is fertile!
        if recipe_name == "bi-arboretum-r1" then
            BioInd.writeDebug(tostring(recipe_name) .. ": Just plant a tree")

            --- 10 attempts to find a random spot to plant a tree and/or change terrain
            for k = 1, 10 do
                new_position = get_new_position(pos)
                new_plant = {
                    name = "seedling",
                    position = new_position,
                    force = "neutral"
                }

                if surface.can_place_entity(new_plant) then
                    consume_ingredients(arboretum, ingredients)
                    create_seedling = surface.create_entity(new_plant)
                    seed_planted_arboretum(event, create_seedling)
                    --- After sucessfully planting a tree, break out of the loop.
                    break
                else
                    BioInd.writeDebug("Can't plant here (attempt %s)", k)
                end
            end
            -- Fertilize the ground with normal fertilizer. Ignore tiles listed in Terrain_Check_1!
        elseif recipe_name == "bi-arboretum-r2" then
            BioInd.writeDebug(tostring(recipe_name) .. ": Just change terrain to grass-3 (basic)")

            for k = 1, 10 do --- 10 attempts to find a random spot to plant a tree and / or change terrain
                new_position = get_new_position(pos)
                currentTilename = surface.get_tile(new_position.x, new_position.y).name

                -- We need to fertilize the ground!
                if Bi_Industries.fertility[currentTilename] and not Terrain_Check_1[currentTilename] then
                    consume_ingredients(arboretum, ingredients)
                    BioInd.writeDebug("%s: Changing terrain from %s to %s (%s)",
                        { k, currentTilename or "unknown tile",
                            terrain_name_g3, serpent.line(new_position) })
                    set_tile(currentTilename, terrain_name_g3, surface, new_position)
                    --- After sucessfully changing the terrain, break out of the loop.
                    break
                else
                    BioInd.writeDebug("%s: Can't change terrain (%s)",
                        { k, currentTilename or "unknown tile" })
                end
            end
            -- Fertilize the ground with advanced fertilizer. Ignore tiles listed in Terrain_Check_2!
        elseif recipe_name == "bi-arboretum-r3" then
            BioInd.writeDebug(tostring(recipe_name) .. ": Just change terrain to grass-1 (advanced)")

            for k = 1, 10 do --- 10 attempts to find a random spot to plant a tree and / or change terrain
                new_position = get_new_position(pos)
                currentTilename = surface.get_tile(new_position.x, new_position.y).name

                if Bi_Industries.fertility[currentTilename] and currentTilename ~= terrain_name_g1 then
                    consume_ingredients(arboretum, ingredients)
                    BioInd.writeDebug("%s: Changing terrain from %s to %s (%s)",
                        { k, currentTilename or "unknown tile",
                            terrain_name_g1, serpent.line(new_position) })
                    set_tile(currentTilename, terrain_name_g1, surface, new_position)
                    --- After sucessfully changing the terrain, break out of the loop.
                    break
                else
                    BioInd.writeDebug("%s: Can't change terrain (%s)",
                        { k, currentTilename or "unknown tile" })
                end
            end
            -- Fertilize the ground with normal fertilizer. Ignore tiles listed in Terrain_Check_1!
            -- Also plant a tree.
        elseif recipe_name == "bi-arboretum-r4" then
            BioInd.writeDebug(tostring(recipe_name) .. ": Plant Tree AND change the terrain to grass-3 (basic)")

            for k = 1, 10 do --- 10 attempts to find a random spot to plant a tree and / or change terrain
                new_position = get_new_position(pos)
                currentTilename = surface.get_tile(new_position.x, new_position.y).name
                new_plant = {
                    name = "seedling",
                    position = new_position,
                    force = "neutral"
                }

                -- Test to see if we can plant
                if surface.can_place_entity(new_plant) and Bi_Industries.fertility[currentTilename] then
                    consume_ingredients(arboretum, ingredients)
                    -- Refund fertilizer -- no need to waste it on fertile ground!
                    if Terrain_Check_1[currentTilename] then
                        arboretum.insert({ name = "fertilizer", count = ingredients.items.fertilizer })
                        BioInd.writeDebug("Refunded fertilizer!")
                    end

                    set_tile(currentTilename, terrain_name_g3, surface, new_position)
                    create_seedling = surface.create_entity(new_plant)
                    seed_planted_arboretum(event, create_seedling)
                    --- After sucessfully planting a tree or changing the terrain, break out of the loop.
                    break
                else
                    BioInd.writeDebug("%s: Can't change terrain and plant a tree (%s)",
                        { k, currentTilename or "unknown tile" })
                end
            end
            -- Fertilize the ground with advanced fertilizer. Ignore tiles listed in Terrain_Check_2!
            -- Also plant a tree.
        elseif recipe_name == "bi-arboretum-r5" then
            BioInd.writeDebug(tostring(recipe_name) .. ": Plant Tree and change the terrain to grass-1 (advanced)")

            for k = 1, 10 do --- 10 attempts to find a random spot to plant a tree and / or change terrain
                new_position = get_new_position(pos)
                currentTilename = surface.get_tile(new_position.x, new_position.y).name
                new_plant = {
                    name = "seedling",
                    position = new_position,
                    force = "neutral"
                }

                if surface.can_place_entity(new_plant) and Bi_Industries.fertility[currentTilename] then
                    consume_ingredients(arboretum, ingredients)
                    -- Refund fertilizer -- no need to waste it on fertile ground!
                    if Terrain_Check_2[currentTilename] then
                        arboretum.insert({
                            name = "bi-adv-fertilizer", count = ingredients.items["bi-adv-fertilizer"]
                        })
                        BioInd.writeDebug("Refunded advanced fertilizer!")
                    end

                    set_tile(currentTilename, terrain_name_g1, surface, new_position)
                    create_seedling = surface.create_entity(new_plant)
                    seed_planted_arboretum(event, create_seedling)
                    --- After sucessfully planting a tree or changing the terrain, break out of the loop.
                    break
                else
                    BioInd.writeDebug("%s: Can't change terrain and plant a tree (%s)",
                        { k, currentTilename or "unknown tile" })
                end
            end
        else
            BioInd.writeDebug("Terraformer has no recipe!")
        end
    end
end
