#include "IoContext.h"
#include "SpeexDsp.h"
#include "WebRtcAecm.h"
#include "api/audio/echo_canceller3_config.h"
#include "api/audio/echo_canceller3_factory.h"
#include "main.h"
#include "modules/audio_processing/aec3/echo_canceller3.h"
#include "modules/audio_processing/ns/noise_suppressor.h"
#include <boost/asio/post.hpp>
#include <sstream>

class ProcessFileTaskPrivate {
public:
    void initialize(int sampleRate, int channels, int period) {
        std::unique_ptr<webrtc::EchoCanceller3Factory> factory = std::make_unique<webrtc::EchoCanceller3Factory>();
        echoCanceller = factory->Create(sampleRate, channels, channels);
        nearendBuffer = std::make_unique<webrtc::AudioBuffer>(sampleRate, channels, sampleRate, channels, sampleRate, channels);
        farendBuffer = std::make_unique<webrtc::AudioBuffer>(sampleRate, channels, sampleRate, channels, sampleRate, channels);
        linearOutputBuffer = std::make_unique<webrtc::AudioBuffer>(sampleRate, channels, sampleRate, channels, sampleRate, channels);

        // RKAP_3A_Init(&m_vqe, AEC_TX_TYPE);
        webrtc::NsConfig config;
        config.target_level = webrtc::NsConfig::SuppressionLevel::k12dB;
        noiseSuppressor = std::make_unique<webrtc::NoiseSuppressor>(config, sampleRate, channels);
        noiseSuppressorBuffer = std::make_unique<webrtc::AudioBuffer>(sampleRate, channels, sampleRate, channels, sampleRate, channels);
    }

    std::unique_ptr<webrtc::EchoControl> echoCanceller;
    std::unique_ptr<webrtc::AudioBuffer> nearendBuffer;
    std::unique_ptr<webrtc::AudioBuffer> farendBuffer;
    std::unique_ptr<webrtc::AudioBuffer> linearOutputBuffer;

    std::unique_ptr<webrtc::NoiseSuppressor> noiseSuppressor;
    std::unique_ptr<webrtc::AudioBuffer> noiseSuppressorBuffer;

    // RKAP_AEC_State m_vqe;
};

ProcessFileTask::ProcessFileTask() : m_d{new ProcessFileTaskPrivate()} {
}

ProcessFileTask::~ProcessFileTask() {
    if (m_d != nullptr) {
        delete m_d;
    }
}

void ProcessFileTask::setDsp(Dsp dsp) {
    if (m_dsp != dsp) {
        m_dsp = dsp;
    }
}

// ./build/Record/Record --file --dsp=aecm
// ./Record --file --dsp=aecm
void ProcessFileTask::run() {
#ifdef __RV1109__
    constexpr auto MicFile = "/sdcard/micin_16k.pcm";
    constexpr auto SpeakerFile = "/sdcard/speaker_16k.pcm";

#else
    constexpr auto MicFile = "resources/micin_16k.pcm";
    constexpr auto SpeakerFile = "resources/speaker_16k.pcm";
#endif
    m_speakerIfs = std::make_shared<std::ifstream>(SpeakerFile, std::ifstream::binary);
    m_micIfs = std::make_shared<std::ifstream>(MicFile, std::ifstream::binary);

    std::ostringstream oss;
    oss << DumpPath << "/out_" << dspToString(m_dsp) << "_16k.wav";
    m_outFilename = oss.str();
    m_ofs = std::make_shared<WavWriter<int16_t>>(m_outFilename, 1, 16000);

    oss.str("");
    oss << DumpPath << "/ns_" << dspToString(m_dsp) << "_16k.pcm";
    m_nsOfs = std::make_shared<std::ofstream>(oss.str(), std::ofstream::binary);

    // m_speakerIfs = std::make_shared<std::ifstream>("/sdcard/speaker_8k.pcm", std::ifstream::binary);
    // m_micIfs = std::make_shared<std::ifstream>("/sdcard/micin_8k.pcm", std::ifstream::binary);
    // m_ofs = std::make_shared<std::ofstream>("/sdcard/out_8k.pcm", std::ofstream::binary);

    m_webRtcAecm = std::make_shared<WebRtcAecm>();
    m_webRtcAecm->start(16000, 1, 10);

    m_speex = std::make_shared<SpeexDsp>();
    m_speex->start(16000, 1, 10);

    m_d->initialize(16000, 1, 10);
    m_begin = std::chrono::system_clock::now();
    process();
}

