----------------------------------------------------------------------------------
--- Mudlet Table Utils
----------------------------------------------------------------------------------


--- Tests if a table is empty: this is useful in situations where you find
--- yourself wanting to do 'if my_table == {}' and such.
---
--- @usage Testing if the table is empty.
---   <pre>
---   myTable = {}
---   if table.is_empty(myTable) then
---      echo("myTable is empty")
---   end
---   </pre>
function table.is_empty(tbl)
	if next(tbl) == nil then return true else return false end
end



--- Lua debug function that prints the content of a Lua table on the screen, split up in keys and values.
--- Useful if you want to see what the capture groups contain i. e. the Lua table "matches".
---
--- @see display
function printTable( map )
	echo("-------------------------------------------------------\n");
	for k, v in pairs( map ) do
		echo( "key=" .. k .. " value=" .. v .. "\n" )
	end
	echo("-------------------------------------------------------\n");
end



-- NOT LUADOC
-- This is supporting function for printTable().
function __printTable( k, v )
	insertText ("\nkey = " .. tostring (k) .. " value = " .. tostring( v )  )
end



--- Lua debug function that prints the content of a Lua table on the screen. <br/>
--- There are currently 3 functions with similar behaviour.
---
--- @see display
--- @see printTable
function listPrint( map )
	echo("-------------------------------------------------------\n");
	for k,v in ipairs( map ) do
		echo( k .. ". ) "..v .. "\n" );
	end
	echo("-------------------------------------------------------\n");
end



--- <b><u>TODO</u></b> listAdd( list, what )
function listAdd( list, what )
	table.insert( list, what );
end



--- <b><u>TODO</u></b> listRemove( list, what )
function listRemove( list, what )
	for k,v in ipairs( list ) do
		if v == what then
			table.remove( list, k )
		end
	end
end



--- Gets the actual size of non-index based tables. <br/><br/>
---
--- For index based tables you can get the size with the # operator: <br/>
--- This is the standard Lua way of getting the size of index tables i.e. ipairs() type of tables with
--- numerical indices. To get the size of tables that use user defined keys instead of automatic indices
--- (pairs() type) you need to use the function table.size() referenced above.
---   <pre>
---   myTableSize = # myTable
---   </pre>
function table.size(t)
	if not t then
		return 0
	end
	local i = 0
	for k, v in pairs(t) do
		i = i + 1
	end
	return i
end



--- Determines if a table contains a value as a key or as a value (recursive).
function table.contains(t, value)
	if type(t) ~= "table" then return nil, "first parameter passed isn't a table" end

	for k, v in pairs(t) do
		if v == value then
			return true
		elseif k == value then
			return true
		elseif type(v) == "table" then
			if table.contains(v, value) then return true end
		end
	end
	return false
end



--- Table Union.
---
--- @return Returns a table that is the union of the provided tables. This is a union of key/value
---   pairs. If two or more tables contain different values associated with the same key,
---   that key in the returned table will contain a subtable containing all relevant values.
---   See table.n_union() for a union of values. Note that the resulting table may not be
---   reliably traversable with ipairs() due to the fact that it preserves keys. If there
---   is a gap in numerical indices, ipairs() will cease traversal.
---
--- @usage Example:
---   <pre>
---   tableA = {
---      [1] = 123,
---      [2] = 456,
---      ["test"] = "test",
---   }
---
---   tableB = {
---      [1] = 23,
---      [3] = 7,
---      ["test2"] = function() return true end,
---   }
---
---   tableC = {
---      [5] = "c",
---   }
---
---   table.union(tableA, tableB, tableC) will return:
---   {
---      [1] = {
---         123,
---         23,
---      },
---      [2] = 456,
---      [3] = 7,
---      [5] = "c",
---      ["test"] = "test",
---      ["test2"] = function() return true end,
---   }
---   </pre>
function table.union(...)
	local sets = {...}
	local union = {}

	for _, set in ipairs(sets) do
		for key, val in pairs(set) do
			if union[key] and union[key] ~= val then
				if type(union[key]) == 'table' then
					table.insert(union[key], val)
				else
					union[key] = { union[key], val }
				end
			else
				union[key] = val
			end
		end
	end

	return union
end



--- Table Union.
---
--- @return Returns a numerically indexed table that is the union of the provided tables. This is
---   a union of unique values. The order and keys of the input tables are not preserved.
function table.n_union(...)
	local sets = {...}
	local union = {}
	local union_keys = {}

	for _, set in ipairs(sets) do
		for key, val in pairs(set) do
			if not union_keys[val] then
				union_keys[val] = true
				table.insert(union, val)
			end
		end
	end

	return union
