-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--
--! @file environment.lua
--! @brief component for environment related functions
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup environment Environment subcomponent
--! @brief Environment check functions used by different subcomponents
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
mobf_assert_backtrace(environment == nil)
environment = {}

mobf_assert_backtrace(environment_list == nil)
--! @brief list of known environments
--! @memberof environment
environment_list = {}

-------------------------------------------------------------------------------
-- @function [parent=#environment] get_pos_same_level(pos_raw,maxsearcharea,entity,checkfunc)
--
--! @brief find a position suitable around a specific position
--! @ingroup environment
--
--! @param pos_raw position to look at
--! @param maxsearcharea max range to look for suitable position
--! @param entity mob to look for position
--! @param checkfunc function doing check if a pos is ok
--! @return {x,y,z} position found or nil
-------------------------------------------------------------------------------
function environment.get_pos_same_level(pos_raw,maxsearcharea,entity,checkfunc)
	dbg_mobf.environment_lvl2("MOBF: --> get_pos_same_level "
		.. printpos(pos_raw))
	local pos = mobf_round_pos(pos_raw)

	dbg_mobf.environment_lvl3("MOBF: Starting pos is "..printpos(pos)
	.." max search area is "..maxsearcharea)
		local e1 = "|"
	local e2 = "|"
	local e3 = "|"
	local e4 = "|"

	local possible_targets = {}

	--search next position on solid ground
	for search=1, maxsearcharea,1 do

		--find along edge 1
		for current=-search,search,1 do
			local pos_tocheck = { x= pos.x + current,y=pos.y,z=pos.z -search}
			local pos_quality = environment.pos_quality(pos_tocheck,entity)

			local pos_state = checkfunc(pos_quality)
			dbg_mobf.environment_lvl3("MOBF: state of "..printpos(pos_tocheck).." is "
				.. dump(pos_state))

			if pos_state then
				dbg_mobf.environment_lvl3("    -->found good pos")
				table.insert(possible_targets, pos_tocheck)
			else
				e1 = e1.."-|"
			end
		end

		--find along edge 2
		for current=-(search-1),(search-1),1 do
			local pos_tocheck = { x= pos.x + search,y=pos.y,z=pos.z + current}
			local pos_quality = environment.pos_quality(pos_tocheck,entity)

			local pos_state = checkfunc(pos_quality)
			dbg_mobf.environment_lvl3("MOBF: state of "..printpos(pos_tocheck).." is "
				.. dump(pos_state))

			if pos_state then
				dbg_mobf.environment_lvl3("    -->found good pos")
				table.insert(possible_targets, pos_tocheck)
			else
				e2 = e2.. "-|"
			end
		end

		--find along edge 3

		for current=search,-search,-1 do
			local pos_tocheck = { x= pos.x + current,y=pos.y,z=pos.z + search}
			local pos_quality = environment.pos_quality(pos_tocheck,entity)

			local pos_state = checkfunc(pos_quality)
			dbg_mobf.environment_lvl3("MOBF: state of "..printpos(pos_tocheck).." is "
				.. dump(pos_state))

			if pos_state then
				dbg_mobf.environment_lvl3("    -->found good pos")
				table.insert(possible_targets, pos_tocheck)
			else
				e3 = e3.."-|"
			end
		end

		--find along edge 4
		for current=(search-1),-(search-1),-1 do
			local pos_tocheck = { x= pos.x -search,y=pos.y,z=pos.z + current}
			local pos_quality = environment.pos_quality(pos_tocheck,entity)

			local pos_state = checkfunc(pos_quality)
			dbg_mobf.environment_lvl3("MOBF: state of "..printpos(pos_tocheck).." is "
				.. dump(pos_state))

			if pos_state then
				dbg_mobf.environment_lvl3("    -->found good pos")
				table.insert(possible_targets, pos_tocheck)
			else
				e4 = e4.."-|"
			end
		end
	end

--	print("MOBF: Bug !!! didn't find a suitable position to place mob")
--	print("Surrounding of " .. printpos(pos_raw) .. "was:")
--	print(e1)
--	print("          " .. e2)
--	print(e4)
--	print(e3)

	if #possible_targets > 0 then
		local i = math.random(1, #possible_targets)
		dbg_mobf.environment_lvl1("Found " .. #possible_targets
			.. " possible positions, selected: "
			.. i .. ": " .. printpos(possible_targets[i]))
		return possible_targets[i]
	end

	return nil
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] get_suitable_pos_same_level(pos_raw,maxsearcharea,entity)
--
--! @brief find a position suitable around a specific position
--! @ingroup environment
--
--! @param pos_raw position to look at
--! @param maxsearcharea max range to look for suitable position
--! @param entity mob to look for position
--! @param accept_possible return position thats possible only too
--! @return {x,y,z} position found or nil
-------------------------------------------------------------------------------
function environment.get_suitable_pos_same_level(pos_raw,maxsearcharea,entity,accept_possible)

	if accept_possible then
		return environment.get_pos_same_level(pos_raw,maxsearcharea,entity,
				function(quality)
					if entity.data.movement.canfly then
						return environment.evaluate_state(quality,LT_GOOD_FLY_POS)
					else
						return environment.evaluate_state(quality,LT_SAFE_POSSIBLE_EDGE_POS)
					end
				end)
	else
		return environment.get_pos_same_level(pos_raw,maxsearcharea,entity,
				function(quality)
					if entity.data.movement.canfly then
						return environment.evaluate_state(quality,LT_GOOD_FLY_POS)
					else
						return environment.evaluate_state(quality,LT_SAFE_EDGE_POS)
					end
				end)
	end
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] is_media_element(nodename,environment)
--
--! @brief check if nodename is in environment
--! @ingroup environment
--
--! @param nodename name to check
--! @param media environment of mob
--! @return true/false
------------------------------------------------------------------------------
function environment.is_media_element( nodename, media )

	--security check
	if media == false then
		mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!!! no environment specified!")
		return false
	end

	for i,v in ipairs(media) do
		if v == nodename then
			return true
		end
	end

	dbg_mobf.environment_lvl2("MOBF: " .. nodename .. " is not within environment list:")

	for i,v in ipairs(media) do
		dbg_mobf.environment_lvl3("MOBF: \t" .. v)
	end

	return false
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] get_absolute_min_max_pos(env, pos)
--
--! @brief check if nodename is in environment
--! @ingroup environment
--
--! @param env environment mob should be
--! @param pos position it is currently
--! @return { minpos,maxpos }
------------------------------------------------------------------------------
function environment.get_absolute_min_max_pos(env,pos)

	local node = minetest.get_node(pos)

	--if is not within environment it should be return current position
	--as min max
	if environment.is_media_element(node.name,env.media) == false then
		return pos.y,pos.y
	end

	local min_y = env.min_height_above_ground
	local max_y = env.max_height_above_ground


	--a fully generic check isn't possible here so we need to use media
	--specific ways ... it's ugly but works
	if node.name == "air" then
		min_y = min_y + ( pos.y - mobf_surface_distance(pos))
		max_y = max_y + ( pos.y - mobf_surface_distance(pos))
	end

	if node.name == "default:water" or
		node.name == "defailt:water_flowing" then
		-- water mobs do use min/max directly
	end

	if node.name == "default:lava" or
		node.name == "default:lava_flowing" then
		--TODO e.g. lava fish
		--not implemented by now
	end

	return min_y,max_y
