// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only

#ifndef QAUDIOENGINE_P_H
#define QAUDIOENGINE_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists for the convenience
// of other Qt classes.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <QtSpatialAudio/qaudioengine.h>
#include <QtMultimedia/private/qaudiosystem_p.h>
#include <QtGui/qvectornd.h>
#include <QtCore/private/qobject_p.h>

#include <optional>

namespace vraudio {
class ResonanceAudio;
} // namespace vraudio

QT_BEGIN_NAMESPACE

namespace QSpatialAudioPrivate {
class QSpatialAudioPlaybackState;
} // namespace QSpatialAudioPrivate

class QAmbientSoundPrivate;
class QAudioRoom;
class QAudioListener;

class QAudioEnginePrivate : public QObjectPrivate
{
public:
    Q_DECLARE_PUBLIC(QAudioEngine)

    static QAudioEnginePrivate *get(QAudioEngine *engine)
    {
        return engine ? static_cast<QAudioEnginePrivate *>(engine->d_func()) : nullptr;
    }

    explicit QAudioEnginePrivate(int sampleRate);
    ~QAudioEnginePrivate() override;

    int sampleRate() const { return m_sampleRate; }
    static constexpr QtMultimediaPrivate::NativePeriodFrames framesPerBuffer{ 128 };

    void setDistanceScale(float scale);
    float distanceScale() const;

    // resonanceAudio access
    void setMasterVolume(float);
    float masterVolume() const;
    float m_masterVolume = 1.f;

    // Listener position
    // if set to nullopt, no QAudioListener is registered
    void setListenerPosition(std::optional<QVector3D>);
    std::optional<QVector3D> listenerPosition() const { return m_position; }
    void setListenerRotation(const QQuaternion &);

    // room effects
    void setRoomEffectsEnabled(bool);
    bool roomEffectsEnabled() const;

    // output mode
    virtual void setOutputMode(QAudioEngine::OutputMode);
    QAudioEngine::OutputMode outputMode() const;

    // rooms
    void addRoom(QAudioRoom *);
    void removeRoom(QAudioRoom *);
    QAudioRoom *currentRoom() const;
    void updateRooms();
    virtual void updateRoomEffects() = 0;

    virtual void start() = 0;
    virtual void stop() = 0;
    virtual void setPaused(bool) = 0;
    virtual bool isPaused() const = 0;
    virtual void setOutputDevice(const QAudioDevice &) = 0;
    virtual QAudioDevice outputDevice() const = 0;

    virtual void addSound(QAmbientSoundPrivate *) = 0;
    virtual void removeSound(QAmbientSoundPrivate *) = 0;
    using SharedPlaybackState = std::shared_ptr<QSpatialAudioPrivate::QSpatialAudioPlaybackState>;
    virtual void setSoundPlaybackData(QAmbientSoundPrivate *, SharedPlaybackState) = 0;

protected:
    struct SmallestRoomForListenerResult
    {
        QAudioRoom *room;
        float volume;
    };

    SmallestRoomForListenerResult findSmallestRoomForListener(QSpan<QAudioRoom *> rooms) const;

private:
    const int m_sampleRate = 44100;

    // Resonance Audio uses meters internally, while Qt Quick 3D and our API uses cm by
    // default. To make things independent from the scale setting, we store all distances in
    // meters internally and convert in the setters and getters.
    float m_distanceScale = 0.01f;

    std::optional<QVector3D> m_position;
    QAudioEngine::OutputMode m_outputMode = QAudioEngine::Surround;

    std::vector<QAudioRoom *> rooms;
    QAudioRoom *m_currentRoom = nullptr;

public:
    const std::shared_ptr<vraudio::ResonanceAudio> resonanceAudio;
};

QT_END_NAMESPACE

#endif
