local modemSide = "left"
local chestSlot = 15
local fuelSlot = 16
local minFuel = 500
local saveFile = "quarry_save"

local originX = 30938
local originY = 251
local originZ = -30535
local bottomY = -55

local x, z = 0, 0
local dir = 0
local order = 1
local index = 0
local blocks = 0
local worldX, worldY, worldZ = originX, originY, originZ

if not rednet.isOpen(modemSide) then
  rednet.open(modemSide)
end

local function areaSize()
  return 2 ^ order
end

local function save()
  local f = fs.open(saveFile, "w")
  f.write(textutils.serialize({
    x=x,z=z,dir=dir,order=order,index=index,blocks=blocks,
    worldX=worldX,worldY=worldY,worldZ=worldZ
  }))
  f.close()
end

local function loadSave()
  if fs.exists(saveFile) then
    local f = fs.open(saveFile, "r")
    local d = textutils.unserialize(f.readAll())
    f.close()
    if d then
      x=d.x or 0
      z=d.z or 0
      dir=d.dir or 0
      order=d.order or 1
      index=d.index or 0
      blocks=d.blocks or 0
      worldX=d.worldX or originX
      worldY=d.worldY or originY
      worldZ=d.worldZ or originZ
    end
  end
end

local function report(status)
  rednet.broadcast(
    "STAT:"..status..
    ":BLOCKS:"..blocks..
    ":FUEL:"..tostring(turtle.getFuelLevel())..
    ":ORDER:"..order..
    ":SIZE:"..areaSize()..
    ":INDEX:"..index..
    ":WX:"..worldX..
    ":WY:"..worldY..
    ":WZ:"..worldZ
  )
end

local function fuel()
  local f = turtle.getFuelLevel()
  if not (f == "unlimited") then
    if f < minFuel then
      turtle.select(fuelSlot)
      turtle.refuel(1)
    end
  end
end

local function dig()
  if turtle.dig() then blocks = blocks + 1 end
end

local function digUp()
  if turtle.digUp() then blocks = blocks + 1 end
end

local function digDown()
  if turtle.digDown() then blocks = blocks + 1 end
end

local function invFull()
  for i = 1, 14 do
    if turtle.getItemCount(i) == 0 then return false end
  end
  return true
end

local function unload()
  turtle.select(chestSlot)
  if turtle.placeDown() then
    for i = 1, 14 do
      turtle.select(i)
      turtle.dropDown()
    end
    turtle.select(chestSlot)
    turtle.digDown()
  end
  turtle.select(1)
end

local function turnRight()
  turtle.turnRight()
  dir = (dir + 1) % 4
  save()
end

local function face(d)
  while not (dir == d) do
    turnRight()
  end
end

local function forward()
  fuel()

  while turtle.detect() do
    dig()
    sleep(0.05)
  end

  if turtle.forward() then
    if dir == 0 then
      z = z + 1
      worldZ = worldZ + 1
    elseif dir == 1 then
      x = x + 1
      worldX = worldX + 1
    elseif dir == 2 then
      z = z - 1
      worldZ = worldZ - 1
    elseif dir == 3 then
      x = x - 1
      worldX = worldX - 1
    end
    save()
  end

  if turtle.detectUp() then digUp() end
  if turtle.detectDown() then digDown() end
  if invFull() then unload() end
end

local function up()
  fuel()

  while turtle.detectUp() do
    digUp()
    sleep(0.05)
  end

  if turtle.up() then
    worldY = worldY + 1
    save()
    return true
  end

  return false
end

local function down()
  fuel()

  while turtle.detectDown() do
    digDown()
    sleep(0.05)
  end

  if turtle.down() then
    worldY = worldY - 1
    save()
    return true
  end

  return false
end

local function goTo(tx, tz)
  if z < tz then face(0) while z < tz do forward() end end
  if z > tz then face(2) while z > tz do forward() end end
  if x < tx then face(1) while x < tx do forward() end end
  if x > tx then face(3) while x > tx do forward() end end
end

local function rot(n, hx, hz, rx, rz)
  if rz == 0 then
    if rx == 1 then
      hx = n - 1 - hx
      hz = n - 1 - hz
    end

    local temp = hx
    hx = hz
    hz = temp
  end

  return hx, hz
end

local function hilbert(i)
  local n = areaSize()
  local hx, hz = 0, 0
  local t = i
  local s = 1

  while s < n do
    local rx = math.floor(t / 2) % 2
    local bit = t % 2
    local rz = 0

    if not (bit == rx) then
      rz = 1
    end

    hx, hz = rot(s, hx, hz, rx, rz)
    hx = hx + s * rx
    hz = hz + s * rz

    t = math.floor(t / 4)
    s = s * 2
  end

  return -hx, -hz
end

local function mineShaft()
  report("ShaftDown")

  while worldY > bottomY do
    if not down() then
      break
    end

    if invFull() then unload() end
  end

  report("ShaftUp")

  while worldY < originY do
    if not up() then
      break
    end

    if invFull() then unload() end
  end
end

loadSave()
report("Loaded")

while true do
  report("Mining")

  while index < areaSize() * areaSize() do
    local tx, tz = hilbert(index)

    goTo(tx, tz)

    mineShaft()

    index = index + 1
    save()
    report("Next")
  end

  report("VolumeDone")

  goTo(0, 0)
  face(0)

  order = order + 1
  index = 0

  save()
  report("Expanded")
end