end


-------------------------------------------------------------------------------
-- @function [parent=#environment] is_jumpable_surface(name)
--
--! @brief check if name is a surface an mob may jump onto
--! @ingroup environment
--
--! @param name name to check
--! @return true/false
-------------------------------------------------------------------------------
function environment.is_jumpable_surface(name)


	if 	name == "default:dirt" or
		name == "default:dirt_with_grass" or
		name == "default:stone" or
		name == "default:sand" or
		name == "default:clay"
		then
		return true
	end

	dbg_mobf.environment_lvl1("MOBF: is "..name.." a jumpable surface?")
	return false
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] checksurface(pos,surfaces)
--
--! @brief check if a position is suitable for an mob
--! @ingroup environment
--
--! @param pos position to check
--! @param surface surfaces valid
--! @return true on valid surface false if not
-------------------------------------------------------------------------------
function environment.checksurface(pos,surface)

	local pos_below = {x=pos.x,y=pos.y-1,z=pos.z}
	local node_below = minetest.get_node(pos_below)

	--if we couldn't even get the node count as wrong
	if node_below == nil then
		return "wrong_surface"
	end

	--if no surfaces are specified any surface is treated as ok
	if surface == nil then
		if minetest.registered_nodes[node_below.name] ~= nil then
			if minetest.registered_nodes[node_below.name].walkable then
				return "ok",node_below.name
			else
				return "wrong_surface",node_below.name
			end
		else
			dbg_mobf.environment_lvl1("ENV surface check: " .. node_below.name..
				" isn't even a registred node??")
			--not a registred nodename??? count as wrong surface
			return "wrong_surface",node_below.name
		end
	end

	for i,v in ipairs(surface.good) do
		if node_below.name == v then
			return "ok",node_below.name
		end
	end

	if surface.possible ~= nil then
		for i,v in ipairs(surface.possible) do
			if node_below.name == v then
				return "possible_surface",node_below.name
			end
		end
	end

	return "wrong_surface",node_below.name

