LuxModule.registerclass("luxinialuacore","VFSmapper",
[[The Virtual File System mapper package provides access on files by
which may either exist in directories of the harddisc or in zip file
archives. Overloads loadfile/dofile in order to load lua source
code from zip archives as well. Further filehandlers can be added
during runtime which allows loading files from different locations
(i.e. Network) as well.
]],VFSmapper,{
	addCallback = [[(function func):(function func) - adds a callback
	function that is called when a file should be loaded. It receives
	two arguments: the filname to be looked for and a boolean value if
	it should be read and returned:

	 function func (filename, read)
	   return read and (read the file content) or (true if it exists)
	 end

	If the function returns nil (nothing), further callback functions
	are called. If it returns false, it is assumed that the file does
	not exist. If it returns true, the file exists and it should be
	possible to return the content as well if needed.

	Luxinia checks if a file exists before it trys to open it. If
	this callback returns true, it MUST be able to return the filecontent
	if requested. Otherwise Luxinia might crash. This callback might
	be extended in near future to determine the filetype as well by
	returning the filetype.]],

	removeCallback = [[():(function func) - removes the given function
	from the callback list.]],

	dir = [[(func iterator):(dirname) - iterates the content of the given
	directoryname. Even if the directory does not exist, it will return
	a function that returns nil. Overloads lfs/FileSystem.dir to use this
	function.]],

	addZip = [[(result,[error]):(file) - adds a file to the list
	of zip archives that are browsed for requested files. If the
	given file could not be opened as zip file, it returns false
	and the reported error string.]],

	removeZip = [[():(file) - remove a zipfile from the list]],

	ziploader = [[(exists/filecontent):(file,read) - callback function
	for that is automaticly added to the list of callbacks. Handles
	the zipfile loading.]]
})



