#! /usr/bin/env lua

local p = require("posix")
local port = 54321

local short = "hp:"
local long = {
	{"help", "none", 'h'},
	{"port", "required", 'p'},
}

local rules = {}

for opt, optarg, optind, longind in p.getopt(arg, short, long) do
	if opt == '?' then
		print("Unrecognized option")
		os.exit(1)
	elseif opt == 'h' then
		print("Usage: ruld [-h|-p PORT]")
		os.exit(0)
	elseif opt == 'p' then
		port = optarg
	end
end

function eval(exp, env)
	local f
	local value = "return " .. exp
	if env then
		f = load(value, nil, nil, env)
	else
		f = load(value)
	end
	return f and f()
end

function test(env)
	local rsp = ""
	for index = #rules, 1, -1 do
		local entry = rules[index]
		if eval(entry[1], env) then
			local delay, duration
			if entry[3] then
				delay = entry[3].delay
				duration = entry[3].duration
				if delay and delay > 0 then
					entry[3].delay = delay - 1
				end
				if ((not delay) or delay == 0) and duration and duration > 0 then
					entry[3].duration = duration - 1
				end
			end
			if (((not delay) or delay == 0) and ((not duration) or duration > 0)) then
				if #rsp > 0 then
					rsp = rsp .. " " .. entry[2]
				else
					rsp = entry[2]
				end
			end
			if duration and duration <= 1 then
				table.remove(rules, index)
			end
		end
	end
	return rsp
end

local fd = p.socket(p.AF_INET, p.SOCK_STREAM, 0)
p.setsockopt(fd, p.SOL_SOCKET, p.SO_REUSEADDR, 1)
p.bind(fd, {family=p.AF_INET, addr="127.0.0.1", port=port})
p.listen(fd, p.SOMAXCONN)
local running = true

while running do
	ret_fd = p.accept(fd)
	if ret_fd then
		local msg = p.recv(ret_fd, p.BUFSIZ)
		if msg then
			local cmd, data = nil
			sep = msg:find(" ")
			if sep then
				cmd = msg:sub(1, sep - 1)
				data = msg:sub(sep + 1)
			else
				cmd = msg
			end
			if cmd == "test" then
				local env = eval(data)
				if env then
					rsp = test(env)
					p.send(ret_fd, rsp)
				end
			elseif cmd == "add" then
				local value = eval(data)
				if value then
					table.insert(rules, value)
				end
			elseif cmd == "remove" then
				if data == "tail" then
					table.remove(rules, #rules)
				elseif data == "head" then
					table.remove(rules, 1)
				else
					for index = #rules, 1, -1 do
						if rules[index][1]:find(data) then
							table.remove(rules, index)
						end
					end
				end
			elseif cmd == "quit" then
				running = false
			elseif cmd == "list" then
				local rsp = ""
				for index, entry in pairs(rules) do
					rsp = rsp .. string.format("%s => %s", entry[1], entry[2])
					if entry[3] then
						if entry[3].delay then
							rsp = rsp .. string.format(" @%i", entry[3].delay)
						end
						if entry[3].duration then
							rsp = rsp .. string.format(" +%i", entry[3].duration)
						end
					end
					if index < #rules then
						rsp = rsp .. "\n"
					end
				end
				p.send(ret_fd, rsp)
			end
		end
		p.close(ret_fd)
	end
end

p.close(fd)