end



--- Table Intersection.
---
--- @return Returns a table that is the intersection of the provided tables. This is an
---   intersection of key/value pairs. See table.n_intersection() for an intersection of values.
---   Note that the resulting table may not be reliably traversable with ipairs() due to
---   the fact that it preserves keys. If there is a gap in numerical indices, ipairs() will
---   cease traversal.
---
--- @usage Example:
---   <pre>
---   tableA = {
---      [1] = 123,
---      [2] = 456,
---      [4] = { 1, 2 },
---      [5] = "c",
---      ["test"] = "test",
---   }
---
---   tableB = {
---      [1] = 123,
---      [2] = 4,
---      [3] = 7,
---      [4] = { 1, 2 },
---      ["test"] = function() return true end,
---   }
---
---   tableC = {
---      [1] = 123,
---      [4] = { 1, 2 },
---      [5] = "c",
---   }
---
---   table.intersection(tableA, tableB, tableC) will return:
---   {
---      [1] = 123,
---      [4] = { 1, 2 },
---   }
---   </pre>
function table.intersection(...)
	sets = {...}
	if #sets < 2 then return false end

	local intersection = {}

	local function intersect(set1, set2)
		local result = {}
		for key, val in pairs(set1) do
			if set2[key] then
				if _comp(val, set2[key]) then result[key] = val end
			end
		end
		return result
	end

	intersection = intersect(sets[1], sets[2])

	for i, _ in ipairs(sets) do
		if i > 2 then
			intersection = intersect(intersection, sets[i])
		end
	end

	return intersection
end



--- Table Intersection.
---
--- @return Returns a numerically indexed table that is the intersection of the provided tables.
---   This is an intersection of unique values. The order and keys of the input tables are
---   not preserved.
function table.n_intersection(...)
	sets = {...}
	if #sets < 2 then return false end

	local intersection = {}

	local function intersect(set1, set2)
		local intersection_keys = {}
		local result = {}
		for _, val1 in pairs(set1) do
			for _, val2 in pairs(set2) do
				if _comp(val1, val2) and not intersection_keys[val1] then
					table.insert(result, val1)
					intersection_keys[val1] = true
				end
			end
		end
		return result
	end

	intersection = intersect(sets[1], sets[2])

	for i, _ in ipairs(sets) do
		if i > 2 then
			intersection = intersect(intersection, sets[i])
		end
	end

	return intersection
end



--- Table Complement.
---
--- @return Returns a table that is the relative complement of the first table with respect to
---   the second table. Returns a complement of key/value pairs.
function table.complement(set1, set2)
	if not set1 and set2 then return false end
	if type(set1) ~= 'table' or type(set2) ~= 'table' then return false end

	local complement = {}

	for key, val in pairs(set1) do
		if not _comp(set2[key], val) then
			complement[key] = val
		end
	end
	return complement
end



--- Table Complement.
---
--- @return Returns a table that is the relative complement of the first table with respect to
---   the second table. Returns a complement of values.
function table.n_complement(set1, set2)
	if not set1 and set2 then return false end

	local complement = {}

	for _, val1 in pairs(set1) do
		local insert = true
		for _, val2 in pairs(set2) do
			if _comp(val1, val2) then
				insert = false
			end
		end
		if insert then table.insert(complement, val1) end
	end

	return complement
end


function table.update(t1, t2)
        local tbl = {}
        for k,v in pairs(t1) do
                tbl[k] = v
        end
        for k,v in pairs(t2) do
                if type(v) == "table" then
                        tbl[k] = table.update(tbl[k] or {}, v)
                else
                        tbl[k] = v
                end
        end
        return tbl
end

---Returns the index of the value in a table
function table.index_of(table, element)
  for index, value in ipairs(table) do
    if value == element then
      return index
    end
  end
  return nil
end

-- returns a deep copy of the table with the metatable intact. Credit to Steve Donovan of Penlight.
function table.deepcopy(t)
    if type(t) ~= 'table' then return t end
    local mt = getmetatable(t)
    local res = {}
    for k,v in pairs(t) do
        if type(v) == 'table' then
            v = table.deepcopy(v)
        end
        res[k] = v
    end
    setmetatable(res,mt)
    return res
end