end

-------------------------------------------------------------------------------
-- @function [parent=#environment] get_min_max_ground_dist(entity)
--
--! @brief calculate absolute minimum and maximum height for a mob
--! @ingroup environment
--
--! @param entity mob to check
--! @return min y val,max y val
-------------------------------------------------------------------------------
function environment.get_min_max_ground_dist(entity)
	local min_ground_distance   = 0
	local max_ground_distance   = 0

	if entity.environment.max_height_above_ground ~= nil then
		max_ground_distance = entity.environment.max_height_above_ground
	end

	if entity.environment.min_height_above_ground ~= nil then
		min_ground_distance = entity.environment.min_height_above_ground
	end

	if entity.data.movement.canfly == nil or
		entity.data.movement.canfly == false then
		max_ground_distance = 1
	end

	return min_ground_distance,max_ground_distance
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] evaluate_pos_media(pos,media)
--
--! @brief check position media quality
--! @ingroup environment
--
--! @param pos position to check
--! @param media media description to check
--!
--! @return 100 = in media 10 collision 0 invalid, nodename
-------------------------------------------------------------------------------
function environment.evaluate_pos_media(pos,media)
	local node_to_check = minetest.get_node(pos)

	if node_to_check == nil then
		mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!!! checking position with invalid node")
		return 0,nil
	end

	if not environment.is_media_element(node_to_check.name,media) then
		return 10,node_to_check
	end

	return 100,node_to_check
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] same_state(state1,state2)
--
--! @brief compare two states
--! @ingroup environment
--
--! @param state1
--! @param state2
--!
--! @return true/false
-------------------------------------------------------------------------------
function environment.same_state(state1,state2)
	environment.is_state(state1)
	environment.is_state(state2)

	if state1.valid == false or
		state2.valid == false then
		return false
	end

	if state1.media_quality ~= state2.media_quality then
		return false
	end

	if state1.geometry_quality ~= state2.geometry_quality then
		return false
	end

	if state1.surface_quality_min ~= state2.surface_quality_min then
		return false
	end

	if state1.surface_quality_max ~= state2.surface_quality_max then
		return false
	end

	if state1.level_quality ~= state2.level_quality then
		return false
	end

	return true
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] compare_state(state1,state2)
--
--! @brief compare state1 and state2
--! @ingroup environment
--
--! @param state1
--! @param state2
--!
--! @return -1, 0, 1
-------------------------------------------------------------------------------
function environment.compare_state(state1,state2)
	environment.is_state(state1)
	environment.is_state(state2)

	local right_better = false
	local left_better = false

	if state1.valid == false and state2.valid == true then
		return 1
	end

	if state1.valid == true and state2.valid == false then
		return -1
	end

	if	state1.media_quality < state2.media_quality or
		state1.geometry_quality < state2.geometry_quality or
		state1.surface_quality_min < state2.surface_quality_min or
		state1.surface_quality_max < state2.surface_quality_max or
		state1.level_quality < state2.level_quality
		then
		right_better = true
	end

	if	state1.media_quality > state2.media_quality or
		state1.geometry_quality > state2.geometry_quality or
		state1.surface_quality_min > state2.surface_quality_min or
		state1.surface_quality_max > state2.surface_quality_max or
		state1.level_quality > state2.level_quality
		then
		left_better = true
	end
	if right_better and not left_better then
		return 1
	end

	if left_better and not right_better then
		return -1
	end

	return 0
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] is_state(state)
--
--
--! @brief assert if state is something else than a state
--! @ingroup environment
-------------------------------------------------------------------------------
function environment.is_state(state)
	mobf_assert_backtrace(state ~= nil)
	mobf_assert_backtrace(type(state) == "table")
	mobf_assert_backtrace(state.valid ~= nil)
	mobf_assert_backtrace(state.media_quality ~= nil)
	mobf_assert_backtrace(state.geometry_quality ~= nil)
	mobf_assert_backtrace(state.center_geometry_quality ~= nil)
	mobf_assert_backtrace(state.surface_quality_min ~= nil)
	mobf_assert_backtrace(state.surface_quality_max ~= nil)
	mobf_assert_backtrace(state.level_quality ~= nil)
	mobf_assert_backtrace(state.surface_quality_center ~= nil)