void ProcessFileTask::process() {
    using namespace Amass;
    auto ioConext = Singleton<IoContext>::instance();
    if (m_speakerIfs && m_micIfs && *m_speakerIfs && *m_micIfs) {
        char farendBuffer[sizeof(int16_t) * 16000 / 1000 * 10] = {0};
        char nearendBuffer[sizeof(int16_t) * 16000 / 1000 * 10] = {0};
        char noiseSuppressorOutBuffer[sizeof(int16_t) * 16000 / 1000 * 10] = {0};
        char outBuffer[sizeof(int16_t) * 16000 / 1000 * 10] = {0};
        webrtc::StreamConfig config(16000, 1); // 单声道

        // char farendBuffer[sizeof(int16_t) * 8000 / 1000 * 10] = {0};
        // char nearendBuffer[sizeof(int16_t) * 8000 / 1000 * 10] = {0};
        // char outBuffer[sizeof(int16_t) * 8000 / 1000 * 10] = {0};

        m_speakerIfs->read(farendBuffer, sizeof(farendBuffer));
        m_micIfs->read(nearendBuffer, sizeof(nearendBuffer));
        m_d->nearendBuffer->CopyFrom(reinterpret_cast<const int16_t *>(nearendBuffer), config);
        if (m_dsp == Speex) {
            m_speex->echoPlayback(reinterpret_cast<const int16_t *>(farendBuffer));
            m_speex->echoCapture(reinterpret_cast<const int16_t *>(nearendBuffer), reinterpret_cast<int16_t *>(outBuffer));
        } else if (m_dsp == AecMobile) {
            m_d->noiseSuppressor->Analyze(*m_d->nearendBuffer);
            m_d->noiseSuppressor->Process(m_d->nearendBuffer.get());
            m_d->nearendBuffer->CopyTo(config, reinterpret_cast<int16_t *>(noiseSuppressorOutBuffer));
            m_nsOfs->write(noiseSuppressorOutBuffer, sizeof(noiseSuppressorOutBuffer));
            // LOG(info) << " " << m_d->noiseSuppressorBuffer->num_frames() << " " << m_d->nearendBuffer->num_frames();

            m_webRtcAecm->echoPlayback(reinterpret_cast<const int16_t *>(farendBuffer), sizeof(farendBuffer) / 2);
            m_webRtcAecm->echoCancellation(reinterpret_cast<int16_t *>(nearendBuffer),
                                           reinterpret_cast<int16_t *>(noiseSuppressorOutBuffer), reinterpret_cast<int16_t *>(outBuffer),
                                           sizeof(farendBuffer) / 2);
        } else if (m_dsp == Aec3) {

            m_d->farendBuffer->CopyFrom(reinterpret_cast<const int16_t *>(farendBuffer), config);

            m_d->echoCanceller->AnalyzeRender(m_d->farendBuffer.get());
            m_d->echoCanceller->AnalyzeCapture(m_d->nearendBuffer.get());
            m_d->echoCanceller->ProcessCapture(m_d->nearendBuffer.get(), false);
            // m_d->echoCanceller->ProcessCapture(&nearendBuffer, &linearOutputBuffer, /*level_change=*/false);

            m_d->nearendBuffer->CopyTo(config, reinterpret_cast<int16_t *>(outBuffer));
        } else if (m_dsp == Vqe) {
        }

        m_ofs->write(outBuffer, sizeof(outBuffer));
        boost::asio::post(*ioConext->ioContext(), [this]() { process(); });
    } else {
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - m_begin);
        LOG(info) << "process file finished, out: " << m_outFilename << ", elapsed: " << elapsed;
        ioConext->stop();
    }
}