-- Salt's ComputerCraft Storage Network Script
--
-- For information on this script, physical in-world setup, and configuration,
-- see: https://git.desu.ltd/salt/mc-scripts/src/branch/master/storage-net

-- Default configuration values. Override in config.lua. DO NOT CHANGE HERE
mode                = "ConfigureMe"     -- The mode of function for this node
networkid           = 0                 -- Unique ID for this network
port_broadcast      = 42914             -- Port for M->S traffic
port_return         = 42915             -- Port for S->M traffic
packet_magic        = "ccstoragenet"    -- Just a random string

-- This loads config.lua. See masterconfig.lua and slaveconfig.lua for example
-- configurations.
require "config"

-- Startup diagnostics
print("Salt's CC Storage Net")
print("Computer " .. os.getComputerID() .. " configured as " .. mode)

-- Common globals
modem = peripheral.find("modem") or error("No modem attached", 0)
modem_side = peripheral.getName(modem)
-- Master globals
m_slaves = {}
-- Slave globals

-- Common functions
function c_mainLoop(loopfunc)
    -- Loops a thing forever
    while true do
        loopfunc()
    end
end
function c_waitForMessage()
    -- Waits for a message on the modem, timing out after 1 second
    local sender, message = rednet.receive(packet_magic, 1)
    if message then
        if
            (not message["type"]) or                                            -- Message type is required
            (message["sourcetype"] == mode) or                                  -- Ignore packets from our class of machines
            (message["networkid"] ~= networkid) or                              -- Ignore packets from other networks
            (message["targetid"] and message["targetid"] ~= os.getComputerID)   -- Ignore packets for other machines
        then
            return nil
        end
        return message
    end
    return nil
end
function c_sendMessage(message)
    -- Sends a message, either to the master or all slaves depending on mode
    local msg = {
        networkid       = networkid,
        sourceid        = os.getComputerID(),
        sourcetype      = mode,
    }
    -- Override basic message object with args
    for k,v in pairs(message) do
        msg[k] = v
    end
    --print("Transmitting message: " .. textutils.serialize(msg))
    rednet.broadcast(msg, packet_magic)
end

-- Master functions
function m_loop()
    -- The main loop of the master server
    -- Listen for packets on the return net
    local msg = c_waitForMessage()
    if not msg then
        return
    end
    if msg["type"] == "pong" then
        local source = msg["sourceid"]
        print("Received pong from slave: " .. source)
        m_slaves[source] = source
    end
end
function m_ping()
    -- Clear out the list of all slaves and send out a fresh ping
    -- Ping information isn't used for much, so delay in clearing cache and
    -- repopulating the data isn't a big deal.
    m_slaves = {}
    c_sendMessage({type="ping"})
end

-- Slave functions
function s_loop()
    -- The main loop of any slave nodes
    -- Listen for packets from the master
    local msg = c_waitForMessage()
    if not msg then
        return
    end
    if (msg["type"] == "ping") then
        -- Respond to pings with pongs
        print("Received ping from master: " .. msg["sourceid"])
        c_sendMessage({type="pong"})
    else
        print("Unknown message: " .. textutils.serialize(msg))
    end
end

-- Application entrypoint
function main ()
    if (mode == "master") then
        print("Beginning initialization as master...")
        rednet.open(modem_side)

        print("Pinging for slaves...")
        m_ping()

        print("Entering main loop")
        c_mainLoop(m_loop)
    elseif (mode == "slave") then
        print("Beginning initialization as slave...")
        rednet.open(modem_side)

        print("Entering main loop")
        c_mainLoop(s_loop)
    else
        error("Invalid mode: " .. mode .. ", please configure this node appropriately", 0)
    end
end

main()