--
-- federung
--
-- author: upsidedown & mogli
-- date: 16.03.2014
-- v2:   05.11.2015
-- v3:   08.01.2017 - convert to ls17 by DerFreddy7




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(savegame)
    
    self.federung = {}
    local i=0
    while true do
        local areaKey = string.format("vehicle.federung.part(%d)", i)
        if not hasXMLProperty(self.xmlFile, areaKey) then            
            break
        end
        
        local part = {}
        
        part.object = Utils.indexToObject(self.components, getXMLString(self.xmlFile, areaKey .. "#index"))    
        
        local x,y,z = getTranslation(part.object)
        
        part.depthNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, areaKey .. "#depthIndex"))    
        part.depth     = Utils.getNoNil(getXMLFloat(self.xmlFile, areaKey .. "#depth"),0)
        
        part.damp      = getXMLFloat(self.xmlFile, areaKey .. "#damper")/100
        part.spring    = getXMLFloat(self.xmlFile, areaKey .. "#spring")/10000
        
        part.minY      = Utils.getNoNil(getXMLFloat(self.xmlFile, areaKey .. "#minY",-0.1))
        part.maxY      = Utils.getNoNil(getXMLFloat(self.xmlFile, areaKey .. "#maxY", 0.1))
        part.lastV_Y   = 0
        
        part.y0        = getXMLFloat(self.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(self.xmlFile, areaKey .. "#bounceY"),0)
        part.bounceX1 = Utils.getNoNil(getXMLFloat(self.xmlFile, areaKey .. "#bounceX1"),0)
        part.bounceX2 = Utils.getNoNil(getXMLFloat(self.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