--[[
	Title: Tables

	Some table helpers.
]]

-- Based off "RecursiveReadOnlyTables" by VeLoSo (http://lua-users.org/wiki/RecursiveReadOnlyTables)

-- cache the metatables of all existing read-only tables,
-- so our functions can get to them, but user code can't
local metatable_cache = setmetatable( {}, { __mode = "k" } )

local function make_getter( real_table )
	local function getter( dummy, key )
		local ret = real_table[ key ]
		if istable( ret ) and not metatable_cache[ ret ] then
			ret = ULib.makeReadOnly( ret )
		end
		return ret
	end
	return getter
end

local function setter()
	ULib.error( "Attempt to modify read-only table!" )
end

local function make_pairs( real_table )
	local function pairs()
		local key, value, cur_key = nil, nil, nil
		local function nexter() -- both args dummy
			key, value = next( real_table, cur_key )
			cur_key = key
			if istable( key ) and not metatable_cache[ key ] then
				key = ULib.makeReadOnly( key )
			end
			if istable( value ) and not metatable_cache[ value ] then
				value = ULib.makeReadOnly( value )
			end
			return key, value
		end
		return nexter -- values 2 and 3 dummy
	end
	return pairs
end


--[[
	Function: makeReadOnly

	Makes a table and all recursive tables read-only

	Parameters:

		t - The table to make read-only

	Returns:

		The table read-only'fied
]]
function ULib.makeReadOnly( t )
	local new={}
	local mt={
		__metatable = "read only table",
		__index = make_getter( t ),
		__newindex = setter,
		__pairs = make_pairs( t ),
		__type = "read-only table" }
	setmetatable( new, mt )
	metatable_cache[ new ] = mt
	return new
end


--[[
	Function: ropairs

	The equivalent of "pairs" for a readonly table, since "pairs" won't work.

	Parameters:

		t - The table
]]
function ULib.ropairs( t )
	local mt = metatable_cache[ t ]
	if mt==nil then
		ULib.error( "bad argument #1 to 'ropairs' (read-only table expected, got " .. type(t) .. ")" )
	end
	return mt.__pairs()
end


--[[
	Function: findInTable

	Finds a value in a table. As opposed to table.HasValue(), this function will *only* check numeric keys, and will return a number of where the value is.

	Parameters:

		t - The table to check
		check - The value to check if it exists in t. Can be any type.
		init - *(Optional, defaults to 1)* The value to start from.
		last - *(Optional, defaults to the length of the table)* The value to end at.
		recursive - *(Optional, default to false)* If true, it will check any subtables it comes across.

	Returns:

		The number of the key where check resides, false if none is found. If init > last it returns false as well.
]]
function ULib.findInTable( t, check, init, last, recursive )
	init = init or 1
	last = last or #t

	if init > last then return false end

	for i=init, last do
		if t[ i ] == check then return i end

		if istable( t[ i ] ) and recursive then return ULib.findInTable( v, check, 1, recursive ) end
	end

	return false
end

--[[
	Function: matrixTable

	Splits a table into a number of given columns. Does not change original table.

	Parameters:

		t - The table to split
		columns, The number of columns to create

	Returns:

		The new table with the column being the first key and the row being the second key.

	Revisions:

		v2.10 - Initial
]]
function ULib.matrixTable( t, columns )
	local baserows = math.floor( #t / columns )
	local remainder = math.fmod( #t, columns )
	local nt = {} -- New table after we process
	local curn = 1 -- What value to grab next from our old table

	for i=1, columns do
		local numtograb = baserows
		if i <= remainder then
			numtograb = baserows + 1
		end

		nt[ i ] = {}
		for n=0, numtograb - 1 do
			table.insert( nt[ i ], t[ curn + n ] )
		end
		curn = curn + numtograb
	end

	return nt
end