end
-------------------------------------------------------------------------------
-- @function [parent=#environment] evaluate_state(state,old_state, min_media,
--                     min_geom,min_geom_center,min_min_surface,min_max_surface)
--
--! @brief evaluate a position
--! @ingroup environment
--
-- @param state verify this state
-- @param limits = {
--					old_state if set new state needs to be better or equal to this state only
--					min_media minimum media quality required
--					min_geom minimum geometry quality required
--					min_geom_center minimum geometry quality at center
--					min_min_surface minimum value for minimal surface quality
--					min_max_surface minimum value for maximal surface quality
--					min_center_surface minimum value for surface quality at center
--				   }
--
-- @return true/false
-------------------------------------------------------------------------------
function environment.evaluate_state(state,limits)

	mobf_assert_backtrace(type(limits) == "table")
	environment.is_state(state)

	if state.valid == false then return false end

	if limits.min_media ~= nil and state.media_quality < limits.min_media
		then return false end

	if limits.min_geom_center ~= nil and state.center_geometry_quality < limits.min_geom_center
		then return false end

	if limits.old_state ~= nil then
		if environment.compare_state(state,limits.old_state) == -1 then
			return true
		end
	end

	local retval = true

	if limits.min_geom ~= nil and state.geometry_quality < limits.min_geom
		then retval = false end
	if limits.min_min_surface ~= nil and state.surface_quality_min < limits.min_min_surface
		then retval = false end
	if limits.min_max_surface ~= nil and state.surface_quality_max < limits.min_max_surface
		then retval = false end
	if limits.min_center_surface ~= nil and state.surface_quality_center < limits.min_center_surface
		then retval = false end

	return retval
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] pos_quality(pos,entity)
--
--! @brief check position quality
--! @ingroup environment
--
--! @param pos position to check
--! @param entity mob to check
--! @return position quality
--! {
--!    media_quality             100 = in media
--!                               30 = collision (not passed outside)
--!                               20 = special in_water
--!                               10 = special in_air
--!                                5 = solid
--!                                0 = not evaluated
--!    geometry_quality =        100 = full contact
--!                               60 = partial contact
--!                               30 = no contact
--!                                0 = not evaluated
--!    center_geometry_quality = 100 = contact
--!                               30 = no contact
--!                                0 = not evaluated
--!    surface_quality_min =     100 = all ok
--!                               60 = worst is possible
--!                               30 = worst is wrong
--!                               10 = special "above_water"
--!                                0 = not evaluated
--!    surface_quality_max =     100 = all ok
--!                               60 = best is possible
--!                               30 = best is wrong
--!                               10 = special "above_water"
--!                                0 = not evaluated
--!    surface_quality_center =  100 = ok
--!                               60 = best is possible
--!                               30 = best is wrong
--!                               10 = special "above_water"
--!                                0 = not evaluated
--!    level_quality     =       100 = ok
--!                               60 = above_limit
--!                               30 = below_limit
--!                                0 = not evaluated
--!    valid             =      true = data is valid
--!                            false = no valid data
-------------------------------------------------------------------------------
function environment.pos_quality(pos,entity)
	mobf_assert_backtrace(pos ~= nil)
	mobf_assert_backtrace(entity ~= nil)
	mobf_assert_backtrace(entity.collisionbox ~= nil)
	mobf_assert_backtrace(entity.environment ~= nil)

	local retval = {
					media_quality           =  100,
					geometry_quality        =    0,
					center_geometry_quality =    0,
					surface_quality_min     =  100,
					surface_quality_max     =    0,
					surface_quality_center  =    0,
					level_quality           =  100,
					valid                   = true,

					tostring = function(state)
						environment.is_state(state)

						local retval = "\nState: ".. dump(state.valid) .. "\n"
						retval = retval .."\tmedia_quality: (" .. state.media_quality ..") "
						if state.media_quality == 100 then retval = retval .. "in media" end
						if state.media_quality ==  30 then retval = retval .. "collision" end
						if state.media_quality ==  20 then retval = retval .. "in water" end
						if state.media_quality ==  10 then retval = retval .. "in air" end
						if state.media_quality ==   5 then retval = retval .. "solid" end
						if state.media_quality ==   0 then retval = retval .. "not evaluated" end

						retval = retval .."\n\tgeometry_quality: (" .. state.geometry_quality .. ") "
						if state.geometry_quality == 100 then retval = retval .. "full contact" end
						if state.geometry_quality ==  60 then retval = retval .. "partial contact" end
						if state.geometry_quality ==  30 then retval = retval .. "no contact" end
						if state.geometry_quality ==   0 then retval = retval .. "not evaluated" end

						retval = retval .."\n\tcenter_geometry_quality: (" .. state.center_geometry_quality .. ") "
						if state.center_geometry_quality == 100 then retval = retval .. "contact" end
						if state.center_geometry_quality ==  30 then retval = retval .. "no contact" end
						if state.center_geometry_quality ==   0 then retval = retval .. "not evaluated" end

						retval = retval .."\n\tsurface_quality_min: (" .. state.surface_quality_min ..") "
						if state.surface_quality_min == 100 then retval = retval .. "ok" end
						if state.surface_quality_min ==  60 then retval = retval .. "possible" end
						if state.surface_quality_min ==  30 then retval = retval .. "wrong" end
						if state.surface_quality_min ==  10 then retval = retval .. "above water" end
						if state.surface_quality_min ==   0 then retval = retval .. "not evaluated" end

						retval = retval .."\n\tsurface_quality_max: (" .. state.surface_quality_max .. ") "
						if state.surface_quality_max == 100 then retval = retval .. "ok" end
						if state.surface_quality_max ==  60 then retval = retval .. "possible" end
						if state.surface_quality_max ==  30 then retval = retval .. "wrong" end
						if state.surface_quality_max ==  10 then retval = retval .. "above water" end
						if state.surface_quality_max ==   0 then retval = retval .. "not evaluated" end

						retval = retval .."\n\tsurface_quality_center: (" .. state.surface_quality_center .. ") "
						if state.surface_quality_center == 100 then retval = retval .. "ok" end
						if state.surface_quality_center ==  60 then retval = retval .. "possible" end
						if state.surface_quality_center ==  30 then retval = retval .. "wrong" end
						if state.surface_quality_center ==  10 then retval = retval .. "above water" end
						if state.surface_quality_center ==   0 then retval = retval .. "not evaluated" end

						retval = retval .."\n\tlevel_quality: (" .. state.level_quality .. ") "
						if state.level_quality == 100 then retval = retval .. "ok" end
						if state.level_quality ==  60 then retval = retval .. "above limit" end
						if state.level_quality ==  30 then retval = retval .. "below limit" end
						if state.level_quality ==   0 then retval = retval .. "not evaluated" end
						retval = retval .. "\n"

						return retval
					end,
					shortstring = function(state)
						return dump(state.valid) .. ":" ..
							state.media_quality .. ";" ..
							state.geometry_quality .. "," .. state.center_geometry_quality .. ";" ..
							state.surface_quality_min .. "," .. state.surface_quality_center .. "," ..state.surface_quality_max .. ";" ..
							state.level_quality
					end
				}



	local cornerpositions = {}
	local lastpos = nil -- performance improvement to skip checking same pos multiple times

	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[4] -0.01,y=pos.y,z=pos.z + entity.collisionbox[6] -0.01})
	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[4] -0.01,y=pos.y,z=pos.z + entity.collisionbox[3] +0.01})
	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[1] +0.01,y=pos.y,z=pos.z + entity.collisionbox[6] -0.01})
	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[1] +0.01,y=pos.y,z=pos.z + entity.collisionbox[3] +0.01})

	local min_ground_distance,max_ground_distance = environment.get_min_max_ground_dist(entity)

	--check if mob at pos will be in correct environment
	for i=1,#cornerpositions,1 do
		if not mobf_pos_is_same(lastpos,cornerpositions[i]) then
			local med_quality,node_to_check =
				environment.evaluate_pos_media(cornerpositions[i],
												entity.environment.media)
			--if current result is worse than old one
			if med_quality < retval.media_quality then
				if med_quality == 0 then
					retval.valid = false
				end

				if node_to_check.name == "default:water_source" or
					node_to_check.name == "default:water_flowing" then
					retval.media_quality = 20
				end

				if node_to_check.name == "air" then
					retval.media_quality = 10
					break
				end

				if med_quality < retval.media_quality then
					retval.media_quality = 5
				end
			end
		end
		lastpos = cornerpositions[i]
	end

	--check height level for flying mobs
	if entity.data.movement.canfly == true then
		lastpos = nil
		for i=1,#cornerpositions,1 do
			if not mobf_pos_is_same(lastpos,cornerpositions[i]) then
				local miny,maxy = environment.get_absolute_min_max_pos(entity.environment,cornerpositions[i])

				dbg_mobf.environment_lvl2("MOBF: \tflying mob detected, min: "
					.. miny .. " max: " .. maxy .. " current: " .. pos.y)


				if cornerpositions[i].y < miny then
					retval.level_quality = 30
				end

				if cornerpositions[i].y > maxy then
					retval.level_quality = 60
				end

				if retval.level_quality < 100 then
					break
				end
			end

		lastpos = cornerpositions[i]
		end
	else

		--check geometry and surface quality
		lastpos = nil
		local have_contact    = false
		local have_no_contact = false

		table.insert(cornerpositions,pos)

		for i=1,#cornerpositions,1 do
			if not mobf_pos_is_same(lastpos,cornerpositions[i]) then

				local ground_distance = mobf_ground_distance(cornerpositions[i], entity.environment.media)


				--first check if on surface or not
				if ground_distance <= max_ground_distance then
					local is_center = false
					if mobf_pos_is_same(pos,cornerpositions[i]) then
						retval.center_geometry_quality = 100
						is_center = true
					end
					have_contact = true

					local current_surface,belowname =
						environment.checksurface(cornerpositions[i],entity.environment.surfaces)

					if current_surface == "ok" then
						if retval.surface_quality_max < 100 then
							retval.surface_quality_max = 100
						end

						if is_center then
							retval.surface_quality_center = 100
						end
					end

					if current_surface == "possible_surface" then
						if retval.surface_quality_max < 60 then
							retval.surface_quality_max = 60
						end

						if retval.surface_quality_min > 60 then
							retval.surface_quality_min = 60
						end

						if is_center then
							retval.surface_quality_center = 60
						end
					end

					if current_surface == "wrong_surface" then
						if	belowname == "default:water_source" or
							belowname == "default:water_flowing" then
							if retval.surface_quality_max < 10 then
								retval.surface_quality_max = 10
							end

							if retval.surface_quality_min > 10 then
								retval.surface_quality_min = 10
							end

							if is_center then
								retval.surface_quality_center = 10
							end
						else
							if retval.surface_quality_max < 30 then
								retval.surface_quality_max = 30
							end

							if retval.surface_quality_min > 30 then
								retval.surface_quality_min = 30
							end

							if is_center then
								retval.surface_quality_center = 30
							end
						end
					end
				else
					if mobf_pos_is_same(pos,cornerpositions[i]) then
						retval.center_geometry_quality = 30
					end
					have_no_contact = true
				end
			end

			lastpos = cornerpositions[i]
		end

		if have_contact and not have_no_contact then
			retval.geometry_quality = 100
		end

		if have_contact and have_no_contact then
			retval.geometry_quality = 60
		end

		if not have_contact and have_no_contact then
			retval.geometry_quality = 30
		end
	end

	return retval
