--
-- federung
--
-- author: upsidedown & mogli
-- date: 16.03.2014
-- v2:   05.11.2015

-- NO RELEASE CANDIDATE!


federung = {}
federung.debugLevel = 0

addConsoleCommand("FederungDebug", "Debug output of federung.lua", "FederungDebug", federung)
function federung:FederungDebug(level)
	print("FederungDebug "..tostring(level))
	if level == nil then
		federung.debugLevel = 0
	else
		federung.debugLevel = tonumber(level)
	end
end

function federung.prerequisitesPresent(specializations)
	return true --SpecializationUtil.hasSpecialization(Steerable, specializations)
end

function federung:load(xmlFile)
	
	self.federung = {}
	local i=0
    while true do
        local areaKey = string.format("vehicle.federung.part(%d)", i)
        if not hasXMLProperty(xmlFile, areaKey) then			
            break
        end
		
        local part = {}
		
		part.object = Utils.indexToObject(self.components, getXMLString(xmlFile, areaKey .. "#index"))	
		
		local x,y,z = getTranslation(part.object)
		
		part.depthNode = Utils.indexToObject(self.components, getXMLString(xmlFile, areaKey .. "#depthIndex"))	
		part.depth     = Utils.getNoNil(getXMLFloat(xmlFile, areaKey .. "#depth"),0)
		
		part.damp      = getXMLFloat(xmlFile, areaKey .. "#damper")/100
		part.spring    = getXMLFloat(xmlFile, areaKey .. "#spring")/10000
		
		part.minY      = Utils.getNoNil(getXMLFloat(xmlFile, areaKey .. "#minY",-0.1))
		part.maxY      = Utils.getNoNil(getXMLFloat(xmlFile, areaKey .. "#maxY", 0.1))
		part.lastV_Y   = 0
		
		part.y0        = getXMLFloat(xmlFile, areaKey .. "#y0")
		if part.y0 == nil then
			if part.depthNode ~= nil then
				part.y0 = y+part.minY
			else
				part.y0 = y
			end
		end
		part.newY = part.y0
		part.yd   = part.y0
		
		part.bounceY  = Utils.getNoNil(getXMLFloat(xmlFile, areaKey .. "#bounceY"),0)
		part.bounceX1 = Utils.getNoNil(getXMLFloat(xmlFile, areaKey .. "#bounceX1"),0)
		part.bounceX2 = Utils.getNoNil(getXMLFloat(xmlFile, areaKey .. "#bounceX2"),2 * part.bounceX1)
        i = i + 1
		self.federung[i] = part		
    end
	
end

function federung:delete()
	
end

function federung:mouseEvent(posX, posY, isDown, isUp, button)
end

function federung:keyEvent(unicode, sym, modifier, isDown)
end

function federung:update(dt)
		
end

function federung:updateTick(dt)

	local debugOut = ""
	
	if self:getIsActive() then
		for i,part in pairs(self.federung) do			
			local x,y,z = getTranslation(part.object)
			local wx,wy,wz = getWorldTranslation(part.object)
			
			-- w/o depth node bounce onyl if lowered
			local bounceIt = self:isLowered()
			
			-- adjust depth 
			if part.depthNode ~= nil then
				local ty = getTerrainHeightAtWorldPos( g_currentMission.terrainRootNode, wx, wy, wz ) + part.depth
				local _,hy,_ = getWorldTranslation(part.depthNode)	
				
				-- adjust depth but without spring and damper effects => reset to yd!!!
				if math.abs( ty-hy ) > 0.001 then
					setTranslation(part.object,x,part.yd,z)
					_,hy2,_ = getWorldTranslation(part.depthNode)	
					part.yd  = Utils.clamp( part.yd + ty-hy2, part.y0+part.minY, part.y0+part.maxY )
					setTranslation(part.object,x,y,z)
					_,hy,_ = getWorldTranslation(part.depthNode)	
					
					if federung.debugLevel > 2 or ( federung.debugLevel > 1 and i <=1 ) then
						debugOut = debugOut .. string.format(" %3.3f %3.3f %3.3f %1.3f",hy,ty,hy2,y-part.yd)
					end
				end
				
				-- let it bounce when it's deep enough
				bounceIt = ( hy+hy <= ty+ty + part.bounceY )
				
				if federung.debugLevel > 2 or ( federung.debugLevel > 1 and i <=1 ) then
					if bounceIt then
						debugOut = debugOut .."?"
					else
						debugOut = debugOut .."."
					end
				end
			end
			
			-- bounce it
			if bounceIt and part.bounceY > 0 and part.bounceX2 > 0 then
				local d = 0
				if part.bounceDist == nil then
					d = 0
				else
					d = Utils.vector2LengthSq( part.bounceWX - wx, part.bounceWZ - wz )
				end
				
				if part.bounceDist == nil or d > part.bounceDist then
					if part.bounceDist ~= nil then
						part.newY  = part.newY + part.bounceY
						-- no spring and damper this time
						part.lastY = nil
						if federung.debugLevel > 0 then
							debugOut = debugOut .."!"
						end
					end
					
					part.bounceWX   = wx
					part.bounceWZ   = wz
					part.bounceDist = part.bounceX1 + math.random() * ( part.bounceX2 - part.bounceX1 )
					part.bounceDist = part.bounceDist * part.bounceDist
				end			
			elseif part.bounceDist ~= nil then
				d = Utils.vector2LengthSq( part.bounceWX - wx, part.bounceWZ - wz )
				if d > part.bounceDist then
					part.bounceDist = nil
				end
			end
			
			-- apply translation now
			part.newY = Utils.clamp(part.newY, part.y0+part.minY, part.y0+part.maxY)
			if math.abs( y - part.newY ) > 0.001 then 
				y = part.newY
				setTranslation(part.object,x,y,z)
				if federung.debugLevel > 1 and i <=1 then
					debugOut = debugOut .. string.format( " %1.4f %1.4f", part.newY-part.y0, y-part.newY )
				end
				_,wy,_ = getWorldTranslation(part.object)
			end
	
			-- get world translation to calculate movement from spring and damper
			
			if part.lastY == nil then
				part.lastY = wy
			end
				
			local dy = wy - part.lastY
			
			--reset to original coordinates:
			part.newY = y - dy
			part.newY = Utils.clamp(part.newY, part.y0+part.minY, part.y0+part.maxY)
			
			--lets do some elementary physics:			
			local Fspring = -part.spring*(part.newY-part.yd)
			local Fdamp = -part.damp*part.lastV_Y
				
			local v = part.lastV_Y + (Fspring+Fdamp)*dt
			part.lastV_Y = v
				
			part.newY = part.newY + v*dt			
			part.newY = Utils.clamp(part.newY, part.y0+part.minY, part.y0+part.maxY)
			
			-- apply translation now
			if math.abs( y - part.newY ) > 0.001 then
				setTranslation(part.object,x,part.newY,z)
				if federung.debugLevel > 1 and i <=1 then
					debugOut = debugOut .. string.format( " %1.4f %1.4f", part.newY-part.y0, y-part.newY )
				end
				_,wy,_ = getWorldTranslation(part.object)
			end
			part.lastY = wy		
		end
		
		if federung.debugLevel > 0 and debugOut ~= "" then
			print(debugOut)
		end
	end
end


function federung:draw()
	
end


function federung:onDeactivate()
	
end