local table_insert = table.insert
local table_remove = table.remove
local type = type
local ipairs = ipairs
local setmetatable = setmetatable

--------- events

local function make_events(table)
	local t = {}

	local function index(_, a)
		local function call(_, ...)
			local f = t[a]
			if f then
				local ok = true
				local tmp, ret
				if type(f) == "table" then
					for i = 1, #f do
						local v = f[i]
						if f ~= nil then
							tmp = v(...)
							if ok and tmp ~= nil then
								ret = tmp
							end
						end
					end
				else
					ret = f(...)
				end
				return ret
			end
		end

		local function add(func)
			local f = t[a]
			if f == nil then
				t[a] = func
			elseif type(f) == "table" then
				table_insert(f, func)
			else
				t[a] = {f, func}
			end
		end
		local function remove(func)
			local f = t[a]
			if f == func then
				t[a] = nil
			elseif f and type(f) == "table" then
				local i = 1
				local v = f[1]
				while v do
					if v == func then
						if i == 1 and f[2] == nil then
							t[a] = nil
						else
							table_remove(f, i)
						end
						return v
					end
					i = i+1
					v = f[i]
				end
			end
		end
		local function clear()
			t[a] = nil
		end
		local function exists(func)
			local f = t[a]
			if func then
				if f == func then
					return true
				elseif f and type(f) == "table" then
					for _, v in ipairs(f) do
						if v == func then
							return true
						end
					end
				end
				return false
			else
				return f ~= nil
			end
		end

		return setmetatable({add = add, Add = add, remove = remove, Remove = remove, exists = exists,
		                    Exists = exists, clear = clear, Clear = clear,
		                    pcall = _pcall, cocall = _cocall, cocalls = _cocalls}, {__call = call})
	end

	local function newindex(_, a, v)
		local f = t[a]
		if f then
			if type(f) == "table" then
				table_insert(f, v)
			else
				t[a] = {f, v}
			end
		else
			t[a] = v
		end
	end

	table = table or {}

	function table.RemoveAll()
		t = {}
	end

	return setmetatable(table, {__index = index, __newindex = newindex}), t
end

events = make_events{new = make_events, global = make_events{}}
