local M = {}

local loadedSounds = {}

local engineSound = nil
local windSound = nil
local cameraNode = 0

local function nop()
end

local function playSoundOnceAtNode(soundName, nodeID, volume)
    local snd = gameEngine:playSFXOnce(soundName)
    snd:setVolume(volume)
    obj:attachSFXNode(snd, nodeID)
end

local wheelsSounds = nil

local function playWheelSound(wheelID, soundName, vol, pitch)
    ws = wheelsSounds[wheelID][soundName]
    if vol < 0.1 then vol = 0 end
    if vol == 0 then 
        pitch = 0
    else
        if math.abs(vol - ws.lastVol) / vol < 0.05 and math.abs(pitch - ws.lastPitch) < 0.05 then return end
    end
    if vol == ws.lastvol then return end
    ws.lastVol = vol
    ws.lastPitch = pitch
    ws.obj:setVolume(vol)
    ws.obj:setPitch(pitch)
end

local function update(dt)
    -- engine
    if v.data.engine ~= nil and v.data.engine.maxRPM ~= nil and engineSound ~= nil then
        local enginePitch = (drivetrain.rpm / v.data.engine.maxRPM) * 2
        engineSound:setPitch(enginePitch)
        engineSound:setVolume((0.45 + (drivetrain.throttle * 0.3)) + (enginePitch * 0.12))
    end
    
    -- wind

    local speed = obj:getVelocity():length() -- speed
    local vol = (speed * speed * 0.001)
    local pitch = speed / 60
    if vol > 1 then vol = 1 end
    windSound:setVolume(vol)
    windSound:setPitch(pitch)

    -- wheels
    for wi,wd in pairs(drivetrain.wheelInfo) do
        local w = wd.obj
        local slip = wd.lastSlip
        local slipEnergy = wd.slipEnergy

        local skidVol = 0
        local skidPitch = 0
        local rollVol = 0
        local rollPitch = 0

        if wd.contactMaterialID1 == 10 and wd.contactMaterialID2 == 4 and wd.contactDepth == 0 then
            if slipEnergy > 6 then
                skidVol = math.min(slipEnergy * 0.005 + 0.3, 1)
                skidPitch = math.max(slip - 4, 0) * 0.05 + 0.8
            else
                local wheelSpeed = math.abs(w.angularVelocity * wd.radius)
                rollVol = math.min(math.sqrt(wheelSpeed * 0.018), 1)
                rollPitch = wheelSpeed * 0.125
            end
        end
        
        playWheelSound(wi, "SkidTestSound", skidVol, skidPitch)
        playWheelSound(wi, "RollingTestSound", rollVol, rollPitch)
    end
end

local function addWheelSound(wheelID, wd, soundName)
    if wheelsSounds == nil then wheelsSounds = {} end
    if wheelsSounds[wheelID] == nil then wheelsSounds[wheelID] = {} end
    
    local s = gameEngine:createSFXSource(soundName)
    if s == nil then 
        M.update = nop
        M.playSoundOnceAtNode = nop
        return nil 
    end
    s:setVolume(0)
    s:setPitch(1)
    s:play(-1)
    obj:attachSFXNode(s, wd.node1)
    wheelsSounds[wheelID][soundName] = {obj = s, lastVol = 0, lastPitch = 0}
    table.insert(loadedSounds, s)
end

local function createSFXSource(SFXname)
    local var = gameEngine:createSFXSource(SFXname)
    if var == nil then 
        M.update = nop
        M.playSoundOnceAtNode = nop
        return nil 
    end
    var:setVolume(0)
    var:play(-1)
    obj:attachSFXNode(var, cameraNode)
    table.insert(loadedSounds, var)
    return var
end

local function init_replaced()
    if v.data.camerasInternal ~= nil then
        local k, v = next(v.data.camerasInternal)
        cameraNode = v.camNodeID
    end

    if engineSound == nil then
        engineSound = createSFXSource(v.data.engine.engineSound or "EngineTestSound")
        print(tostring(v.data.engine.engineSound))
    end

    if windSound == nil then
        windSound = createSFXSource("WindTestSound")
    end    

    if wheelsSounds == nil then
        for wi,wd in pairs(drivetrain.wheels) do
            addWheelSound(wi, wd, "RollingTestSound")
            addWheelSound(wi, wd, "SkidTestSound")
        end
    end
end

local function destroy()
    for k,v in pairs(loadedSounds) do
        if v and v:isValid() then
            gameEngine:deleteSFXSource(v, true)
            v:destroy()
        end
    end
    loadedSounds = {}
    wheelsSounds = nil
    engineSound = nil
    windSound = nil
end

local function init()
    init_replaced()
    --sounds.destroy()
    sounds.update = update
    sounds.destroy = destroy
end

M.destroy = destroy
M.init = init
return M