//
//  Resampler.hpp
//  SwitchboardSDK
//
//  Created by Nádor Iván on 2022. 03. 03..
//  Copyright © 2022. Synervoz Inc. All rights reserved.
//

#pragma once

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

namespace switchboard {

struct SWITCHBOARDSDK_EXPORT ResamplerInternals;

/**
 * Resampler class.
 * @brief Resamples audio data from one sample rate to another.
 */
class SWITCHBOARDSDK_EXPORT Resampler {
public:
    /**
     * @brief Creates a Resampler instance.
     *
     * @param maxNumberOfChannels Maximum number of channels that is supported by the resampler, determines buffer sizes.
     * @param maxNumberOfFrames Maximum number of frames that is supported by the resampler, determines buffer sizes.
     */
    Resampler(uint maxNumberOfChannels = constants::STEREO, uint maxNumberOfFrames = constants::MAX_NUMBER_OF_FRAMES);

    /**
     * @brief Resampler destructor.
     */
    ~Resampler();

    /**
     *  @brief Resamples an input buffer of sampling rate N, to an output buffer of sampling rate M.
     *
     *  @param input The short input buffer that needs to be resampled. Either interleaved stereo or mono.
     *  @param output The resampled output buffer. Interleaved stereo. Should not be the same as output.
     *  @param inputSampleRate The sample rate of the input buffer.
     *  @param outputSampleRate The target sample rate of the output buffer.
     *
     *  @return The number of frames in the resampled output buffer.
     */
    uint process(
        const AudioBuffer<int16>& input,
        AudioBuffer<int16>& output,
        const uint inputSampleRate,
        const uint outputSampleRate
    );

    /**
     *  @brief Resamples an input buffer of sampling rate N, to an output buffer of sampling rate M.
     *
     *  @param input The float input buffer that needs to be resampled. Either interleaved stereo or mono.
     *  @param output The resampled output buffer. Interleaved stereo. Should not be the same as output.
     *  @param inputSampleRate The sample rate of the input buffer.
     *  @param outputSampleRate The target sample rate of the output buffer.
     *
     *  @return The number of frames in the resampled output buffer.
     */
    uint process(
        const AudioBuffer<float>& input,
        AudioBuffer<float>& output,
        const uint inputSampleRate,
        const uint outputSampleRate
    );

    /**
     *  @brief Resamples an input buffer of sampling rate N, to an output buffer of sampling rate M.
     *
     *  @param input The short input buffer that needs to be resampled. Either interleaved stereo or mono.
     *  @param output The resampled output buffer. Interleaved stereo. Should not be the same as output.
     *  @param inputSampleRate The sample rate of the input buffer.
     *  @param outputSampleRate The target sample rate of the output buffer.
     *  @param numberOfInputFrames Number of frames in the input.
     *  @param maxNumberOfOutputFrames Max number of frames in the output. Derived from the sample rate ratio.
     *  @param numChannels Indicates the number of channels.
     *  @param endOfInput Optional - Signals the end of input in a stream of input buffers. Resampler needs to be reset after it is set to true.
     *
     *  @return The number of frames in the resampled output buffer.
     */
    uint process(
        const int16* input,
        float* output,
        uint inputSampleRate,
        uint outputSampleRate,
        uint numberOfInputFrames,
        uint maxNumberOfOutputFrames,
        uint numChannels,
        bool endOfInput = false
    );

    /**
     *  @brief Resamples an input buffer of sampling rate N, to an output buffer of sampling rate M.
     *
     *  @param input The float input buffer that needs to be resampled. Either interleaved stereo or mono.
     *  @param output The resampled output buffer. Interleaved stereo. Should not be the same as output.
     *  @param inputSampleRate The sample rate of the input buffer.
     *  @param outputSampleRate The target sample rate of the output buffer.
     *  @param numberOfInputFrames Number of frames in the input.
     *  @param maxNumberOfOutputFrames Max number of frames in the output. Derived from the sample rate ratio.
     *  @param numChannels Indicates the number of channels.
     *  @param endOfInput Optional - Signals the end of input in a stream of input buffers. Resampler needs to be reset after it is set to true.
     *
     *  @return The number of frames in the resampled output buffer.
     */
    uint process(
        const float* input,
        float* output,
        uint inputSampleRate,
        uint outputSampleRate,
        uint numberOfInputFrames,
        uint maxNumberOfOutputFrames,
        uint numChannels,
        bool endOfInput = false
    );

    /**
     *  @brief Resamples an input buffer of sampling rate N, to an output buffer of sampling rate M.
     *
     *  @param input The float input buffer that needs to be resampled. Either interleaved stereo or mono.
     *  @param output The resampled output buffer. Interleaved stereo. Should not be the same as output.
     *  @param inputSampleRate The sample rate of the input buffer.
     *  @param outputSampleRate The target sample rate of the output buffer.
     *  @param numberOfInputFrames Number of frames in the input.
     *  @param maxNumberOfOutputFrames Max number of frames in the output. Derived from the sample rate ratio.
     *  @param numChannels Indicates the number of channels.
     *  @param endOfInput Optional - Signals the end of input in a stream of input buffers. Resampler needs to be reset after it is set to true.
     *  @param numberOfInputFramesUsed The number of frames used from the input buffer to generate the output.
     *  @param numberOfOutputFramesGenerated The number of frames written to the output buffer.
     *
     */
    void process(
        const float* input,
        float* output,
        uint inputSampleRate,
        uint outputSampleRate,
        uint numberOfInputFrames,
        uint maxNumberOfOutputFrames,
        uint numChannels,
        bool endOfInput,
        uint& numberOfInputFramesUsed,
        uint& numberOfOutputFramesGenerated
    );

    /**
     *  @brief Resamples an input buffer of sampling rate N, to an output buffer of sampling rate M.
     *  This method can take an input bigger than the maximum amount a ring buffer can hold and will iterate through it.
     *  **Non realtime - Will allocate memory**
     *
     *  @param input The float input vector that needs to be resampled. Either interleaved stereo or mono.
     *  @param output The resampled output vector. Interleaved stereo. This vector is allocated inside the function.
     *  @param inputSampleRate The sample rate of the input buffer.
     *  @param outputSampleRate The target sample rate of the output buffer.
     *  @param numChannels Indicates the number of channels.
     *
     *  @return The number of frames in the resampled output buffer.
     */
    uint process(
        const std::vector<float>& input,
        std::vector<float>& output,
        const uint inputSampleRate,
        const uint outputSampleRate,
        uint numChannels
    );

    /**
     *  @brief Resets the resampler after endOfInput is set to true in the process method. Needs to be called before a new stream of data is input to the process method.
     */
    void reset();

    /**
     *  @brief Retrieves the number of buffered frames currently in the Resampler.
     *
     *  The count returned by this method is indicative of the internal state of the Resampler's
     *  buffering system and can be used for diagnostics or managing the flow of data through the
     *  Resampler.
     *
     *  @return The number of buffered frames in the Resampler.
     */
    int getNumberOfBufferedFrames() const;

private:
    ResamplerInternals* internals;

    buffer::AudioData<float> tempInputBuffer;
    buffer::AudioData<float> tempOutputBuffer;
    audio::RingBuffer<float> ringBuffer;
};

}
