maxCompareSlot = 0   -- all slots up to this one are used for comparing, initialized in initCompareSlots
currentCompareSlot = 1  -- current compare slot (does not have to be saved)

startupWait = 5 -- time in seconds to wait after reading state before resuming

xRelPos = 0 -- "positive = forward"
yRelPos = 0 -- "positive = right"
zRelPos = 0 -- "positive = upwards"

facing = {
    x = 1,
    y = 0
}

holeCounter = {
    line = 0,
    hole = 0
}

function printPos()
    print("At (", xRelPos, ", ", yRelPos, ", ", zRelPos, ") facing (", facing.x, ", ", facing.y, ")")
end

function startupTimer()
    print("Waiting ", startupWait, "s before starting... ")
    sleep(startupWait)
    print("Starting!")
end

function setupResume()
    if fs.exists("startup") then
        return
    end
    local file = fs.open("startup", "w") --Startup File
    file.writeLine( --The below is on the left because spacing
[[
print("Resuming Miner")
local event
if fs.exists("state") then
    os.run({},"]]..shell.getRunningProgram()..[[")
else
    print("Never mind, no save file found")
    fs.delete("startup")
end
]])
    file.close()
end

-- get all lines from a file, returns an empty 
-- list/table if the file does not exist
function lines_from(file)
    local lines = {}
    for line in io.lines(file) do 
        lines[#lines + 1] = line
    end
    return lines
end

function readState()
    if not fs.exists("state") then
        initCompareSlots()
        return
    end
    print("Reading state after restart")
    local file = fs.open("state", "r")

    xRelPos = tonumber(file.readLine())
    yRelPos = tonumber(file.readLine())
    zRelPos = tonumber(file.readLine())
    facing.x = tonumber(file.readLine())
    facing.y = tonumber(file.readLine())
    holeCounter.line = tonumber(file.readLine())
    holeCounter.hole = tonumber(file.readLine())
    maxCompareSlot = tonumber(file.readLine())

    file.close()
    print("Read state:")
    printPos()

    startupTimer()
end

function writeState()
    if fs.exists("state") then
        fs.delete("state")
    end
    file = fs.open("state", "w")

    file.writeLine(tostring(xRelPos))
    file.writeLine(tostring(yRelPos))
    file.writeLine(tostring(zRelPos))
    file.writeLine(tostring(facing.x))
    file.writeLine(tostring(facing.y))
    file.writeLine(tostring(holeCounter.line))
    file.writeLine(tostring(holeCounter.hole))
    file.writeLine(tostring(maxCompareSlot))

    file.close()
end

-- helper functions for movement
function forward()
    xRelPos = xRelPos + facing.x
    yRelPos = yRelPos + facing.y
    writeState()

    if turtle.forward() then
        return true
    end
    
    xRelPos = xRelPos - facing.x
    yRelPos = yRelPos - facing.y
    writeState()

    turtle.dig()
    turtle.attack()
    return false
end

function upwards()
    zRelPos = zRelPos + 1
    writeState()
    if turtle.up() then
        return true
    end
    zRelPos = zRelPos - 1
    writeState()
    turtle.digUp()
    turtle.attackUp()
    return false
end

function downwards()
    zRelPos = zRelPos - 1
    writeState()
    if turtle.down() then
        return true
    end
    zRelPos = zRelPos + 1
    writeState()
    turtle.digDown()
    turtle.attackDown()
    return false
end

function turnRight()
    if facing.x == 1 and facing.y == 0 then -- facing forward
        facing.x = 0
        facing.y = 1
    elseif facing.x == 0 and facing.y == 1 then -- facing right
        facing.x = -1
        facing.y = 0
    elseif facing.x == -1 and facing.y == 0 then -- facing backward
        facing.x = 0
        facing.y = -1
    elseif facing.x == 0 and facing.y == -1 then -- facing left
        facing.x = 1
        facing.y = 0
    else 
        print("turnRight: This should never happen")
    end
    writeState()
    turtle.turnRight()
end

function turnLeft()
    if facing.x == 1 and facing.y == 0 then -- facing forward
        facing.x = 0
        facing.y = -1
    elseif facing.x == 0 and facing.y == 1 then -- facing right
        facing.x = 1
        facing.y = 0
    elseif facing.x == -1 and facing.y == 0 then -- facing backward
        facing.x = 0
        facing.y = 1
    elseif facing.x == 0 and facing.y == -1 then -- facing left
        facing.x = -1
        facing.y = 0
    else 
        print("turnLeft: This should never happen")
    end
    writeState()
    turtle.turnLeft()
end

function faceTo(x, y)
    if (x == facing.x) and (y == facing.y) then
        return
    end
    if (x == 1 and facing.x == -1) or (x == -1 and facing.x == 1) or (y == 1 and facing.y == -1) or (y == -1 and facing.y == 1) then
        turnRight()
        turnRight()
    elseif (facing.y == -1 and x == 1) or (facing.y == 1 and x == -1) or (facing.x == 1 and y == 1) or (facing.x == -1 and y == -1) then
        turnRight()
    elseif (facing.y == -1 and x == -1) or (facing.y == 1 and x == 1) or (facing.x == 1 and y == -1) or (facing.x == -1 and y == 1) then
        turnLeft()
    else
        print("faceTo: This should never happen")
    end
end

function moveTo(x, y)
    if (yRelPos > y) then
        faceTo(0, -1)
        while yRelPos > y do
            forward()
        end
    elseif (yRelPos < y) then
        faceTo(0, 1)
        while yRelPos < y do
            forward()
        end
    end

    if (xRelPos > x) then
        faceTo(-1, 0)
        while xRelPos > x do
            forward()
        end
    elseif (xRelPos < x) then
        faceTo(1, 0)
        while xRelPos < x do
            forward()
        end
    end
end

-- should only be called once when manually starting, not when restarting
function initCompareSlots()
    print("Setting up compare slots...")
    
    while true do
        if (turtle.getItemCount(maxCompareSlot + 1) == 0) then
            break
        end
        maxCompareSlot = maxCompareSlot + 1
    end

    print("Found ", maxCompareSlot, " compare slots!")
end

-- checks slots 1..maxCompareSlot against currently looked at block
-- make sure that the selected slot == currentCompareSlot at all times
function blockIsWorth()
    if (maxCompareSlot <= 0) then
        -- special case
        return true
    end

    lastCompareSlot = currentCompareSlot
    while true do
        if turtle.compare() then
            return false
        end

        -- cycle currentCompareSlot, fmod would work perfectly if indices started at 0 -_-
        nextCompareSlot = currentCompareSlot + 1
        if nextCompareSlot > maxCompareSlot then
            nextCompareSlot = 1
        end

        if (nextCompareSlot == lastCompareSlot) then
            break
        end

        currentCompareSlot = nextCompareSlot
        turtle.select(currentCompareSlot)
    end

    return true
end

function checkForItems()
    dir = math.fmod(zRelPos, 2)

    -- alternatingly check relative facing (left, forward, right) and (right, forward, left) blocks, so at the end we can face backwards when going up and check without turning at all
    -- -> two turns per mined level
    if (dir == 0) then
        faceTo(0, -1)   -- just to be safe, but this should never really do anything except for the first level
        if blockIsWorth() then
            turtle.dig()
        end
        turnRight()
        if blockIsWorth() then
            turtle.dig()
        end
        turnRight()
        if blockIsWorth() then
            turtle.dig()
        end
    else
        faceTo(0, 1)
        if blockIsWorth() then
            turtle.dig()
        end
        turnLeft()
        if blockIsWorth() then
            turtle.dig()
        end
        turnLeft()
        if blockIsWorth() then
            turtle.dig()
        end
    end
end

-- Digs straight down right where it is, taking resources with it
function digHole()
    print("Digging Hole (", holeCounter.line, ", ", holeCounter.hole, ")")

    -- go down
    turtle.digDown()
    while downwards() do
        checkForItems()
        turtle.digDown()
    end

    -- go back up
    -- look at the remaining direction (-1, 0) and check blocks
    faceTo(-1, 0)
    while zRelPos < 0 do
        if blockIsWorth() then
            turtle.dig()
        end
        upwards()
    end
end

function shouldReturnItems()
    if turtle.getItemCount(11) == 0 then
        return false
    end
    return true
end

-- moves to the chest and deposits items, but only when deemed necessary by shouldReturnItems
function returnItems()
    if not shouldReturnItems() then
        return
    end
    
    -- chest coordinates are -1, 0, so go to 0, 0 facing "backwards"
    moveTo(0, 0)

    faceTo(-1, 0)

    local droppedAll = true

    while true do
        droppedAll = true
        -- already facing right direction, put items
        for slot = 1, maxCompareSlot do
            -- put away compare slots' items except one
            turtle.select(slot)
            turtle.drop(turtle.getItemCount(slot) - 1)
            if (turtle.getItemCount(slot) ~= 1) then
                droppedAll = false
            end
        end
        for slot = maxCompareSlot + 1, 16 do
            -- then drop everything else
            turtle.select(slot)
            turtle.drop()
            if (turtle.getItemCount(slot) ~= 0) then
                droppedAll = false
            end
        end
        if (droppedAll) then
            break
        end
    end

    -- necessary so the next mined block doesn't go in random places
    turtle.select(currentCompareSlot)
end

function moveToNext()
    if not (zRelPos == 0) then
        return  -- we're resuming, don't move, don't increase hole
    end

    print("Moving to the next hole")

    xPos = 1
    yPos = 1

    -- find out where the next hole should be
    for i = 1, holeCounter.line do
        if math.fmod(i, 2) == 0 then
            xPos = xPos + 2
            yPos = 1
        else
            xPos = xPos + 3
            yPos = 0
        end
    end
    
    for i = 1, holeCounter.hole do
        xPos = xPos - 1
        yPos = yPos + 2
    end

    moveTo(xPos, yPos)

    -- update holeCounter
    -- if the xPos we calculated is 1 that means we need to start the next line
    if xPos == 0 then
        holeCounter.line = holeCounter.line + 1
        holeCounter.hole = 0
    else
        holeCounter.hole = holeCounter.hole + 1
    end

end

setupResume()
readState()

while true do
    moveToNext()
    digHole()
    print("Returning to chest")
    returnItems()
end