local m = peripheral.find "modem"
local o = peripheral.find "monitor"

o.setTextScale(0.5)
 
local w, h = o.getSize()
local command_window = window.create(o, 1, h, w, 1)
local outputs_window = window.create(o, 1, 1, w, h - 1)

local function exec_in_window(w, f)
	local x, y = o.getCursorPos()
	local last = term.redirect(w)
	f()
	term.redirect(last)
	w.redraw()
	o.setCursorPos(x, y)
end
 
local function print_output(txt, color)
	exec_in_window(outputs_window, function()
		term.setTextColor(color or colors.white)
		print(txt)
	end)
end

local function splitspace(str)
    local tokens = {}
    for token in string.gmatch(str, "[^%s]+") do
        table.insert(tokens, token)
    end
    return tokens
end

local function controls()
	term.redirect(command_window)
	term.setBackgroundColor(colors.white)
	term.setTextColor(colors.black)
	term.clear()
	local hist = {}
	while true do
		write "> "
		local msg = read(nil, hist)
		table.insert(hist, msg)
		local tokens = splitspace(msg)
		local command = table.remove(tokens, 1)
		if command == "open" then
			local chan = tonumber(tokens[1])
			m.open(chan)
			print_output(("Opened %d"):format(chan), colors.gray)
		elseif command == "close" then
			local chan = tonumber(tokens[1])
			m.close(chan)
			print_output(("Closed %d"):format(chan), colors.gray)
		else
			print_output("Command invalid", colors.gray)
		end
	end
end

local function compact_serialize(x)
    local t = type(x)
    if t == "number" then
        return tostring(x)
    elseif t == "string" then
        return textutils.serialise(x)
    elseif t == "table" then
        local out = "{ "
        for k, v in pairs(x) do
            out = out .. string.format("[%s]=%s, ", compact_serialize(k), compact_serialize(v))
        end
        return out .. "}"
    elseif t == "boolean" then
        return tostring(x)
    else
        error("Unsupported type " .. t)
    end
end
 
local function safe_serialize(m)
    local ok, res = pcall(compact_serialize, m)
    if ok then return res
    else return ("[UNSERIALIZABLE %s: %s]"):format(tostring(m), res) end
end

local function tostring_with_default(x)
    if not x then return "[UNKNOWN]"
    else return tostring(x) end
end
 
local function receive()
	while true do
		local _, _, channel, reply_channel, message, distance = os.pullEvent "modem_message"
		print_output(("%d \16 %d | %s"):format(channel, reply_channel, tostring_with_default(distance)))
		print_output(safe_serialize(message), colors.lightGray)
	end
end
 
parallel.waitForAny(controls, receive)