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

local sealevel = 0
local alt_max = 1000
local alt_min = -1000

-------------------------------
--------- DECORATIONS ---------
-------------------------------

local function sch(name)
    return (mod_path .. DIR_DELIM .. "schematics" .. DIR_DELIM .. name .. ".mts")
end

local perlin = {}
local function register_noise(p)
    perlin[p.name] = p
end

register_noise({
    name = "variant",
    np = {
        offset = 0.5,
        scale = 0.5,
        spread = {x = 1, y = 1, z = 1},
        seed = 678567 + minetest.get_mapgen_setting("seed"),
        octaves = 1,
        persist = 0.1,
        lacunarity = 2.0,
    },
    perlin = nil,
    data = {},
})
register_noise({
    name = "biome",
    np = {
        offset = 0.5,
        scale = 0.5,
        spread = {x = 900, y = 900, z = 900},
        seed = 87602 + minetest.get_mapgen_setting("seed"),
        octaves = 1,
        persist = 0.0,
        lacunarity = 2.0,
    },
    perlin = nil,
    data = {},
})

local rotations = {
    "0", "90", "180", "270"
}

function br_core.get_biome(pos)
end

local function level_from_vm(minp, maxp)
    return math.floor((minp.y + 32) / br_core.chunk_width)
end


if br_core.mapgen == "flat" then
    minetest.register_ore({
        ore_type       = "stratum",
        ore            = "br_core:barrier",
        wherein        = {"air", "group:liquid"},
        y_min = -32,
        y_max = -32,
    })
end

local function to_grid(n, seg)
    seg = seg or br_core.chunk_width
    return math.floor((n+16)/seg)
end

if true then
    minetest.register_on_generated(function(minp, maxp)
        local level = level_from_vm(minp, maxp)
        local level_def = br_core.level[level]
        if not br_core.level[level] then return end
        local segsize = br_core.level[level].segsize
        local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")

        minetest.generate_ores(vm, minp, maxp)
        minetest.generate_decorations(vm, minp, maxp)
        if (br_core.mapgen == "flat" or ((br_core.mapgen == "v6") and level ~= 0)) then return end

        local sidelen = math.abs(math.ceil(maxp.x - minp.x)) + 1
        local permapdims3d = {x = sidelen, y = sidelen, z = sidelen}

        -- get the perlin noise data
        for name, p in pairs(perlin) do
            p.perlin = p.perlin or minetest.get_perlin_map(p.np, permapdims3d)
            p.perlin:get3dMap_flat(minp, p.data)
        end

        local chunk_width = br_core.chunk_width
        local chunk_offset = 8
        local biomes = br_core.level[level].biome

        local ni = 1
        for z = 0, math.floor(chunk_width / segsize) - 1 do
            for x = 0, math.floor(chunk_width / segsize) - 1 do
                -- get the biome for this horizontal segment location
                local zero_dist = math.sqrt((x + to_grid(minp.x, segsize)) ^ 2 + (z + to_grid(minp.z, segsize)) ^ 2)
                local biomeindex = 1
                if zero_dist > (level_def.grace_dist or 20)
                or (not biomes[biomeindex].can_generate)
                or not biomes[biomeindex].can_generate(vector.new(x + to_grid(minp.x, segsize), 0, z + to_grid(minp.z, segsize))) then
                    -- get a random biome from the list based on the noise
                    biomeindex = math.floor(perlin.biome.data[ni] * (level_def.biome_roughness or 17.239)) % (#biomes+1) + 1
                    -- check this biome can go here, and if not, cycle through until you find one that works
                    for i=0, #biomes - 1 do
                        local index = (biomeindex + i) % #biomes + 1
                        if (not biomes[index].can_generate)
                        or biomes[index].can_generate(vector.new(x + to_grid(minp.x, segsize), 0, z + to_grid(minp.z, segsize))) == true then
                            biomeindex = index
                            break -- when you find one, end the search
                        end
                    end
                end
                -- get the biome definition so we know stuff about the biome
                local biome = br_core.level[level].biome[biomeindex]
                ----------------------
                -- ACTUAL PLACEMENT --
                ----------------------
                local skips = 0
                local rotation = rotations[(math.floor(92801747 * perlin.variant.data[ni]) % #rotations) + 1]
                for y = 0, math.ceil(chunk_width / biome.segheight) - 1 do
                    if skips < 1 then
                        -- get a random variant based on the noise
                        local variant = math.floor(#biome.vert_schems[y+1] * perlin.variant.data[ni]) + 1
                        local schem = biome.vert_schems[y+1][variant]
                        -- get a new schem if this one shouldn't spawn here
                        if schem and schem.can_generate
                        and not schem.can_generate(vector.new(x + to_grid(minp.x, segsize), y, z + to_grid(minp.z, segsize))) then
                            for i=0, #biome.vert_schems[y+1] - 1 do
                                local index = (variant + i) % #biome.vert_schems[y+1] + 1
                                if biome.vert_schems[index] and ((not biome.vert_schems[index].can_generate)
                                or biome.vert_schems[index].can_generate(vector.new(
                                    x + to_grid(minp.x, segsize),
                                    y,
                                    z + to_grid(minp.z, segsize))) == true) then

                                    variant = index
                                    schem = biome.vert_schems[y+1][variant]
                                    break -- when you find one, end the search
                                end
                            end
                        end
                        -- now place the schematic
                        if schem then
                            if y == 0 then
                                rotation = (schem.rotation or schem.no_rotation and "0") or rotation
                            end
                            local y_offset = schem.y_offset or 0
                            local pos = vector.new(x*segsize+minp.x, y*biome.segheight+minp.y + y_offset, z*segsize+minp.z)
                            local rot = rotation
                            if not minetest.place_schematic_on_vmanip(vm, pos, sch(schem.name), rot, nil, true, nil) then
                            end
                        else
                            -- minetest.log("ERROR NO SCHEMATIC")
                        end
                        ni = ni + 1
                        if schem and schem.skip_above then
                            skips = (schem.skip_above)
                        end
                    else
                        skips = skips - 1
                    end
                end
            end
        end
        -- minetest.log(maxp.x-minp.x.."  "..segsize.."  "..ni-1)
        -- vm:set_data(data)
        vm:calc_lighting()
        -- minetest.generate_ores(vm, minp, maxp)
        -- minetest.generate_decorations(vm, minp, maxp)
        vm:write_to_map()
        vm:update_liquids()
    end)
end