-- debris.lua
-- Lane-only PC controlled quarry turtle
-- Slot 15 = dimensional chest
-- Slot 16 = fuel
-- Turtles face SOUTH
-- Run on every turtle: debris

local PROTOCOL = "lane_quarry"
local CHEST_SLOT = 15
local FUEL_SLOT = 16

local state = {
    id = os.getComputerLabel() or tostring(os.getComputerID()),
    status = "waiting for PC",
    running = false,
    paused = false,
    length = 500,
    depth = 314,
    y = 0,
    z = 0,
    blocks = 0,
    fuel = 0
}

local function openModem()
    for _, side in ipairs(peripheral.getNames()) do
        if peripheral.getType(side) == "modem" then
            rednet.open(side)
            return
        end
    end
    error("No modem found")
end

local function save()
    local f = fs.open("debris_save.txt", "w")
    f.write(textutils.serialize(state))
    f.close()
end

local function load()
    if fs.exists("debris_save.txt") then
        local f = fs.open("debris_save.txt", "r")
        local data = textutils.unserialize(f.readAll())
        f.close()
        if data then state = data end
    end
end

local function sendStatus()
    state.fuel = turtle.getFuelLevel()
    rednet.broadcast(state, PROTOCOL)
end

local function refuel()
    if turtle.getFuelLevel() == "unlimited" then return end
    if turtle.getFuelLevel() < 500 then
        turtle.select(FUEL_SLOT)
        if turtle.getItemCount(FUEL_SLOT) > 0 then
            turtle.refuel(1)
        end
    end
end

local function waitPaused()
    while state.paused do
        state.status = "paused"
        sendStatus()
        sleep(1)
    end
end

local function digDown()
    while turtle.detectDown() do
        turtle.digDown()
        state.blocks = state.blocks + 1
        sleep(0.05)
    end
end

local function digUp()
    while turtle.detectUp() do
        turtle.digUp()
        state.blocks = state.blocks + 1
        sleep(0.05)
    end
end

local function digForward()
    while turtle.detect() do
        turtle.dig()
        state.blocks = state.blocks + 1
        sleep(0.05)
    end
end

local function moveDown()
    waitPaused()
    refuel()
    digDown()

    while not turtle.down() do
        turtle.attackDown()
        digDown()
        sleep(0.2)
    end

    state.y = state.y - 1
    save()
    sendStatus()
end

local function moveUp()
    waitPaused()
    refuel()
    digUp()

    while not turtle.up() do
        turtle.attackUp()
        digUp()
        sleep(0.2)
    end

    state.y = state.y + 1
    save()
    sendStatus()
end

local function moveForward()
    waitPaused()
    refuel()
    digForward()

    while not turtle.forward() do
        turtle.attack()
        digForward()
        sleep(0.2)
    end

    state.z = state.z + 1
    save()
    sendStatus()
end

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

local function emptyInventory()
    state.status = "emptying"
    sendStatus()

    turtle.select(CHEST_SLOT)

    if turtle.getItemCount(CHEST_SLOT) == 0 then
        state.status = "NO CHEST SLOT 15"
        sendStatus()
        return
    end

    digUp()
    turtle.placeUp()

    for i = 1, 14 do
        turtle.select(i)
        turtle.dropUp()
    end

    turtle.select(CHEST_SLOT)
    turtle.digUp()

    save()
    sendStatus()
end

local function mineColumn()
    state.status = "mining down"
    sendStatus()

    while math.abs(state.y) < state.depth and state.running do
        moveDown()
        if inventoryFull() then emptyInventory() end
    end

    state.status = "mining up"
    sendStatus()

    while state.y < 0 and state.running do
        moveUp()
        if inventoryFull() then emptyInventory() end
    end
end

local function mineLoop()
    while true do
        if state.running and not state.paused then
            while state.z < state.length and state.running do
                mineColumn()

                if state.z < state.length and state.running then
                    state.status = "moving south"
                    sendStatus()
                    moveForward()
                end
            end

            if state.running then
                state.running = false
                state.status = "finished"
                emptyInventory()
                save()
                sendStatus()
            end
        end

        sleep(0.5)
    end
end

local function commandLoop()
    while true do
        local sender, msg = rednet.receive(PROTOCOL)

        if type(msg) == "table" then
            if msg.cmd == "start" then
                state.length = msg.length or 500
                state.depth = msg.depth or 314
                state.running = true
                state.paused = false
                state.status = "started"
                save()
                sendStatus()

            elseif msg.cmd == "pause" then
                state.paused = true
                state.status = "paused"
                save()
                sendStatus()

            elseif msg.cmd == "resume" then
                state.running = true
                state.paused = false
                state.status = "resumed"
                save()
                sendStatus()

            elseif msg.cmd == "stop" then
                state.running = false
                state.paused = false
                state.status = "stopped"
                save()
                sendStatus()

            elseif msg.cmd == "status" then
                sendStatus()

            elseif msg.cmd == "empty" then
                emptyInventory()

            elseif msg.cmd == "reset" then
                state.running = false
                state.paused = false
                state.y = 0
                state.z = 0
                state.blocks = 0
                state.status = "reset"
                save()
                sendStatus()
            end
        end
    end
end

openModem()
load()

state.id = os.getComputerLabel() or tostring(os.getComputerID())
state.running = false
state.paused = false
state.status = "waiting for PC"

save()
sendStatus()

parallel.waitForAny(commandLoop, mineLoop)