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

facing = {
    x = 1,
    y = 0
}

cells_drop = 0
slot_cells_min = 0
slot_cells_max = 0
slot_cells_cur = 0

input_drop = 0
slot_input_min = 0
slot_input_max = 0
slot_input_cur = 0

output_1_take = 0
slot_output_1_min = 0
slot_output_1_max = 0
slot_output_1_cur = 0

output_2_take = 0
slot_output_2_min = 0
slot_output_2_max = 0
slot_output_2_cur = 0

output_3_take = 0
slot_output_3_min = 0
slot_output_3_max = 0
slot_output_3_cur = 0

output_4_take = 0
slot_output_4_min = 0
slot_output_4_max = 0
slot_output_4_cur = 0

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

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

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

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

function forceForward()
    -- try moving forward until it works
    while not forward() do end
end

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

function forceUpwards()
    -- try moving upwards until it works
    while not upwards() do end
end

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

function forceDownwards()
    -- try moving upwards until it works
    while not downwards() do end
end

function forcePlace()
    -- try placing until it works
    while not turtle.place() do 
        turtle.dig()
        turtle.attack()
    end
end

function forcePlaceDown()
    -- try placing until it works
    while not turtle.placeDown() do 
        turtle.digDown()
        turtle.attackDown()
    end
end

function forcePlaceUp()
    -- try placing until it works
    while not turtle.placeUp() do 
        turtle.digUp()
        turtle.attackUp()
    end
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
    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
    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

function resupplyItem(slot, num)
    turtle.select(slot)
    while (turtle.getItemCount(slot) < num) do
        turtle.suck(num - turtle.getItemCount(slot))
    end
end

function resupplyAll()
    -- reset current slot numbers
    slot_output_1_cur = slot_output_1_min
    slot_output_2_cur = slot_output_2_min
    slot_output_3_cur = slot_output_3_min
    slot_output_4_cur = slot_output_4_min

    -- clean entire inventory
    moveTo(0, 2); faceTo(-1, 0)
    for i = 1, 16 do
        while turtle.getItemCount(i) > 0 do
            turtle.select(i)
            turtle.drop()
        end
    end

    -- get ingredient
    moveTo(0, 1); faceTo(-1, 0)
    slot_input_cur = slot_input_min
    for i = slot_input_min, slot_input_max do
        if (turtle.getItemCount(i) < 64) then
            resupplyItem(i, 64)
        end
    end

    -- get cells
    moveTo(0, 0); faceTo(-1, 0)
    slot_cells_cur = slot_cells_min
    for i = slot_cells_min, slot_cells_max do
        if (turtle.getItemCount(i) < 64) then
            resupplyItem(i, 64)
        end
    end
end

function supplyCentrifuge()
    module = peripheral.wrap("right")

    -- drop cells from below (side 0)
    turtle.select(slot_cells_cur)
    drop = cells_drop
    if (turtle.getItemCount(slot_cells_cur) < drop) then
        drop = drop - turtle.getItemCount(slot_cells_cur)
        module.dropSneakyUp(0)
        slot_cells_cur = slot_cells_cur + 1
        turtle.select(slot_cells_cur)
    end   
    module.dropSneakyUp(0, drop)

    -- drop input from above (side 1)
    turtle.select(slot_input_cur)
    drop = input_drop
    if (turtle.getItemCount(slot_input_cur) < drop) then
        drop = drop - turtle.getItemCount(slot_input_cur)
        module.dropSneakyUp(1)
        slot_input_cur = slot_input_cur + 1
        turtle.select(slot_input_cur)
    end
    module.dropSneakyUp(1, drop)
end