end


-------------------------------------------------------------------------------
-- @function [parent=#environment] pos_is_ok(pos,entity) DEPRECATED
--
--! @brief check if a position is suitable for an mob
--! @ingroup environment
--
--! @param pos position to check
--! @param entity mob to check
--! @param dont_do_jumpcheck
--! @return suitability of position for mob values:
--!           -ok                    -@>position is ok
--!           -collision             -@>position is within a node
--!           -collision_jumpable    -@>position is within a node that can be jumped onto
--!           -drop                  -@>position is a drop
--!           -drop_above_water      -@>position is to far above water
--!           -above_water           -@>position is right over water
--!           -in_water              -@>position is within a water node(source or flow)
--!			  -in_air                -@>position is in air
--!           -above_limit           -@>position is above level limit
--!           -below_limit           -@>position is below level limit
--!           -wrong_surface         -@>position is above surface mob shouldn't be
--!           -invalid               -@>unable to check position
-------------------------------------------------------------------------------
function environment.pos_is_ok(pos,entity,dont_do_jumpcheck)

	local min_ground_distance,max_ground_distance = environment.get_min_max_ground_dist(entity)

	local cornerpositions = {}

	table.insert(cornerpositions,pos)
	--read positions at corners
	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[4] -0.01,y=pos.y,z=pos.z + entity.collisionbox[6] -0.01})
	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[4] -0.01,y=pos.y,z=pos.z + entity.collisionbox[3] +0.01})
	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[1] +0.01,y=pos.y,z=pos.z + entity.collisionbox[6] -0.01})
	table.insert(cornerpositions,{x=pos.x + entity.collisionbox[1] +0.01,y=pos.y,z=pos.z + entity.collisionbox[3] +0.01})

	local lastpos = nil

	local retval = "temp_ok"
	
	local min_ground_distance = 33000 -- above max height

	--check if mob at pos will be in correct environment
	for i=1,#cornerpositions,1 do
		if not mobf_pos_is_same(lastpos,cornerpositions[i]) then
			local node_to_check = minetest.get_node(cornerpositions[i])

			if node_to_check == nil then
				mobf_bug_warning(LOGLEVEL_ERROR,"MOBF: BUG!!!! checking position with invalid node")
				retval = "invalid"
				break
			end

			if not environment.is_media_element(node_to_check.name,entity.environment.media) == true then
				dbg_mobf.environment_lvl3("MOBF: " .. i .. ": " ..
					printpos(cornerpositions[i]) .. " -- " .. printpos(pos) ..
					" not within environment")

				if mobf_pos_is_same(pos,cornerpositions[i]) then
					if node_to_check.name == "default:water_source" or
						node_to_check.name == "default:water_flowing" then
						retval = "in_water"
						break
					end

					if node_to_check.name == "air" then
						retval = "in_air"
						break
					end

					--TODO maybe replace by "invalid medium"
				else
					retval = "collision"
				end
			end
			
			min_ground_distance =
				MIN(min_ground_distance,
					mobf_ground_distance(cornerpositions[i],entity.environment.media))
		end
		lastpos = cornerpositions[i]
	end

	--
	if retval == "temp_ok" then
		dbg_mobf.environment_lvl3("MOBF: \tin environment")
		local ground_distance = mobf_ground_distance(pos,entity.environment.media)

		--following return codes are only usefull for non flying
		if entity.data.movement.canfly == nil or
			entity.data.movement.canfly == false then

			if mobf_above_water(pos) then

				if min_ground_distance > max_ground_distance then
					dbg_mobf.environment_lvl2("MOBF: \tdropping above water")
					retval = "drop_above_water"
				end
				dbg_mobf.environment_lvl2("MOBF: \tabove water")
				retval = "above_water"
			end

			if min_ground_distance > max_ground_distance then
				dbg_mobf.environment_lvl2("MOBF: \tdropping "
					.. ground_distance .. " / " .. max_ground_distance)
				retval = "drop"
			else
				dbg_mobf.environment_lvl2("MOBF: \tsurface dependent")
				retval = environment.checksurface(pos,entity.environment.surfaces)
			end
		else
			local miny,maxy = environment.get_absolute_min_max_pos(entity.environment,pos)
			dbg_mobf.environment_lvl2("MOBF: \tflying mob detected, min: "
				.. miny .. " max: " .. maxy .. " current: " .. pos.y)
			if pos.y < miny then
				retval = "below_limit"
			else if pos.y > maxy then
				retval = "above_limit"
			else
				retval = environment.checksurface(pos,entity.environment.surfaces)
			end end
		end
	end

	if retval == "collision" and not dont_do_jumpcheck then
		dbg_mobf.environment_lvl2("MOBF: check if pos is jumpable")
		local upper_pos_state = environment.pos_is_ok({x=pos.x,
																y=pos.y+1,
																z=pos.z},
																entity,true)
		if  upper_pos_state == "ok" then
			retval = "collision_jumpable"
		else
			dbg_mobf.environment_lvl2("MOBF: upper pos state was: " .. upper_pos_state)
		end
	end

	return retval
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] get_default_gravity(pos,environment,canfly)
--
--! @brief get default acceleration depending on mobs medium and pos
--! @ingroup environment
--
--! @param pos position where to check gravity
--! @param media mobs movement medium
--! @param canfly is mob capable of flying?
--! @return y-acceleration
------------------------------------------------------------------------------
function environment.get_default_gravity(pos,media,canfly)

	if pos == nil then
		return nil
	end

	local node = minetest.get_node(pos)

	--if an mob can't fly or isn't within it's medium default acceleration
	-- for it's current medium is applied
	if  canfly == nil or
		canfly == false or
		environment.is_media_element(node.name,media) == false
		then
		if (node.name == "air") then
			return -9.81
		end

		if node.name == "default:water_source" or
			node.name == "default:water_flowing" then
			return -2.5
		end

		if node.name == "default:lava" then
			return 0.1
		end

		--mob is at invalid position thus returning default air acceleration
		return -9.81
	end

	return 0
