local settings = minetest.settings
local debug = settings:get_bool("goblins_debug", false)

-- 1 in n chance for goblin lairs to replace dungeons on mapgen
local goblins_lair_chance =
    tonumber(settings:get("goblins_lair_chance") or 2) 
if goblins_lair_chance == 0 then return end

-- Maximum elevation for goblin lairs in dungeons, 0 = no maximum
local goblins_lair_elev_max =
    tonumber(settings:get("goblins_lair_elev_max") or -30)

-- Minimum elevation for goblin lairs in dungeons, 0 = no minimum
local goblins_lair_elev_min =
    tonumber(settings:get("goblins_lair_elev_min") or -5000)

-- This is only effective in MCx based games like VoxeLibre and Mineclonia
local goblins_lair_overworld_only =
   settings:get_bool("goblins_lair_overworld_only", true)

-- 1 in n chance for goblin lairs to replace rail corridors on MCL mapgen
local goblins_lair_rail_corridor_chance =
    tonumber(settings:get("goblins_lair_rail_corridor_chance") or 2)


local function mr(...)
  return math.random(...)
end

local function tins(...)
  return table.insert(...)
end

local function mtsn(...)
  minetest.set_node(...)
end
  
  
if  goblins.compat_mode == "mc2" then
  if goblins_lair_overworld_only and 
     goblins_lair_elev_min <= mcl_vars.mg_overworld_min then
    
        goblins_lair_elev_min = mcl_vars.mg_overworld_min
  end

end



local function vtos(input)
   return dump(input):gsub("\r?\n?%s", " ")
end

local setting_check

if
   goblins_lair_chance ~= 0 
   