function emptyCentrifuge()
    module = peripheral.wrap("right")

    -- get output 1
    turtle.select(slot_output_1_cur)
    take = output_1_take
    if (turtle.getItemCount(slot_output_1_cur) + take > 64) then
        take_now = 64 - turtle.getItemCount(slot_output_1_cur)
        take = take - take_now
        module.suckSneakyUp(2, take_now)
        slot_output_1_cur = slot_output_1_cur + 1
        turtle.select(slot_output_1_cur)
    end
    -- if no input is ready, move on immediately
    if module.suckSneakyUp(2, take) == false then
        return
    end

    -- get output 2
    turtle.select(slot_output_2_cur)
    take = output_2_take
    if (turtle.getItemCount(slot_output_2_cur) + take > 64) then
        take_now = 64 - turtle.getItemCount(slot_output_2_cur)
        take = take - take_now
        module.suckSneakyUp(2, take_now)
        slot_output_2_cur = slot_output_2_cur + 1
        turtle.select(slot_output_2_cur)
    end
    module.suckSneakyUp(2, take)

    -- get output 3
    turtle.select(slot_output_3_cur)
    take = output_3_take
    if (turtle.getItemCount(slot_output_3_cur) + take > 64) then
        take_now = 64 - turtle.getItemCount(slot_output_3_cur)
        take = take - take_now
        module.suckSneakyUp(2, take_now)
        slot_output_3_cur = slot_output_3_cur + 1
        turtle.select(slot_output_3_cur)
    end
    module.suckSneakyUp(2, take)

    -- get output 4
    turtle.select(slot_output_4_cur)
    take = output_4_take
    if (turtle.getItemCount(slot_output_4_cur) + take > 64) then
        take_now = 64 - turtle.getItemCount(slot_output_4_cur)
        take = take - take_now
        module.suckSneakyUp(2, take_now)
        slot_output_4_cur = slot_output_4_cur + 1
        turtle.select(slot_output_4_cur)
    end
    module.suckSneakyUp(2, take)
end

-- sets up slot numbers with <cells> slots for cells and <input> slots for input ingredient, and output 1 through 4 numbers of slots
function setupSlots(cells, input, output_1, output_2, output_3, output_4)
    slot_cells_min = 1
    slot_cells_max = cells
    slot_cells_cur = slot_cells_min
    cells_drop = cells

    slot_input_min = slot_cells_max + 1
    slot_input_max = slot_input_min + input - 1
    slot_input_cur = slot_input_min
    input_drop = input

    slot_output_1_min = 1
    slot_output_1_max = output_1
    slot_output_1_cur = slot_output_1_min
    output_1_take = output_1

    slot_output_2_min = slot_output_1_max + 1
    slot_output_2_max = slot_output_2_min + output_2 - 1
    slot_output_2_cur = slot_output_2_min
    output_2_take = output_2

    slot_output_3_min = slot_output_2_max + 1
    slot_output_3_max = slot_output_3_min + output_3 - 1
    slot_output_3_cur = slot_output_3_min
    output_3_take = output_3

    slot_output_4_min = slot_output_3_max + 1
    slot_output_4_max = slot_output_4_min + output_4 - 1
    slot_output_4_cur = slot_output_4_min
    output_4_take = output_4
end

function enterSupplyLoop()
    while true do
        print("Resupplying...")
        resupplyAll()

        -- go to first centrifuge and wait to start
        moveTo(1, 0)
        turtle.select(16)
        while turtle.suckUp() do
            putBack = turtle.getItemCount(16)
            turtle.dropUp(putBack)
            sleep(5)
        end

        print("Putting cells and ingredient in the centrifuges...")
        -- putting in ingredients on the way there
        for i = 1, 7, 2 do
            for j = 0, 7, 1 do -- moving right
                moveTo(i, j)
                supplyCentrifuge()
            end
            for j = 7, 0, -1 do -- moving left
                moveTo(i + 1, j)
                supplyCentrifuge()
            end
        end

        sleep(5)
        -- wait a little so all centrifuges are ready for having their outputs taken

        print("Getting outputs from the centrifuges...")
        -- getting outputs out on the way back
        for i = 7, 1, -2 do
            for j = 0, 7, 1 do -- moving right
                moveTo(i + 1, j)
                emptyCentrifuge()
            end
            for j = 7, 0, -1 do -- moving left
                moveTo(i, j)
                emptyCentrifuge()
            end
        end
    end
end

function redstoneLoop()
    print("Supplying Redstone Centrifuges")
    setupSlots(4, 10, 1, 5, 1, 3) -- 4 slots cells, 10 slots redstone
    print("Starting in 5s...")
    sleep(5)

    enterSupplyLoop()
end

print("Centrifuges even item distribution program")
redstoneLoop()