LuxModule.registerclass("luxinialuacore","DataRegistry",
	[[The DataRegistry class provides a simple method to save projectdependent
	data like highscores, savegames etc. in a directory of luxinia itself (for now).
	The location of this data container might change in future to be stored
	in a user specific directory of the OS.

	The class provides simplified functions to load and save tables
	as well as providing direct access for file operations.

	Separating the user data and the projectdata helps to keep the project
	directory clean. Keep in mind that it becomes impossible to save
	data in the project directory if the project is compressed
	into a single archive. Thus, this system should be favored instead
	of direct file access.

	The DataRegistry awaits a so called projectkey, which should be
	a string that identifies the project. A directory in the data
	directory with this name is then created. The additional
	filename can contain directorie relations as well. If the
	location does not exist, it will automaticly be created.]],
	{},
	{
		delete = [[():(projectkey,file) - deletes the given file in the project
		directory. Directories are only deleted, if they are empty.]],
		deleteall = [[():(projectkey,[dirpath]) - deletes the whole project or
		the given subdirectories.]],
		dir = [[(iterator):(projectkey,[dirpath]) - returns a directory
		content iterator for the  given directory.]],
		makepath = [[():(path) - makes sure that the given path exists]],
		getpath= [[(path):(string projectkey,[string file]) -
		returns a valid path that points to the given project path or
		to the file in that projectpath]],
		loadtable = [[([tab]):(projectkey,file,[table defaulttable]) -
		returns defaulttable if no matching file could  be loaded, otherwise
		the file's content is loaded]],
		savetable = [[():(projectkey,file,table data) -
		writes the given data table to the file located in the projectpath]],
		open = [[(filehandle,[errormsg]):(projectkey,file,[mode]) -
		returns the filehandle to the given file or nil and the errormessage
		if this could not be done]],
	})


UtilFunctions = {}
LuxModule.registerclass("luxinialuacore","UtilFunctions",
		"The UtilFunctions class is a collection of miscellanous useful lua functions.",UtilFunctions,{})

LuxModule.registerclassfunction("pairsByKeys",
[[
	(function):(table,[comperator]) - returns an iterator function to traverse the table in
	sorted order. The comperator function can be used to use another ordering for traversion.
	The returned function returns three values if it is called: the key, the value, a
	number that tells the current index and another number that tells how many indeces
	are left to be be iterated. The comperator should take care about both
	values it gets - these could either be numbers, functions, strings, tables, etc.

	!!!!Example:

	 for key,value in UtilFunctions.pairsByKeys({cde=1, abc=2})
	 	do print(key,value) end

	!!!!Output:

	 abc  2
	 cde  3
]])
function UtilFunctions.pairsByKeys (t, f)
	local a = {}
	for n in pairs(t) do table.insert(a, n) end
	table.sort(a, f or
		function (a,b)
			if (type(a)=="number" and type(b)=="number") then return a<b
			elseif (type(a)=="number") then return true
			elseif (type(b)=="number") then return false
			else return tostring(a)<tostring(b) end
		end
	)
	local i,n = 0,table.getn(a)      -- iterator variable
	local iter = function ()   -- iterator function
		i = i + 1
		if a[i] == nil then return nil
		else return a[i], t[a[i]], i, n-i end
	end
	return iter
end

LuxModule.registerclassfunction("printf",
[[
	():(string format, ...) - prints out a formated  string as the rules of string.format describe. This
	function is also stored in the global variable named printf
]])

function UtilFunctions.printf (fmt, ...)
	print(fmt:format(...))
end

printf = UtilFunctions.printf

LuxModule.registerclassfunction("smallLogoPos",
[[
	():(int n) - Sets the position of the small Luxinia Logo at one of the four
	corners (whatever you like more). Topright = 1, rightbottom = 2, bottomleft = 3,
	topleft = 4.
]])
function UtilFunctions.smallLogoPos (n) end


LuxModule.registerclassfunction("tabletotalcount",
[[
	(n):(table) - returns the number of elements in that table and all of its childs
	it also traverses into the metatables, but ignores tables that are weak typed. It also counts metatables as element.

	This function should help to find memory leaks, which can occure if table references
	are not removed.
]])
function UtilFunctions.tabletotalcount (tab)
	local visited = {}
	local stack = {tab}
	local n = 0

	local function add(t)
		n = n + 1
		if type(t)=="table" and not visited[t] then
			visited[t] = true
			stack[#stack+1] = t
		end
		if type(t)=="userdata" then
			add(debug.getmetatable(t))
		end
	end

	while #stack>0 do
		local t = table.remove(stack)
		local mt = getmetatable(t)
		if mt then add(mt) end
		if not mt or not mt.__mode then
			for k,v in pairs(t) do
				add(k)
				add(v)
			end
		end
	end
	return n
end

LuxModule.registerclassfunction("setTableValue",
[[
	([index,oldvalue]):(table,value,[newvalue]) - searches a val in the given table.
	The first hit is replaced by newvalue, which removes the tableentry if
	it is nil. You can use it therefore to remove a value from a table.
	The index of the removed element and the value is then returned.

	If no match was found, nothing is returned.
]])
function UtilFunctions.setTableValue(tab,val,newvalue)
	for i,v in pairs(tab) do
		if (v == val) then
			tab[i] = newvalue
			return i,val
		end
	end
end


LuxModule.registerclassfunction("fileexists",
	[[
	(boolean exists):(string path) - tries out if the given file exists.
	This is not very clean since it only trys to open and close the file and
	if it doesn't throw an error it is assumed that the file exists.
	]])

function UtilFunctions.fileexists(file)
	return VFSmapper.fileexists(file)
end

LuxModule.registerclassfunction("luafilename", [[
	(string filename):(int level) - retrieves the filename of a luafunction,
	if possible.
	The level is the functioncallerstack and should be >=1
	returns filename and level of the found path.
	]])
function UtilFunctions.luafilename(level)
	level = level and level + 1 or 2
	local src
	while (true) do
		src = debug.getinfo(level)
		if (src == nil) then return nil,level end
		if (string.byte(src.source) == string.byte("@")) then
			return string.sub(src.source,2),level
		end
		level = level + 1
	end
end

LuxModule.registerclassfunction("luafilepath",
	[[(string filepath):(int level) - like luafilename,
	but removes the filename itself]])
function UtilFunctions.luafilepath(level)
	local src,level = UtilFunctions.luafilename(level)
	if (src == nil) then return src,level end
	src = string.gsub(src,"[\\/][^\\//]*$","")
	return src,level
end


-- wrapper for the dofile function that loads and executes the luachunk
LuxModule.registerclassfunction("luaDoFile", "original lua dofile function")
UtilFunctions.luaDoFile = dofile

LuxModule.registerclassfunction("dofile",
	[[():(string path,...) - similiar to original dofile except that this
	function trys to find the file in different locations relative to all
	loaded luafiles. This function replaces the original dofile function.]])
local _dofile = dofile
function UtilFunctions.dofile (file,...)
	assert(type(file) == 'string',"String as filename expected")
	return assert(loadfile(file))(...)
	--[=[local name = file
	local level = 3
	while (name) do
		if (UtilFunctions.fileexists(name)) then
			return _dofile(name,unpack(arg))
		end
		name,level = UtilFunctions.luafilepath(level)
		if (name == nil) then break end
		name = name .. "/" .. file
		--_print(name)
	end
	return _dofile(file)]=]
end
dofile = UtilFunctions.dofile

LuxModule.registerclassfunction("luaLoadFile", "original lua loadfile function")
UtilFunctions.luaLoadFile = loadfile
LuxModule.registerclassfunction("loadfile",
	[[():(string path,...) - similiar to original loadfile except that this
	function trys to find the file in different locations relative to all
	loaded luafiles. This function replaces the original loadfile function.]])
local _loadfile = loadfile
function UtilFunctions.loadfile (file)
	assert(type(file)=='string',"String as filename expected")
	local name = file
	local level = 3
	while (name) do
		if (UtilFunctions.fileexists(name)) then return _loadfile(name) end
		name,level = UtilFunctions.luafilepath(level)
		if (name == nil) then break end
		name = name .. "/" .. file
		--_print(name)
	end
	return _loadfile(file)
end
loadfile = UtilFunctions.loadfile

LuxModule.registerclassfunction("loadFontFile",
	[[(fontset,texture):(string fontname) - loads a font generated by the Font Bitmap Generator. The fontname must not contain
	a suffix (.lua). The function trys to load a fontfile that must reside in a 'fonts' directory. You must not delete
	the fontset yourself. All characters in the fontset that are not set by the font itself are mapped to the glyphid 0 (lefttopmost glyph)]])
local fonttables = {}
function UtilFunctions.loadFontFile (fontname)
	if fonttables[fontname] then
		return  fonttables[fontname][1], texture.load("fonts/"..fontname..".tga")
	end

	--reschunk.usecore(true)
	local tex = assert(texture.load("fonts/"..fontname..".tga"), "font texture not found - make sure that "..fontname.."exists!")
	--reschunk.usecore(false)
	local param
	local flua = "base/fonts/"..fontname..".lua"
	if not FileSystem.attributes(flua) then flua = system.projectpath().."/fonts/"..fontname..".lua" end
	param = dofile(flua)

	local fset = fontset.new(param.tablesize)
	for i=0,255 do fset:lookup(i,0) end
	local div = param.resolution/param.tablesize
	for i=1,param.tablesize*param.tablesize do
		local chr = param.chartable[i]
		local w = param.widths[i]
		fset:width(chr,w/div)
		fset:lookup(chr,i-1)
	end

	fset:offsetx(param.xoffset)
	fset:offsety(param.yoffset)
	fset.scale = param.resolution/256
	fset:linespacing(param.lineheight/fset.scale)
	fonttables[fontname] = {fset,tex}
	return fset,tex
end

LuxModule.registerclassfunction("getModelColMesh",
	[[(tableBase,tableHit,[tableRaw]):(model,[boolean needrawtable],[matrix4x4],[boolean visibleonly],[boolean neednotrimeshdata]) - Creates a geom trimeshdata from the given model. Optionally transforms vertices.<br>
	tableBase : {data = dgeomtrimeshdata, aabox = {min{x,y,z},max{x,y,z}, center = {x,y,z}} <br>
	tableHit : {table similar to base, but for every "hitmesh" = meshes that contain "hitzone" in the material name <br>
	tableRaw : { inds = indices{}, verts = vertices{}, tris = tridata{{mesh=meshid,orig=original index}...} }, table with all indices/vertices and a table with the tridata for every face in the model
	]])
function UtilFunctions.getModelColMesh (mdl,needrawtable,matrix,visibleonly,neednotrimeshdata)
	local indices,vertices= {},{}
	local hitmeshes
	local globalprop = {}
	local tridata = {}

	local function addmesh (m,mcur)
		local useindex,usevertices = indices,vertices
		local hitmesh = false
		local props = globalprop
		if (m:texname() and string.find(m:texname(),"hitzone")) then
			useindex = {}
			usevertices = {}
			hitmesh = true
			props = {}
		end
		if not visibleonly and ((m:texname() and string.find(m:texname(),".*light.*")) or (m:name() and m:name():find("nocol"))) then
			return
		end
		if visibleonly and string.find(m:name(),"invisible") then return end

		-- handle bone transforms
		local mymatrix = nil
		local bid = m:boneid()
		if (bid) then
			local bmatrix = bid:matrix()
			if (matrix) then
				mymatrix = matrix4x4.new()
				mymatrix:mul(matrix,bmatrix)
			else
				mymatrix = bmatrix
			end
		else
			mymatrix = matrix
		end

		for i=0,m:indexPrimitivecount()-1 do
			local tri = {m:indexPrimitive(i)}
			local a,b,c = unpack(tri)
			--_print(a,b,c)
			if (not (a==b or b==c or c==a)) then
				table.insert(tridata,{mesh = m, index = i})
				local offset = table.getn(usevertices)/3
				for j=1,3 do
					local p = {m:vertexPos(unpack({tri[j],mymatrix}))}
					props.min = props.min or {p[1],p[2],p[3]}
					props.max = props.max or {p[1],p[2],p[3]}
					for k=1,3 do
						props.min[k] = math.min(props.min[k], p[k])
						props.max[k] = math.max(props.max[k], p[k])
						--print(string.format("%.2f %.2f %.2f",props.min[k], p[k],props.max[k]))
					end
					table.insert(usevertices,p[1])
					table.insert(usevertices,p[2])
					table.insert(usevertices,p[3])
				end
				table.insert(useindex,offset)
				table.insert(useindex,offset+1)
				table.insert(useindex,offset+2)
			end
		end

		if (hitmesh) then
			hitmeshes = hitmeshes or {}
			cntr = {(props.min[1]+props.max[1])*0.5,(props.min[2]+props.max[2])*0.5,
				(props.min[3]+props.max[3])*0.5 }
			table.insert(hitmeshes,{data = dgeomtrimeshdata.new(useindex,usevertices),
				aabox = props, center = cntr})
		end
	end

	for i=0,mdl:meshcount()-1 do
		addmesh(mdl:meshid(i),i)
	end
	local props = globalprop
	props.min = props.min or {0,0,0}
	props.max = props.max or {0,0,0}
	local cntr = {(props.min[1]+props.max[1])*0.5,(props.min[2]+props.max[2])*0.5,
		(props.min[3]+props.max[3])*0.5 }

	local rawtable = nil
	if (needrawtable) then
		rawtable = {inds = indices, verts = vertices, tris = tridata}
	end
	--print("trimeshcount: ",#indices/3)
	return 	{data = neednotrimeshdata or dgeomtrimeshdata.new(indices,vertices), aabox = globalprop, center = cntr}, hitmeshes, rawtable
end



LuxModule.registerclassfunction("loadCollisionDataFile",
	[[
	(dgeom table,[extratable]):([string path,table],[boolean needoriginaltable],[boolean createl3ds]) - loads dgeoms from the collision data lua file. extratable can contain the tables: "original" and/or "visual".Currently exported by 3dsmax.
	]])

function UtilFunctions.loadCollisionDataFile(file,needoriginaltable,createl3ds)
	local header,geomdata
	if (type(file) ~= "table") then
		header,geomdata = dofile(file)
	else
		header,geomdata = unpack(file)
	end


	local geoms = {}
	local visual = {}
	local trimeshdatas = {}
	local loader = {}

	if (not createl3ds) then
	 createl3ds = nil
	end

	if (header ~= "luxinia_collision_data version 001") then return end

	--print("importing..")

	loader.dgeombox = function (tab)
		return 	dgeombox.new(unpack(tab.parameters.sizes)),
			createl3ds and l3dprimitive.newcube(tab.name,unpack(tab.parameters.sizes))
	end

	loader.dgeomsphere = function (tab)
		return 	dgeomsphere.new(tab.parameters.radius),
			createl3ds and l3dprimitive.newsphere(tab.name,tab.parameters.radius)
	end

	loader.dgeomcylinder = function (tab)
		return 	dgeomcylinder.new(tab.parameters.radius,tab.parameters.height),
			createl3ds and l3dprimitive.newcylinder(tab.name,tab.parameters.radius,tab.parameters.radius,tab.parameters.height)

	end

	loader.dgeomccylinder = function (tab)
		local vis = createl3ds and l3dprimitive.newcylinder(tab.name,tab.parameters.radius,tab.parameters.radius,tab.parameters.height)
		if (vis) then
			local l3d = l3dprimitive.newsphere(tab.name,tab.parameters.radius)
			l3d:link(vis)
			l3d:localpos(0,0,tab.parameters.height/2)
			l3d:rfLitSun(true)
			l3d = l3dprimitive.newsphere(tab.name,tab.parameters.radius)
			l3d:link(vis)
			l3d:localpos(0,0,-tab.parameters.height/2)
			l3d:rfLitSun(true)
		end
		return dgeomccylinder.new(tab.parameters.radius,tab.parameters.height),vis
	end

	loader.dgeomtrimesh = function (tab)
		local trimesh
		local tabname
		local l3d = createl3ds and l3dprimitive.newcube(tab.name,100) --todo better size approximation

		if (tab.parameters.trimesh) then
			trimesh = trimeshdatas[tab.parameters.trimesh].mesh
			tabname = tab.parameters.trimesh
		else
			trimesh =  dgeomtrimeshdata.new(tab.parameters.indicesdata,tab.parameters.verticesdata)
			trimeshdatas[tab.name] = {mesh = trimesh, verts = tab.parameters.verticesdata, inds = tab.parameters.indicesdata}
			tabname = tab.name
		end
		if (not trimesh) then
			return nil
		end
		if (l3d) then
			l3d:renderscale(1,1,1)
			local tabverts = trimeshdatas[tabname].verts
			local tabinds = trimeshdatas[tabname].inds
			l3d:usermesh(vertextype.vertex32normals(),#tabverts,#tabinds)
			l3d:vertexCount(#tabverts)
			for i,v in ipairs(tabverts) do
				l3d:vertexPos(i-1,unpack(v))
				local normal = {unpack(v)}
				-- poor man's normals
				normal = ExtMath.v3normalize(normal)
				l3d:vertexNormal(i-1,unpack(normal))
			end

			l3d:indexPrimitivetype(primitivetype.triangles())
			l3d:indexCount(#tabinds)
			for i,v in ipairs(tabinds) do
				l3d:indexValue(i-1,v)
			end
			l3d:indexMinmax()
			l3d:indexTrianglecount()
			l3d:rfLitSun(true)
		end
		return  dgeomtrimesh.new(trimesh),l3d
	end

	for i,obj in pairs(geomdata) do
		local geom,l3dvis = loader[obj.type](obj)
		if (geom ~= nil) then
			geom:pos(unpack(obj.pos))
			geom:rotdeg(unpack(obj.rot))
			table.insert(geoms,geom)
		else
			print("luxinia_collision_data loader error: no geom created "+obj.name)
		end
		if (l3dvis) then
			vis = {s3d = scenenode.new(obj.name) , l3d = l3dvis}
			vis.s3d:localpos(unpack(obj.pos))
			vis.s3d:localrotdeg(unpack(obj.rot))
			vis.l3d:linkinterface(vis.s3d)
			vis.l3d:rfLitSun(true)
			table.insert(visual,vis)
		end
	end


	if (needoriginaltable or createl3ds) then
		local extradata = {}
		if (needoriginaltable) then extradata.original = geomdata end
		if (createl3ds) then extradata.visual = visual end

		return geoms,extradata
	else
		return geoms
	end
end

UtilFunctions.div255 = 1/255
LuxModule.registerclassfunction("color3byte", [[(float r,g,b):(float r,g,b) - converts the incoming color values 0-255, to 0-1 floats]])
function UtilFunctions.color3byte(r,g,b)
	return r*UtilFunctions.div255,g*UtilFunctions.div255,b*UtilFunctions.div255
end

LuxModule.registerclassfunction("color4byte", [[(float r,g,b,a):(float r,g,b,a) - converts the incoming color values 0-255, to 0-1 floats]])
function UtilFunctions.color4byte(r,g,b,a)
	return r*UtilFunctions.div255,g*UtilFunctions.div255,b*UtilFunctions.div255,a*UtilFunctions.div255
end


LuxModule.registerclassfunction("color3rgb2hsv", "(float h,s,v) : (float r,g,b) - converts the rgb color to a hsv color value")
function UtilFunctions.color3rgb2hsv (r,g,b)
	local min = (r<g and r<b and r) or (g<b and g) or b
	local max = (r>g and r>b and r) or (g>b and g) or b
	local v,s,h = max

	local delta = max - min;

	if  max ~= 0 then
		s = delta / max
	else
		return 0,0,0
	end

	if delta == 0 then
		return 0,0,v
	end

	if r == max then
		h = ( g - b ) / delta
	elseif g == max then
		h = 2 + ( b - r ) / delta
	else
		h = 4 + ( r - g ) / delta
	end

	h = h * 60
	if h < 0 then
		h = h + 360
	end

	return h/360,s,v
end

LuxModule.registerclassfunction("color3hsv", [[(float r,g,b):(float h,s,v) - converts the incoming hsv to color to 0-1 rgb floats]])
function UtilFunctions.color3hsv(H,S,V)
	if (S == 0) then
		return V*1.0,V*1.0,V*1.0
	else
		local var_h = (H*6)%6

		local var_i = math.floor(var_h)
		local var_1 = V * ( 1 - S )
		local var_2 = V * ( 1 - S * ( var_h - var_i ) )
		local var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) )

		if     ( var_i == 0 ) then 	return V,		var_3,	var_1
		elseif ( var_i == 1 ) then 	return var_2, 	V,		var_1
		elseif ( var_i == 2 ) then 	return var_1, 	V,		var_3
		elseif ( var_i == 3 ) then 	return var_1, 	var_2,	V
		elseif ( var_i == 4 ) then 	return var_3,  	var_1,	V
		else                  		return V,     	var_1,	var_2
		end
	end

end


FreeLook = {}
FreeLook.state = false
FreeLook.shiftmulti = 5.0
FreeLook.movemulti = 2.5
FreeLook.invert = false
FreeLook.dx,FreeLook.dy = 0,0
function FreeLook.think()
	if not FreeLook.act then return end -- got disabled
	if console.active() then return end -- don't do stuff while in console

	local spd = FreeLook.movemulti * (Keyboard.isKeyDown("SHIFT") and FreeLook.shiftmulti or 1.0)

	local cur,diff = system.time()
	spd = spd * diff * 0.0166

	local pos = {FreeLook.act:pos()}

	local sd = (Keyboard.isKeyDown("D") and spd or 0) - (Keyboard.isKeyDown("A") and spd or 0)
	local fw = (Keyboard.isKeyDown("W") and spd or 0) - (Keyboard.isKeyDown("S") and spd or 0)
	local up = (Keyboard.isKeyDown("SPACE") and spd or 0) - (Keyboard.isKeyDown("C") and spd or 0)

	local xx,xy,xz,yx,yy,yz,zx,zy,zz = FreeLook.act:rotaxis()

	local newpos = ExtMath.v3scaledadd(ExtMath.v3scaledadd(ExtMath.v3scaledadd(pos,{xx,xy,xz},sd),{yx,yy,yz},fw),{zx,zy,zz},up)

	FreeLook.act:pos(unpack(newpos))

	local mx,my = input.mousepos()
	local wx,wy = window.refsize()
	wx,wy = math.floor(wx/2),math.floor(wy/2)

	--if (mx == wx and my == wy) then return end
	local dx,dy = FreeLook.dx,FreeLook.dy
	if not MouseCursor.enable() then 
		dx,dy = 0,0
		for rx,ry,btn,down,when in input.popmouse do
			dx,dy = wx-rx+dx,wy-ry+dy
		end
	end

	if dx*dx+dy*dy>200^2 then dx,dy = 0,0 end -- mouse moved too strong, I assume we tabbed in window

	FreeLook.rot[3] = FreeLook.rot[3] + ((dx)/8.0)
	FreeLook.rot[1] = FreeLook.rot[1] + ((dy)/8.0 * (FreeLook.invert and -1.0 or 1.0))

	FreeLook.rot[1] = ((FreeLook.rot[1] > 86.0) and 85.5 or ((FreeLook.rot[1] < -86.0) and -85.5 or FreeLook.rot[1]))

	FreeLook.act:rotdeg(unpack(FreeLook.rot))

	
	input.mousepos(wx,wy)
	FreeLook.dx,FreeLook.dy = 0,0
	FreeLook.lastx,FreeLook.lasty = wx,wy
end

LuxModule.registerclassfunction("freelook",
	[[(boolean):(boolean) - enables/disables freelook of current default l3dcamera. Useful for debugging, FreeLook.invert .movemulti .shiftmulti let you modify behavior. Controls are WASD,C,SPACE + Mouse.]])
function UtilFunctions.freelook(state)
	if state == nil then return FreeLook.state end
	if (FreeLook.state == state) then return end
	FreeLook.state = state
	if (state) then
		FreeLook.act = actornode.new("_fl",true)
		FreeLook.cam = l3dcamera.default()
		FreeLook.oldlink = FreeLook.cam:linkinterface()

		if (FreeLook.oldlink) then
			local mat = FreeLook.oldlink.worldmatrix and FreeLook.oldlink:worldmatrix() or FreeLook.oldlink:matrix()
			FreeLook.act:matrix(mat)
		else
			FreeLook.oldwm = FreeLook.cam:worldmatrix()
		end
		FreeLook.cam:linkinterface(FreeLook.act)
		FreeLook.rot = {FreeLook.act:rotdeg()}

		render.drawcamaxis(true)
		Timer.set("_freelook",FreeLook.think)
		
		FreeLook.lastx,FreeLook.lasty = input.mousepos()
		FreeLook.mouselistener = FreeLook.mouselistener or 
			MouseListener.new(function (self,me)
				local mx,my = me.x,me.y
				local dx,dy = FreeLook.lastx-mx,FreeLook.lasty-my
				FreeLook.lastx,FreeLook.lasty = mx,my
				FreeLook.dx,FreeLook.dy = FreeLook.dx+dx,FreeLook.dy+dy
			end, MouseEvent.MOUSE_MOVED)-- mouselistener class does not exist yet during init
		MouseCursor.addMouseListener(FreeLook.mouselistener)
	else
		Timer.remove("_freelook")
		MouseCursor.removeMouseListener(FreeLook.mouselistener)

		render.drawcamaxis(false)
		if (FreeLook.oldlink) then
			FreeLook.cam:linkinterface(FreeLook.oldlink)
		else
			FreeLook.cam:unlink()
			FreeLook.cam:usemanualworld(true)
			FreeLook.cam:worldmatrix(FreeLook.oldwm)
		end
		FreeLook.cam = nil
		FreeLook.oldwm = nil
		FreeLook.act:delete()
		FreeLook.act = nil
	end

end

LuxModule.registerclassfunction("simplerenderqueue",
	[[(l3dview):([l3dview],[boolean noempty],[tab ignorelayers]) - sets up a standard renderqueue for the view (which is also returned).
	The l3dview has equivalent rcmds in its table. The order is: rClear,rDepth,rDrawbg,rLayers[1..16].
	Each rLayer contains: stencil,drawlayer,drawprt for the equivalent l3dlayerids-1. Sorting is enabled and material-based.<br>
	ignorelayers should contain 'true' at layer+1 field. Ie {true,true} will disable layer 0 & 1.
	]])
function UtilFunctions.simplerenderqueue(view,noempty,ignorelayers)
	view = view or l3dset.get(0):getdefaultview()

	--if (not l3dview._globalrefs) then
	--	l3dview._globalrefs = {}
	--end

	if (not noempty) then
		view:rcmdempty()
	end

	view.rClear = rcmdclear.new()
	view.rClear:colorvalue(0.8,0.8,0.8,0)
	view:rcmdadd(view.rClear)
	
	view.rDepth = rcmddepth.new()
	view.rDepth:compare(comparemode.lequal())
	view:rcmdadd(view.rDepth)
	
	view.rDrawbg = rcmddrawbg.new()
	view:rcmdadd(view.rDrawbg)

	view.rLayers = {}
	for i=1,l3dlist.l3dlayercount() do
		if ((not ignorelayers) or (not ignorelayers[i])) then
			local st,d,prt = rcmdstencil.new(),rcmddrawlayer.new(),rcmddrawprt.new()
			view.rLayers[i] = {drawlayer=d,drawprt=prt,stencil=st}
			d:layer(i-1)
			d:sort(1)
			prt:layer(i-1)

			view:rcmdadd(st)
			view:rcmdadd(d)
			view:rcmdadd(prt)
		else
			view.rLayers[i] = false
		end
	end
	
	--l3dview._globalrefs[view] = view

	return view
end

LuxModule.registerclassfunction("picktest",
	[[([geom,{x,y,z},{nx,ny,nz}]):(dspace space,[x=mousex],[y=mousey],[maxlength=1000000],[l3dcamera camera = l3dcamera.default]) -
	This function will test the given space with a ray and returns, if a hit was generated, the geom, the coordinate of the
	nearest hit and the hitnormal. The ray is in line with the x,y pair (or the mouse coordinates if not given)
	on the camera projection plane and through the last known camera worldposition (this worldposition can be one frame behind, so
	you might have to call camera:forceupdate() to update to the currently set position if this is important, which is not in most cases).
	The maxlength can be optionally set and is per default set to 1000000. If no camera is specified, the default camera is used.

	This function handles also orthogonal camera fovs, which requires a different
	raysetting code.

	!!Example:

	[img utilfunctions.picktest.png]

	 UtilFunctions.simplerenderqueue()

	 input.showmouse(true) -- so we can see the mouse
	 space = dspacehash.new() -- our space with our objects
	 box = actornode.new("box")  -- our box location
	 box.geom = dgeombox.new(1,1,1,space) -- the geom, placed in 0,0,0
	 box.geom.l3d = l3dprimitive.newbox("box",1,1,1) -- the l3d for our box
	 box.geom.l3d:linkinterface(box) -- link it
	 cam = actornode.new("cam",5,4,3) -- our cameranode so we can view
	 l3dcamera.default():linkinterface(cam) -- link it with defaultcamera
	 cam:lookat(0,0,0,0,0,1) -- and look at 0,0,0 (the box location)

	 highlight = actornode.new("highlight") -- a simple highlighter
	 highlight.l3d = l3dprimitive.newsphere("hightlight",0.05)
	 highlight.l3d:color(1,0,0,1) -- make sphere red
	 highlight.l3d:linkinterface(highlight) -- link it

	 local function test () -- our testing function
	 	 local g,pos = UtilFunctions.picktest(space)  -- test it
	 	 highlight.l3d:rfNodraw(not pos) -- don't highlight if no hit

	 	 if pos then  -- if we have a hit
	     highlight:pos(pos[1],pos[2],pos[3]) -- move the highlighter
	     g.l3d:color(1,math.sin(os.clock()*5)/2+.5,1,1)
	     	-- ^^ make the hit object do something
	 	 end
	 end

	 Timer.set("picker", test,20) -- run the test every 20ms
	]])
local pickray = dgeomray.new(1)
function UtilFunctions.picktest(space,mx,my,maxlength,camera)
   ------------------------
   -- setup the test ray:--
   ------------------------
   -- compute origin as point
   camera = camera or l3dcamera.default()
   local mousex,mousey = input.mousepos()

	maxlength = maxlength or 1000000 
   if camera:fov()<0 then
   	local w,h = window.refsize()
   	local s = -camera:fov()
   	local a = camera:aspect()
   	if a<0 then a = w/h end
   	local mx,my = (mx or mousex)/w - .5,((my or mousey)-h*.5)/w
   	local x,y,z = camera:worldpos()
   	local rx,ry,rz, vx,vy,vz, ux,uy,uz = camera:worldrotaxis()
   	local function sc (a,b,c,s) return a*s,b*s,c*s end

   	rx,ry,rz = sc(rx,ry,rz, mx*s)
   	ux,uy,uz = sc(ux,uy,uz, -my*s)
   	x,y,z = x + rx + ux,y + ry + uy,z + rz + uz
   	--print(mx,my)
   	pickray:set(x,y,z,vx,vy,vz)
   else
		local x,y,z = camera.default():worldpos()
	   local tx,ty,tz = camera:toworld(mx or mousex or 0,my or mousey or 0,1)
	   -- compute direction as unit vector
		local dx,dy,dz = tx-x,ty-y,tz-z
	   local d = (dx*dx+dy*dy+dz*dz)^0.5
	   dx,dy,dz = dx/d,dy/d,dz/d
	   -- set ray values
	   pickray:set(x,y,z,dx,dy,dz)
	 end
	 pickray:length(maxlength)
   ---------------------
   -- make coll. test --
   ---------------------
   local x,y,z = camera:worldpos()
   local cnt,depth,geoms,pos,norm = pickray:test(space,"dgpn")

   -- prefix for
   if (cnt>0) then -- a hit is produced
    local nearest,d
    for i = 1,cnt do
    	local dx,dy,dz = x-pos[i*3-2],y-pos[i*3-1],z-pos[i*3]
    	local dist = dx*dx+dy*dy+dz*dz
    	if not d or d>dist then
      	nearest = i
      	d = dist
      end
    end
    local i = nearest

		local x,y,z = pos[i*3-2],pos[i*3-1],pos[i*3]
		local g = geoms[nearest*2-1]
		if g == pickray then g = geoms[nearest*2] end
    return g,{x,y,z},{norm[i*3-2],norm[i*3-1],norm[i*3]}
   end
end


dofile("serializer.lua")


LuxEventCallbacks = {}
LuxEventCallbacks.windowresize = {}

function luxinia.windowresized()
	for i,f in pairs(LuxEventCallbacks.windowresize) do
		f()
	end
end

LuxModule.registerclassfunction("addWindowResizeListener",
	[[():(function) - will call function after window was resized (directly after window.update or with a few milliseconds delay if thru window.resizemode > 0.]])
function UtilFunctions.addWindowResizeListener(func)
	LuxEventCallbacks.windowresize[func] = func
end

LuxModule.registerclassfunction("removeWindowResizeListener",
	[[():(function) - removes the given function from being called on window resize events]])
function UtilFunctions.removeWindowResizeListener(func)
	LuxEventCallbacks.windowresize[func] = nil
end


LuxModule.registerclassfunction("checkargtable",
[[(table):(table) - This function provides a system to check table keys
for values. This is useful if a functions is taking a table containing 
various "named" function arguments. The return value of this function is a
table which provides sophisticated functionalities. If the table is
called with a string as argument, it expects that a key in the table
is to be checked and returns a table with possible value checking functions:

* type: checks if the value of this key is of a certain type
* optional: if the value is nil, the passed optional value is returned (may be nil)
* translate: expects an table to be passed. That table contains values that substitue the value of the key. A 2nd optional value is used if no value was found 
* oneofthis: like translate, but doesn't substitute the original value

If the field "finish" is called, the collected arguments will be returned in the order
they've been checked

  local x,y,vx,vy,type = UtilFunctions.checkargtable(info) 
    "x":type "number" 
    "y":type "number"
    "vx":optional(0)
    "vy":optional(0)
    "type":oneofthis{["player"]=true, ["AI"]=true}
    :finish()

]])
function UtilFunctions.checkargtable (info)
  local ret = {}
  local handler = {}
  local mt = {}
  local nret = 0
  setmetatable(handler,mt)
  function mt:__call(name)
    local checking = {}
    
    -- using colon for cosmetical reason only, though could be useful later...
    function checking:type (whichtype)
      if type(info[name])~=whichtype then
        error("Invalid type for argument "..name..": "..type(info[name]),3)
      end
      nret = nret + 1
      ret[nret] = info[name]
      return handler
    end
    
    function checking:oneofthis (tab, opt)
      if (info[name]==nil and not opt) or not tab[info[name] or opt] then
      	error("Invalid value for argument "..name..": none of the expected possible values")
      end
      nret = nret + 1
      ret[nret] = info[name] or opt
      return handler
    end
    
    function checking:translate (tab,opt)
      if (info[name]==nil and not opt) or tab[info[name] or opt]==nil then
      	error("Invalid value for argument "..name..": Could not be translated")
      end
      
      nret = nret + 1
      ret[nret] = tab[info[name] or opt]
      return handler
    end
    
    function checking:optional(value)
   	  nret = nret + 1
      if info[name]==nil then
      	ret[nret] = value
      else
      	ret[nret] = info[name]
      end
      return handler
    end
    
    return checking
  end
  
  function handler:finish()
  	return unpack(ret,1,nret) -- I am done
  end
    
  return handler
end