end


-------------------------------------------------------------------------------
-- @function [parent=#environment] fix_base_pos(entity, middle_to_bottom)
--
--! @brief fix the mobs y position according to model or sprite height
--! @ingroup environment
--
--! @param entity mob to fix base position
--! @param center_to_bottom distance from center of mob to its bottom (absolute value)
--! @return new position set by function
------------------------------------------------------------------------------
function environment.fix_base_pos(entity, center_to_bottom)

	if center_to_bottom > 0.5 then

		local pos = entity.object:getpos()

		local node_pos = minetest.get_node(pos)

		local pos_to_check = {x=pos.x,y=pos.y-center_to_bottom+0.1,z=pos.z}
		local node_pos_check = minetest.get_node(pos_to_check)

		if node_pos ~= nil and
			node_pos_check ~= nil then
			dbg_mobf.environment_lvl3("MOBF: fixing y position / base position required? "
				.. node_pos.name .. " " .. node_pos_check.name)
			if node_pos.name ~= node_pos_check.name then
				local distance_to_ground = mobf_surface_distance(pos)

				pos.y = pos.y + (center_to_bottom - distance_to_ground +0.5)
				dbg_mobf.environment_lvl2("MOBF: fixing y position of " .. entity.data.name
					.. " got distance " .. center_to_bottom .. " moving to " ..printpos(pos))
				entity.object:moveto(pos)
				entity.dynamic_data.spawning.spawnpoint = pos
			end
		end
	end

	return entity.getbasepos(entity)
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] register(name, environment)
--
--! @brief register an environment to mob framework
--! @ingroup environment
--
--! @param name id of environment
--! @param environment description of environment
--! @return true/false succesfully registred environment
-------------------------------------------------------------------------------
function environment.register(name, environment)

	if environment_list[name] ~= nil then
		return false
	end

	environment_list[name] = environment
	return true
end

-------------------------------------------------------------------------------
-- @function [parent=#environment] pos_state_is_impossible(entity,pos)
--
--! @brief checks if a entity can be there (not if it would move there by its own)
--! @ingroup environment
--
--! @param entity entity to check
--! @param pos position to check
--! @return true entity may be there, entity can never be there
-------------------------------------------------------------------------------
function environment.possible_pos(entity,pos)
	local state = environment.pos_is_ok(pos,entity)

	if 	state == "collision" or
		state == "collision_jumpable" or
		state == "invalid" then
		return false
	end

	return true
end

--!@}

dofile (mobf_modpath .. "/environments/general_env_sets.lua")
dofile (mobf_modpath .. "/environments/flight_1.lua")
dofile (mobf_modpath .. "/environments/meadow.lua")
dofile (mobf_modpath .. "/environments/on_ground_1.lua")
dofile (mobf_modpath .. "/environments/on_ground_2.lua")
dofile (mobf_modpath .. "/environments/open_waters.lua")
dofile (mobf_modpath .. "/environments/shallow_waters.lua")
dofile (mobf_modpath .. "/environments/deep_water.lua")
dofile (mobf_modpath .. "/environments/simple_air.lua")