local string = string
local table = table
local pairs = pairs
local type = type
local unpack = unpack
local process = process
local assert = assert
local tostring = tostring
local select = select

local _NAME = ...
local _ENV = {}
package.loaded[_NAME] = _ENV
-- no more external access after this point
setfenv(1, _ENV)

--
-- Checks if 'key' is not one of the following values:
function IsExcludedKey(key)
	key = key:lower()
	if key == "user_id" or key == "user_name" or key == "display_name" or key == "vcc" or key == "user_type" then
		return true
	end
	return false
end

-- helper function to clone tables
function Clone (t)
	assert(type(t) == "table")
	
	local n = {}
	
	for k,v in pairs(t) do
		if type(k) == "table" then
			k = Clone(k)
		end
		if type(v) == "table" then
			v = Clone(v)
		end
		n[k] = v
	end
	
	return n
end

---
-- helper function to compute the union of two tables
-- modifies t1 in place
--
function Union (t1, t2)
	assert(type(t) == "table" and type(t2) == "table")
	
	for k,v in pairs(t2) do
		t1[k] = v
	end

	return t1
end

--
--
function __ImlToLua(iml)
	local t = {}
	
	local mark1 = string.char(162) .. string.char(220)	-- "¢Ü"
	local mark2 = string.char(165) .. string.char(163)	-- "¥£"
	
	local sep = iml:sub(1, 1)
	local subsep = iml:sub(2, 2)
	iml = iml:sub(3)
	local pattern = ([[([^%s]*)%s([^%s]*)%s]]):format(subsep, subsep, sep, sep)
	--print(pattern)
	for key, value in iml:gmatch(pattern) do
		-- desagradable!
		--console.log("%s", key)
		--console.log("%s", value)
		local signature = value:sub(1,2)
		if signature == mark1 or signature == mark2 then
			t[key] = ImlToLua(value)
		else
			t[key] = value
		end
	end
	
	return t
end

--
--
function __LuaToIml(t)
	-- "¢Ü"
	local sep = string.char(162)
	local subsep = string.char(220)
	local o = {sep .. subsep}
	for k,v in pairs(t) do
		o[#o + 1] = k
		o[#o + 1] = subsep
		o[#o + 1] = v
		o[#o + 1] = sep
	end
	return table.concat(o)
end

--
--
function CountKeys (table)
	local count = 0
	for k in pairs(table or {}) do
		count = count + 1
	end
	return count
end

---
--
function TryCall (f, ...)
	if type(f) == "function" then
		return f(...)
	end
end

---
--
function TryAsyncCall (f, ...)
	if type(f) == "function" then
		local args = {...}
		process.nextTick(function()
			f(unpack(args))
		end)
	end
end

---
--
function AssertValidRole (role)
	assert(role == "normal" or role == "coach" or role == "monitor" or role == "ninja",
		("invalid role '%s'"):format(tostring(role) )
	)
end

---
-- http://www.haskell.org/haskellwiki/Partial_application
--
function PartialApply (f, arg_count)
	
	-- appends 'num_args' elements from table 'source' to table 'dest'
	local function append (dest, source, num_args)
		for i = 1, num_args do
			dest[dest.n + i] = source[i]
		end
		dest.n = dest.n + num_args
		return dest
	end
	
	-- function, accumulated arguments, arguments left
	local function partial_apply_h (f, acc_args, num_args_left)
		return function (...)
			local num_args = select("#", ...)
			
			if num_args < num_args_left then
				return partial_apply_h(f, append(acc_args, {...}, num_args), num_args_left - num_args)
			else
				local whole_args = append(acc_args, {...}, num_args)
				return f( unpack(whole_args, 1, whole_args.n) )
			end
		end
	end
	
	return partial_apply_h(f, {n = 0}, arg_count)
end

return _ENV