module("lua_ostasks", package.seeall)

local config_regexp = ("([^\n])\n"):rep(5):sub(1, -2)
local dir_sep, path_sep, path_mark, execdir, igmark = package.config:match(config_regexp)

if dir_sep == "\\" then
	platform = "windows"
else
	platform = "posix"
end

local os_dependent = platform == "windows" and require "lua_ostasks.windows" or require "lua_ostasks.posix"

local task_set_mt = {
	__index = {
	},
	__metatable = false
}

local function make_task_filename(task_set_id)
	local tmp = platform == "windows" and os.getenv("TMP") or "/tmp"
	return ("%s%s%s"):format(tmp, dir_sep, task_set_id)
end

---
-- Crea un nuevo conjunto de tareas
function new (task_set_id)
	local newTaskSet = setmetatable({}, task_set_mt)
	
	newTaskSet.id = task_set_id
	newTaskSet._tasks = {}
	newTaskSet._task_filename = make_task_filename(task_set_id)
	newTaskSet._task_file = io.open(newTaskSet._task_filename, "w+")
	newTaskSet._task_file:close() -- erase it
	newTaskSet._task_file = nil
	
	return newTaskSet
end

---
-- Mata un conjunto de tareas
function kill (task_set_id)
	local filename = make_task_filename(task_set_id)
	local f, err = io.open(filename, "r")
	if not f then
		-- nothing to be done
		return
	end
	
	for line in f:lines() do
		local pid = tonumber(line)
		if pid then
			os_dependent.kill(pid)
		end
	end
	
	f:close()
	os_dependent.rmfile(filename)
end

---
-- Inicia un nuevo proceso y lo agrega al conjunto de tareas
-- task puede ser el nombre de un ejecutable, la ruta completa al mismo, o tambien una tabla con campos
--   - task (obligatorio): idem anterior
--   - working_directory (opcional): el directorio actual para el nuevo proceso
-- Retorna el pid de la tarea creada
function task_set_mt.__index:start (task, ...)
	if type(task) == "string" then
		task = {
			task = task
		}
	elseif type(task) ~= "table" then
		error("task id must be a string or a table")
	end
	
	local pid = os_dependent.start(task, ...)
	self._tasks[pid] = pid
	
	self._task_file = io.open(self._task_filename, "a")
	self._task_file:write(pid.."\n")
	self._task_file:close()
	self._task_file = nil
	
	return pid
end

---
-- Mata una tarea individual.
-- Una vez que hace eso, la borra del conjunto de tareas
function task_set_mt.__index:kill (pid)
	-- No matamos tareas que no hayamos creado
	if not self._tasks[pid] then return false end
	
	local ok, err = os_dependent.kill(pid)
	if not ok then
		io.stderr:write( ("fuckings!!! error killing pid %d : %s"):format(pid, err) )
		return false
	end
	
	self._tasks[pid] = nil
	
	self._task_file = io.open(self._task_filename, "w+")
	for _, pid in pairs(self._tasks) do
		self._task_file:write(pid.."\n")
	end
	self._task_file:close()
	self._task_file = nil
	
	return true
end

---
-- Mata todas las tareas que existen en el conjunto de tareas.
-- una vez que hace eso, borra el archivo con la lista de tareas
function task_set_mt.__index:killall ()
	
	for k,pid in pairs(self._tasks) do
		local ok, err = os_dependent.kill(pid)
		if not ok then
			io.stderr:write( ("fuckings!!! error killing pid %d : %s"):format(pid, err) )
			return
		end
	end
	
	if self._task_file then
		self._task_file:close()
		self._task_file = nil
	end
	os_dependent.rmfile(self._task_filename)
end
