local args = {...}
local Logging = require("logging")
local Logger = Logging.firstLoad(
  Logging.OUTPUTS.file("tunnel.log"),
  Logging.OUTPUTS.stdout()
)

local CHEST_SLOT = #args == 1 and tonumber(args[1]) or 1
if type(CHEST_SLOT) ~= "number" or CHEST_SLOT < 1 or CHEST_SLOT > 16 then
  Logger:error("Slot number is not valid:", CHEST_SLOT)
end
local CHEST_PICKUP = #args == 2 and (args[2]:lower() )

local CHEST_DETAIL = turtle.getItemDetail(CHEST_SLOT)
if CHEST_DETAIL == nil then
  Logger:error("No chest in slot! Quitting...")
  return
end
local CHEST_NAME = CHEST_DETAIL.name

local function refuel(minFuel)
  local fuelLevel = turtle.getFuelLevel()
  while fuelLevel < minFuel do
    Logger:debug("Checking fuel level:", fuelLevel)
    for i=1,16 do
      if i == CHEST_SLOT then
        goto continue
      end

      if turtle.getItemCount(i) > 0 then
        turtle.select(i)
        turtle.refuel()
      end
        ::continue::
    end
    fuelLevel = turtle.getFuelLevel()
  end
  Logger:debug("Fuel level is sufficient:", fuelLevel)
  turtle.select(1)
end

local function isFull(minEmpty)
  local emptyCount = 0
  for i=1,16 do
    if i == CHEST_SLOT then
      goto continue
    end

    if turtle.getItemCount(i) == 0 then
      emptyCount = emptyCount + 1
      if emptyCount >= minEmpty then
        return false
      end
    end
      ::continue::
  end
  return true
end

local function placeChest()
  if not turtle.select(CHEST_SLOT) then
    Logger:error("Cannot select chest slot", CHEST_SLOT)
    return false, nil, nil, nil
  end

  if turtle.placeUp() or (turtle.digUp() and turtle.placeUp()) then
    return true, turtle.dropUp, turtle.digUp, function() end
  end

  if turtle.turnLeft() and turtle.turnLeft() and (turtle.place() or (turtle.dig() and turtle.place())) then
    return true, turtle.drop, turtle.dig, function()
      turtle.turnRight()
      turtle.turnRight()
    end
  end
  return false, nil, nil, nil
end

local function handleFullInv(minEmpty)
  local didPlace = false

  local result, drop, dig, onComplete = false, nil, nil, nil
  -- Empty inventory
  while isFull(minEmpty) do
    if not didPlace then
      local detail = turtle.getItemDetail(CHEST_SLOT)
      if type(detail) ~= "table" or detail.name ~= CHEST_NAME then
        Logger:error("Can't find chest :(")
        os.sleep(5)
        goto continue
      end

      -- Try: place, check block above is empty or dig it, place
      -- If all fails, print error, wait and repeat
      result, drop, dig, onComplete = placeChest()
      if not result then
        Logger:error("Can't place chest :(")
        os.sleep(5)
        goto continue
      end
      didPlace = true
    end

    assert(drop ~= nil, "Placed chest, but drop operation is nil")
    for i=1,16 do
      if i == CHEST_SLOT then
        goto continue_SLOT
      end
      if turtle.getItemCount(i) > 0 and not (turtle.select(i) and drop()) then
        Logger:error("Couldn't drop items into chest!")
        goto continue
      end

        ::continue_SLOT::
    end

      ::continue::
  end

  if result then
    assert(dig ~= nil, "Placed chest, but dig operation is nil")
    if didPlace and CHEST_PICKUP then
      turtle.select(CHEST_SLOT)
      dig()
    end
  
    assert(onComplete ~= nil, "Placed chest, but onComplete operation is nil")
    onComplete()
  end
end

local function dig(checkRefuel)
  while not turtle.forward() do
    turtle.dig()
    checkRefuel()
  end
  turtle.digUp()
  turtle.digDown()
end

local function line(length, turn, checkRefuel)
  turtle.digDown()
  for _=2,length do
    dig(checkRefuel)
  end
  turn()
end

local function panel(width, length, leftFirst, checkRefuel, checkFullInv)
  Logger:trace("Panel:", width, length)
  local turn, otherTurn = leftFirst and turtle.turnLeft or turtle.turnRight, leftFirst and turtle.turnRight or turtle.turnLeft
  for _=2,width do
    checkFullInv()
    line(length, turn, checkRefuel)
    dig(checkRefuel)
    turn()
    turn, otherTurn = otherTurn, turn
  end
  line(length, turn, checkRefuel)
  turn()
end

local function rectPrism(depth, width, length, leftFirst)
  local refuelTarget = width * length * 1.5
  local function checkRefuel()
    refuel(refuelTarget)
  end
  local invEmptyTarget = 3
  local function checkFullInv()
    Logger:debug("Handling full inventory with target:", invEmptyTarget, " handled:", handleFullInv(invEmptyTarget))
  end
  Logger:trace("RectPrism:", depth, width, length)
  for _=1,depth do
    panel(width, length, leftFirst, checkRefuel, checkFullInv)
    for __=1,3 do
      while not turtle.down() do
        turtle.digDown()
        checkRefuel()
      end
    end
    leftFirst = (not not leftFirst) ~= (width % 2 == 0)
  end
end

rectPrism(200, 16, 16)