
function aom_vehicleapi.stop_all_sounds(self)
    for i, def in pairs(self._aomv_sound_data or {}) do
        if def.id then
            core.sound_stop(def.id)
            def.id = nil
        end
    end
end

function aom_vehicleapi.fade_all_sounds(self, step)
    for i, def in pairs(self._aomv_sound_data or {}) do
        if def.id then
            core.sound_fade(def.id, step, 0)
            def.id = nil
        end
    end
end

function aom_vehicleapi.do_sound(self, dtime, parent)
    self._sound_tick = (self._sound_tick or 0) - dtime
    local do_refresh = false
    if self._sound_tick < 0 then
        self._sound_tick = self._sound_tick + 0.5
        do_refresh = true
    end

    -- DEF is READ ONLY
    for sound_name, DEF in pairs(self._aomv_sounds or {}) do repeat
        if self._aomv_sound_data == nil then self._aomv_sound_data = {} end
        local si = self._aomv_sound_data[sound_name]
        if not si then si = {}; self._aomv_sound_data[sound_name] = si end
        if not DEF.auto_loop then break end
        si.time = (si.time or 0) + dtime
        if si.time * (DEF.pitch or 1) > DEF.length then
            if si.id ~= nil then
                core.sound_fade(si.id, DEF.fade or 0.1, 0.0)
                si.id = nil
            end
            si.time = 0
            -- core.log(core.colorize("#09f", "fading out .. " .. sound_name))
        end
        if si.id == nil then
            do_refresh = true
            si.id = core.sound_play(DEF.name, {
                gain = 0.00001,
                pitch = DEF.pitch or 1,
                object = self.object,
                loop = true,
            })
            si.time = 0
            -- core.log(core.colorize("#f0f", "fading IN .. " .. sound_name))
        end

        if do_refresh then
            DEF.on_refresh(parent or self, si, DEF)
        end
    until true end
end

function aom_vehicleapi.do_footstep_sound(self, playername, dtime)
    local player = core.get_player_by_name(playername)
    if not player then return end
    local pi = self._aomv_pl[playername]
    local pos = player:get_pos()
    if player:get_attach() then
        local ent = player:get_attach():get_luaentity()
        if ent._get_real_target_pos then
            pos = ent._get_real_target_pos(ent)
        end
    end
    local rpos = pos - self.object:get_pos()
    rpos = aom_vehicleapi.rotate_point(rpos, -aom_vehicleapi.get_actual_yaw(self))
    rpos.y = rpos.y - aom_vehicleapi.get_platform_y(self, player, pi)

    pi.since_last_step = (pi.since_last_step or 0) + dtime

    if (math.abs(rpos.y) > 0.3) then return end

    -- get the relative position on the ship
    if pi.last_rel_pos == nil then pi.last_rel_pos = rpos end
    local stride = (self._footstep_stride or 1.6)
    local speed = player:get_physics_override().speed
    if speed < 0.1 then speed = 0.1 end

    -- when you start walking after pausing, play immediately
    if (pi.since_last_step > 1) then
        pi.footstep_stride = stride - 0.1
    end

    local moved_dist = vector.distance(rpos, pi.last_rel_pos)
    pi.footstep_stride = (pi.footstep_stride or stride - 0.0001) + moved_dist

    local should_step = false
    if pi.footstep_stride > stride then
        should_step = true
    end

    should_step = should_step and pi.is_in_poly and (math.abs(rpos.y) < 0.1)

    if should_step then
        pi.since_last_step = 0
        pi.footstep_stride = pi.footstep_stride % stride
        local spec = self._aomv_sounds.footstep
        core.sound_play(spec.name, {
            gain = spec.gain,
            pitch = spec.pitch,
            object = player,
        })
    end

    pi.last_rel_pos = rpos
end

function aom_vehicleapi.do_footstep_sound_for_all_players(self, dtime)
    if self._aomv_sounds == nil then return end
    if self._aomv_sounds.footstep == nil then return end
    if self._aomv_pl == nil then self._aomv_pl = {} end
    for playername, pi in pairs(self._aomv_pl) do
        aom_vehicleapi.do_footstep_sound(self, playername, dtime)
    end
end

function aom_vehicleapi.add_sound_player(self, pos, sounds)
    local spos = self.object:get_pos()
    local obj = core.add_entity(spos + pos, "aom_vehicleapi:sound_ENTITY")
    local ent = obj and obj:get_luaentity()
    if ent then
        ent._aomv_sounds = sounds
        ent._rpos = pos
        ent._parent = self
    end
end

aom_vehicleapi.sound_player_entity = {
    initial_properties = {
        visual = "mesh",
        mesh = "blank.obj",
        textures = {"blank.png"},
        -- use_texture_alpha = true,
        stepheight = 0,
        physical = false,
        collide_with_objects = false,
        pointable = false,
        static_save = false,
    },
    _aomv_sounds = {},
    _iterate_all = function(self, dtime)
        aom_vehicleapi.do_sound(self, dtime, self._parent)
    end,
    _add_sound = function(self, def)
        table.insert(self._aomv_sounds, def)
    end,
    on_step = function(self, dtime)
        if (not self._parent) or (self._parent.object:get_pos() == nil) then self.object:remove(); return end
        local parent_yaw = aom_vehicleapi.get_actual_yaw(self._parent)
        local parent_pos = self._parent.object:get_pos()
        local target_pos = aom_vehicleapi.rotate_point(self._rpos, parent_yaw) + parent_pos
        self.object:move_to(target_pos)
        self._iterate_all(self, dtime)
    end,
    on_deactivate = function(self, removal)
        if removal then
            aom_vehicleapi.fade_all_sounds(self, 0.2)
        end
    end,
}

core.register_entity("aom_vehicleapi:sound_ENTITY", aom_vehicleapi.sound_player_entity)