then
  setting_check = 1


    --look for mapgen generated dungeons
    if not goblins.db_fields["dungeons"] then
      goblins.db_write("dungeons",{})
    end

    minetest.set_gen_notify("dungeon")

    local dungeons = {}


 -- print("lair_chance: " ..goblins_lair_chance .. ", lair_elev_max: " .. goblins_lair_elev_max ..", lair_elev_min "..goblins_lair_elev_min)

    local water_source
    local lava_source
    local mossycobble = goblins.comp.default.mossycobble
    local dirt_grass
    local grass_nodes

    local function grass(size)
              if type(goblins.comp.default.grass) == "table" then
                  return goblins.comp.default.grass[1](size)

              else
                  return goblins.comp.default.grass
              end
    end
 -- print("grass = " ..vtos(grass(5)))

    local function gob_mushroom(size)
      size = size or 4
      return "goblins:mushroom_goblin"..tostring(mr(size))
    end
 -- print( gob_mushroom(4).." "..gob_mushroom(4).." "..gob_mushroom(4).." "..gob_mushroom(4))

    local slab_mossycobble = goblins.comp.default.slab_mossycobble
    local dg_replace = {"group:water", "group:cracky","group:pickaxey","group:shovely","group:sand" }

 -- print("\n***checking for water source***\n")
    --if #minetest.registered_nodes >= 1 then
     -- print(#minetest.registered_nodes.." nodes registered")
    --end

        for k in pairs(minetest.registered_nodes) do  
          --print(k)
            if string.find(k, ":water_source") then
                water_source = k
             -- print("water: " ..  k)
            end

            if string.find(k, ":lava_source") then
              lava_source = k
           -- print("lava: " ..  k)

            end

            if string.find(k, ":grass") then
              grass_nodes = k
              --print("dirt_grass: " ..  k)
            end

            if string.find(k, ":dirt_with_grass") then
              dirt_grass = k
              --print("dirt_grass: " ..  k)
            end

        end

  lava_source = lava_source or water_source --no lava in some games :P

  --create the lair!
  local function goblairgen(dg)
    local chest_node = goblins.comp.default.chest
    local ring_iter = {"goblins:moss", water_source, "goblins:moss"}
    local cur_dg

    if dg and dg.dungeon and #dg.dungeon > 1  then
       -- print("dungeon elevation of " .. vtos(vector.new(dg.dungeon[#dg.dungeon])) .. "detected")
          cur_dg = vector.new(dg.dungeon[#dg.dungeon])
          --print("cur_dg: " .. vtos(cur_dg))
    end

    local dg_check
    if cur_dg and
       cur_dg.y <= goblins_lair_elev_max or
       goblins_lair_elev_max == 0 then 
        if
          cur_dg.y >= goblins_lair_elev_min or
          goblins_lair_elev_min == 0 then
          dg_check = 1
        else
          dg_check = nil
        end
      else 
        dg_check = nil
    end

    if setting_check == 1 and dg_check == 1 and mr(1, goblins_lair_chance) == 1

      then -- LET'S DO IT!

        local dg_chest
        local dg_chest_ex = {}
        local dg_area
        local ch_area
        local replaced = {}
        --print(vtos(minetest.registered_nodes))

         print("dungeon "..#dg.dungeon.. " found at: ".. vtos(cur_dg))

        --dg_chest = minetest.find_node_near(cur_dg, 50, chest_node , true)
        --dg_area = {minp, maxp}
        dg_area = {vector.add(cur_dg, 30), vector.subtract(cur_dg, 30)}
        ch_area = {vector.add(cur_dg, 60), vector.subtract(cur_dg, 60)}

        --print("looking for chest: " .. vtos(dg_area))

        local dg_chest_f = minetest.find_nodes_in_area_under_air(ch_area[1], ch_area[2], chest_node)
     -- print(vtos(dg_chest_f))
        if #dg_chest_f >= 1 then

            dg_chest = dg_chest_f

            --gather a list of forbidden node pos to avoid
            dg_chest_ex = {}
            for i in ipairs(dg_chest) do
              tins(dg_chest_ex, dg_chest[i])
              tins(dg_chest_ex, vector.subtract(dg_chest[i],{x=0,y=1,z=0}))
              tins(dg_chest_ex, vector.add(dg_chest[i],{x=0,y=1,z=0}))
              tins(dg_chest_ex, vector.add(dg_chest[i],{x=0,y=2,z=0}))

           -- print("****  chest found at: "..vtos(dg_chest[i]) ..", "..vector.distance(dg_chest[i], cur_dg).." from dungeon center")
            end
        end

        local dg_any_f = minetest.find_nodes_in_area(dg_area[1], dg_area[2], { goblins.comp.default.cobble, goblins.comp.default.deepslate })
        -- print(dump(dg_any_f))
        
        --go over the dungeon and maybe mossy up any plain boring cobble nodes
        for i in ipairs(dg_any_f) do
          local dg_elev = math.ceil((dg_any_f[i].y - dg_area[2].y + 1)/5)
          local dg_dist = math.ceil(vector.distance(dg_any_f[i], cur_dg)/5) + 2
          -- print("dg elev= " .. dg_elev)
          --print("dg dist= " .. dg_dist)
          local node_n = minetest.get_node_or_nil(dg_any_f[i]).name

          if string.find(node_n, "stair") then
          
          elseif mr(dg_dist) == 1 then
            if string.find(node_n, "deepslate") then
              mtsn(dg_any_f[i], { name = "goblins:deepslate_with_moss" })  
            else
              mtsn(dg_any_f[i], { name = "goblins:cobble_with_moss" })
            end
          end
        end

        -- extra goblin floor treatment
        local dg_floor_f = minetest.find_nodes_in_area_under_air(dg_area[1], dg_area[2], { "group:stone","group:cracky","group:cobble" })
        local lava_count = 0
        local lava_max = 1
        for i in ipairs(dg_floor_f) do
          local dg_dist = math.ceil(vector.distance(dg_floor_f[i], cur_dg)/5) + 1
          local function dgfix(n)
              --print(vtos(n))
              local vn = vector.new(vector.add(dg_floor_f[i],vector.new({ x = 0, y = n , z = 0 })))
              return vn
            end
          --print (vtos(dgfix(3)))
          -- do not mess with the stairs!
          local stair_ex = minetest.get_node_or_nil(dg_floor_f[i]).name

          if stair_ex and string.find(stair_ex, "stair") then
              tins(dg_chest_ex, dg_floor_f[i])
          end

          if table.indexof(dg_chest_ex, dg_floor_f[i]) == -1 and
                                    mr(dg_dist) >= 2 then

            if mr(50) == 1 and lava_count < lava_max then

              lava_count = lava_count + 1
              mtsn(dgfix(0), { name = "air" })
              mtsn(dgfix(-1), { name = "air" })
              mtsn(dgfix(-2), { name = lava_source })

            elseif  mr(10) == 1 then
              mtsn(dgfix(1), { name = gob_mushroom(4) })
            elseif  mr(10) == 1 then
                mtsn(dgfix(0), { name = gob_mushroom(4) })
            elseif mr(10) == 1 then
                mtsn(dgfix(0), { name = dirt_grass })
            elseif mr(20) == 1 then
                mtsn(dgfix(0), { name = water_source })
            elseif mr(10) == 1 then
                mtsn(dgfix(0), { name = slab_mossycobble })
            elseif mr(5) == 1 then
                mtsn(dgfix(1), { name = grass() })
            elseif mr(6) == 1 then
                mtsn(dgfix(0), { name = "goblins:moss" })
            elseif mr(5) == 1 then
                mtsn(dgfix(1), { name = mossycobble })
            elseif mr(3) == 1 then
                mtsn(dgfix(1), { name = slab_mossycobble })
            end
          end
        end

        local function columns(center)
            if center then
                local n_ring
             -- print("*  creating ring at: "..vtos(center))
                local rmax
                local rmin
                if table.indexof(replaced, dg_chest_ex ) == -1 then
                  tins(replaced, dg_chest_ex ) 
                end
                for i in ipairs(ring_iter) do
                  --local num = #ring_iter + 1 - i

                  local num = i
                  rmax = vector.add(center, vector.new(num,0,num))
                  rmin = vector.subtract(center, vector.new(num,0,num)) 
                  n_ring = minetest.find_nodes_in_area_under_air(rmin, rmax, dg_replace )

                  if n_ring then
                   -- print("*  setting nodes around ring: ".. num)

                      for r_node in ipairs(n_ring) do
                          local rpos = minetest.serialize(n_ring[r_node])
                          if table.indexof(replaced, rpos) == -1 then
                           -- print(ring_iter[num].. " = " .. vtos(n_ring[r_node]))
                              mtsn(n_ring[r_node], { name = ring_iter[num] })
                              tins(replaced, rpos)
                          end
                      end
                  end 
                end

                local cols1 = {
                    vector.new(rmax.x+1, rmax.y, rmax.z+1),
                    vector.new(rmax.x+1, rmax.y, rmin.z -1),
                    vector.new(rmin.x-1, rmax.y, rmin.z-1),
                    vector.new(rmin.x-1, rmax.y, rmax.z+1),
                }
                local cols2 ={ 
                    vector.add(cols1[1], vector.new(-2,0,0)),
                    vector.add(cols1[1], vector.new(0,0,-2)),
                    vector.add(cols1[2], vector.new(-2,0,0)),
                    vector.add(cols1[2], vector.new(0,0,2)),
                    vector.add(cols1[3], vector.new(2,0,0)),
                    vector.add(cols1[3], vector.new(0,0,2)),
                    vector.add(cols1[4], vector.new(2,0,0)),
                    vector.add(cols1[4], vector.new(0,0,-2)),
                }

                for i in ipairs(cols1) do

                  local col=vector.new(cols1[i])
               -- print("column1 at "..vtos(col))
                  local toppers = { grass(5), slab_mossycobble, gob_mushroom(4) }
                  --local h = 5
                  local h = 0
                  --col = vector.add( col, vector.new(0,1,0) ) 
                  local colc = vector.add( col, vector.new(0,0,0) ) 
                  local max = 20  

                  while max >=1 and minetest.get_node(colc) and minetest.get_node(colc).name == "air" do
                    max = max - 1
                    h = h + 1
                    colc = vector.add( colc, vector.new(0,1,0) ) 
                  end  

               -- print(" col h:  "..h)
                  for j=1, h do

                      if mr(2) == 1 then
                          mtsn( col, { name = "goblins:moss" } )
                      else
                          mtsn( col, { name = mossycobble } )
                      end
                      col = vector.add( col, vector.new(0,1,0))

                      if mr(5) == 1 then
                        mtsn( col, { name = toppers[mr(#toppers)] } )
                        break
                      end
                  end

                end

                for i in ipairs(cols2) do
                  local col=vector.new(cols2[i])
               -- print("column2 at "..vtos(col))
                  local toppers = { grass(5), slab_mossycobble, gob_mushroom(4) }
                     --local h = 5
                     local h = 0
                     --col = vector.add( col, vector.new(0,1,0) ) 
                     local colc = vector.add( col, vector.new(0,0,0) ) 
                     local max = 20  

                     while max >=1 and minetest.get_node(colc) and minetest.get_node(colc).name == "air" do
                       max = max - 1
                       h = h + 1
                       colc = vector.add( colc, vector.new(0,1,0) ) 
                     end  

                 -- print(" col h:  "..h)
                    for j=1, h do

                        if mr(2) == 1 then
                            mtsn( col, { name = "goblins:moss" } )
                        else
                            mtsn( col, { name = mossycobble } )
                        end
                        col = vector.add( col, vector.new(0,1,0))

                        if mr(3) == 1 then
                          mtsn( col, { name = toppers[mr(#toppers)] } )
                          break
                        end
                    end
                end
            end
        end

        columns(cur_dg)
        if dg_chest then
          for i in ipairs(dg_chest) do
            columns(vector.subtract(dg_chest[i], vector.new(0,1,0)))
          end
        end

      end
  end 


  minetest.register_on_generated(function(minp, maxp, blockseed)

      local dg = minetest.get_mapgen_object("gennotify")
      goblairgen(dg)
     
  end)

  if goblins.compat_mode == "mc2" and goblins_lair_rail_corridor_chance ~= 0 and goblins_lair_chance ~= 0 then
    --get mineshafts for goblin lairs - thank you Cora!
    local dg = {dungeon = {}}
    print(dump(mcl_structures.registered_structures["mineshaft"]))
   
    mcl_structures.registered_structures["mineshaft"].after_place = function(pos, def, pr) 
    print("*** mineshaft placed: "..vector.to_string(pos))
    
    --print(" def: "..dump(def).." pr: "..dump(pr))

    tins(dg.dungeon, pos)
    print("goblin dungeon list: #" .. #dg.dungeon .." " .. vector.to_string(dg.dungeon[#dg.dungeon]))
    goblairgen(dg)
  
    end
end
  
end