//
//  AudioRingBuffer.hpp
//  SwitchboardSDK
//
//  Created by Nádor Iván on 2022. 09. 06..
//

#pragma once

#include <audio/RingBuffer.hpp>
#include <buffer/AudioData.hpp>
#include <memory>
#include <switchboard/export.h>
#include <switchboard_core/AudioBuffer.hpp>
#include <switchboard_core/SwitchboardCore.hpp>
#include <vector>

namespace switchboard {

/**
 * AudioRingBuffer class.
 * @brief Class providing ring buffer functionality specialized for audio (most importantly multichannel functionality).
 */
template <typename T>
class SWITCHBOARDSDK_EXPORT AudioRingBuffer {
public:
    /**
     * @brief Creates an AudioRingBuffer instance. Allocates underlying buffer, constructor not suitable for real-time use.
     *
     * @param numberOfFrames The number of frames that the audio ring buffer can hold.
     * @param numberOfChannels The number of channels that the audio ring buffer can hold.
     * @param isInterleaved Indicates whether the audio ring buffer should hold interleaved data.
     */
    AudioRingBuffer(uint numberOfFrames, uint numberOfChannels, bool isInterleaved);

    /**
     * @brief Creates an AudioRingBuffer instance. Does not allocate underlying buffer, suitable for real-time use.
     * @details Does not delete the buffer on destruction. Make sure to handle deletion.
     *
     * @param numberOfFrames The number of frames that the audio ring buffer can hold.
     * @param numberOfChannels The number of channels that the audio ring buffer can hold.
     * @param isInterleaved Indicates whether the audio ring buffer should hold interleaved data.
     * @param buffer The underlying buffer(s) the ring buffer is operating on.
     */
    AudioRingBuffer(uint numberOfFrames, uint numberOfChannels, bool isInterleaved, buffer::AudioData<T>& buffer);

    /**
     * @brief Move constructor.
     */
    AudioRingBuffer(AudioRingBuffer&&) = default;

    /**
     * @brief Move assignment operator.
     */
    AudioRingBuffer& operator=(AudioRingBuffer&&) = default;

    /**
     * @brief Clears all data in the audio ring buffer.
     */
    void clear();

    /**
     * @brief Returns the maximum number of frames that can be added to the audio ring buffer.
     *
     * @returns The number of frames that can be produced.
     */
    [[nodiscard]] uint getAvailableFramesToProduce() const;

    /**
     * @brief Adds frames to the audio ring buffer.
     *
     * @param sourceBuffer The buffer where the frames are copied from.
     * @param numberOfFrames The number of frames that should be added to the audio ring buffer.
     */
    void produce(const AudioBuffer<T>& sourceBuffer, uint numberOfFrames);

    /**
     * @brief Returns the maximum number of items that can be read from the ring buffer.
     *
     * @returns The number frames that can be consumed.
     */
    uint getAvailableFramesToConsume() const;

    /**
     * @brief Reads items from the ring buffer.
     *
     * @param destinationBuffer [inout] The buffer where the read frames will be copied to.
     * @param numberOfFrames The number of frames that should be consumed.
     */
    void consume(AudioBuffer<T>& destinationBuffer, uint numberOfFrames);

    /**
     * @brief Reads items from the ring buffer without updating the read pointer.
     *
     * @param destinationBuffer [inout] The buffer where the read frames will be copied to.
     * @param numberOfItems The number of items that should be consumed.
     */
    void read(AudioBuffer<T>& destinationBuffer, uint numberOfItems);

    /**
     * @brief Discards items from the ring buffer. Steps the read pointer without reading the items.
     *
     * @param numberOfItems The number of items that should be discarded.
     */
    void discard(uint numberOfItems);

private:
    std::vector<std::unique_ptr<audio::RingBuffer<T>>> bufferContainer;
    uint numberOfChannels;
    bool isInterleaved;
};

}
