/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "AudioVividEncoder.h"
#include <multimedia/player_framework/native_avcodec_base.h>
#include <multimedia/player_framework/native_avbuffer.h>
#include <multimedia/player_framework/native_audio_vivid.h>
#include <cstring>

AudioVividEncoder::~AudioVividEncoder()
{
    Release();
}

int32_t AudioVividEncoder::Create()
{
    encoder_ = OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_VIVID, true);
    if (encoder_ == nullptr) {
        AVCODEC_SAMPLE_LOGE("Create AudioVivid encoder failed");
        return -1;
    }
    AVCODEC_SAMPLE_LOGI("Create AudioVivid encoder success");
    return 0;
}

int32_t AudioVividEncoder::Config(int32_t sampleRate, int32_t channelCount, int64_t channelLayout, int32_t bitrate)
{
    sampleRate_ = sampleRate;
    channelCount_ = channelCount;
    channelLayout_ = channelLayout;
    bitrate_ = bitrate;
    int32_t ret = Configure();
    if (ret != 0) {
        AVCODEC_SAMPLE_LOGE("Configure failed");
        return ret;
    }
    ret = SetCallback();
    if (ret != 0) {
        AVCODEC_SAMPLE_LOGE("SetCallback failed");
        return ret;
    }
    ret = OH_AudioCodec_Prepare(encoder_);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Prepare failed, ret: %{public}d", ret);
        return -1;
    }
    AVCODEC_SAMPLE_LOGI("Config AudioVivid encoder: sampleRate=%{public}d, channelCount=%{public}d, bitrate=%{public}d",
        sampleRate_,
        channelCount_,
        bitrate_);
    return 0;
}

int32_t AudioVividEncoder::Configure()
{
    OH_AVFormat *format = OH_AVFormat_Create();
    if (format == nullptr) {
        AVCODEC_SAMPLE_LOGE("AVFormat create failed");
        return -1;
    }
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_VIVID_SIGNAL_FORMAT, OH_AUDIO_VIVID_SIGNAL_FORMAT_MIX);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_S24LE);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleRate_);
    OH_AVFormat_SetLongValue(format, OH_MD_KEY_AUDIO_SOUNDBED_LAYOUT, channelLayout_);
    // 声床比特率设置为128000 bps
    OH_AVFormat_SetLongValue(format, OH_MD_KEY_AUDIO_SOUNDBED_BITRATE, 128000);
    // 对象格式设置为2个
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_OBJECT_NUMBER, 2);
    // 声床比特率设置为64000 bps
    OH_AVFormat_SetLongValue(format, OH_MD_KEY_AUDIO_OBJECT_BITRATE, 64000);
    int32_t ret = OH_AudioCodec_Configure(encoder_, format);
    OH_AVFormat_Destroy(format);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Configure encoder failed, ret: %{public}d", ret);
        return -1;
    }
    return 0;
}

int32_t AudioVividEncoder::SetCallback()
{
    auto onError = [](OH_AVCodec *codec, int32_t errorCode, void *userData) {
        AudioVividEncoderContext *context = static_cast<AudioVividEncoderContext *>(userData);
        context->errorCode = errorCode;
        context->outputCond.notify_all();
        AVCODEC_SAMPLE_LOGE("Encoder error: %{public}d", errorCode);
    };
    auto onFormatChange = [](OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
        AVCODEC_SAMPLE_LOGI("Encoder format change");
    };
    auto onNeedInputBuffer = [](OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
        AudioVividEncoderContext *context = static_cast<AudioVividEncoderContext *>(userData);
        std::unique_lock<std::mutex> lock(context->inputMutex);
        context->inputBufferIndices.push(index);
        context->inputBufferQueue.push(buffer);
        context->inputCond.notify_all();
    };
    auto onNewOutputBuffer = [](OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
        AudioVividEncoderContext *context = static_cast<AudioVividEncoderContext *>(userData);
        AudioVividEncoderOutputData outputData;
        OH_AVCodecBufferAttr attr;
        OH_AVBuffer_GetBufferAttr(buffer, &attr);
        outputData.encodedSize = attr.size;
        outputData.presentationTimeUs = attr.pts;
        outputData.eos = (attr.flags & AVCODEC_BUFFER_FLAGS_EOS) != 0;
        outputData.encodedData = nullptr;
        uint8_t *encodedAddr = OH_AVBuffer_GetAddr(buffer);
        if (encodedAddr && attr.size > 0) {
            outputData.encodedData = new uint8_t[attr.size];
            memcpy(outputData.encodedData, encodedAddr, attr.size);
        }
        {
            std::unique_lock<std::mutex> lock(context->outputMutex);
            context->outputQueue.push(outputData);
            context->outputCond.notify_all();
        }
        OH_AudioCodec_FreeOutputBuffer(codec, index);
    };

    int32_t ret = OH_AudioCodec_RegisterCallback(
        encoder_, {onError, onFormatChange, onNeedInputBuffer, onNewOutputBuffer}, &context_);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("RegisterCallback failed, ret: %{public}d", ret);
        return -1;
    }
    return 0;
}

int32_t AudioVividEncoder::Start()
{
    if (encoder_ == nullptr) {
        AVCODEC_SAMPLE_LOGE("Encoder is null");
        return -1;
    }
    int32_t ret = OH_AudioCodec_Start(encoder_);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Start encoder failed, ret: %{public}d", ret);
        return -1;
    }
    AVCODEC_SAMPLE_LOGI("AudioVivid encoder started");
    return 0;
}

void AudioVividEncoder::UpdateMetadata(uint8_t *metadata, int32_t metadataSize)
{
    std::lock_guard<std::mutex> lock(metadataMutex_);
    if (currentMetadata_) {
        delete[] currentMetadata_;
    }
    if (metadataSize > 10485760) { // 10485760:10MB 异常过大的输入大小
        return;
    }
    currentMetadata_ = new uint8_t[metadataSize];
    memcpy(currentMetadata_, metadata, metadataSize);
    currentMetadataSize_ = metadataSize;
}

void AudioVividEncoder::AttachMetadataToBuffer(OH_AVBuffer *buffer, uint8_t *metadata, int32_t metadataSize)
{
    if (metadata && metadataSize > 0) {
        OH_AVFormat *meta = OH_AVFormat_Create();
        if (meta) {
            OH_AVFormat_SetBuffer(meta, OH_MD_KEY_AUDIO_VIVID_METADATA, metadata, metadataSize);
            OH_AVBuffer_SetParameter(buffer, meta);
        }
        OH_AVFormat_Destroy(meta);
    }
}

int32_t AudioVividEncoder::PushInputBuffer(
    uint8_t *pcmData, int32_t pcmSize, uint8_t *metadata, int32_t metadataSize, int64_t presentationTimeUs)
{
    if (encoder_ == nullptr || pcmData == nullptr || pcmSize <= 0) {
        AVCODEC_SAMPLE_LOGE("Invalid parameters: encoder=%{public}s, pcmData=%{public}s, pcmSize=%{public}d",
            encoder_ ? "valid" : "null",
            pcmData ? "valid" : "null",
            pcmSize);
        return -1;
    }
    std::unique_lock<std::mutex> lock(context_.inputMutex);
    if (context_.inputBufferIndices.empty()) {
        context_.inputCond.wait_for(lock, std::chrono::milliseconds(100), [this] { // 等100ms输入
            return !context_.inputBufferIndices.empty() || context_.eos;
        });
    }
    if (context_.inputBufferIndices.empty()) {
        AVCODEC_SAMPLE_LOGE("No input buffer available");
        return -1;
    }
    uint32_t index = context_.inputBufferIndices.front();
    context_.inputBufferIndices.pop();
    OH_AVBuffer *buffer = context_.inputBufferQueue.front();
    context_.inputBufferQueue.pop();
    lock.unlock();
    uint8_t *bufferAddr = OH_AVBuffer_GetAddr(buffer);
    int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
    if (pcmSize > capacity) {
        AVCODEC_SAMPLE_LOGW("PCM size %{public}d exceeds capacity %{public}d, truncating", pcmSize, capacity);
        pcmSize = capacity;
    }
    memcpy(bufferAddr, pcmData, pcmSize);
    OH_AVCodecBufferAttr attr = {static_cast<int32_t>(presentationTimeUs), pcmSize, 0, AVCODEC_BUFFER_FLAGS_NONE};
    OH_AVBuffer_SetBufferAttr(buffer, &attr);
    if (metadata && metadataSize > 0) {
        AttachMetadataToBuffer(buffer, metadata, metadataSize);
    } else {
        AVCODEC_SAMPLE_LOGE("no meta");
    }
    int32_t ret = OH_AudioCodec_PushInputBuffer(encoder_, index);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Push input buffer failed, ret: %{public}d", ret);
        return -1;
    }
    return 0;
}

AudioVividEncoderOutputData *AudioVividEncoder::GetOutputBuffer()
{
    std::unique_lock<std::mutex> lock(context_.outputMutex);
    // 等待100ms
    context_.outputCond.wait_for(lock, std::chrono::milliseconds(100),
        [this] { return !context_.outputQueue.empty() || context_.eos; });
    if (context_.outputQueue.empty()) {
        return nullptr;
    }
    AudioVividEncoderOutputData *data = new AudioVividEncoderOutputData(context_.outputQueue.front());
    return data;
}

void AudioVividEncoder::FreeOutputBuffer()
{
    std::unique_lock<std::mutex> lock(context_.outputMutex);
    if (!context_.outputQueue.empty()) {
        AudioVividEncoderOutputData &data = context_.outputQueue.front();
        if (data.encodedData) {
            delete[] data.encodedData;
            data.encodedData = nullptr;
        }
        context_.outputQueue.pop();
    }
}

int32_t AudioVividEncoder::NotifyEndOfStream()
{
    if (encoder_ == nullptr) {
        return -1;
    }
    std::unique_lock<std::mutex> lock(context_.inputMutex);
    if (context_.inputBufferIndices.empty()) {
        context_.inputCond.wait_for(lock, std::chrono::milliseconds(1000), [this] { // 1000ms
            return !context_.inputBufferIndices.empty() || context_.eos;
        });
    }
    if (context_.inputBufferIndices.empty()) {
        AVCODEC_SAMPLE_LOGE("No input buffer available for EOS");
        context_.eos = true;
        context_.outputCond.notify_all();
        return -1;
    }
    uint32_t index = context_.inputBufferIndices.front();
    context_.inputBufferIndices.pop();
    OH_AVBuffer *buffer = context_.inputBufferQueue.front();
    context_.inputBufferQueue.pop();
    lock.unlock();
    OH_AVCodecBufferAttr attr = {0, 0, 0, AVCODEC_BUFFER_FLAGS_EOS};
    OH_AVBuffer_SetBufferAttr(buffer, &attr);
    int32_t ret = OH_AudioCodec_PushInputBuffer(encoder_, index);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Push EOS buffer failed, ret: %{public}d", ret);
        return -1;
    }
    AVCODEC_SAMPLE_LOGI("AudioVivid encoder notified EOS via buffer flag");
    return 0;
}

int32_t AudioVividEncoder::Stop()
{
    if (encoder_ == nullptr) {
        return -1;
    }
    context_.eos = true;
    context_.inputCond.notify_all();
    context_.outputCond.notify_all();
    int32_t ret = OH_AudioCodec_Stop(encoder_);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Stop encoder failed, ret: %{public}d", ret);
        return -1;
    }
    context_.inputBufferIndices = std::queue<uint32_t>();
    context_.inputBufferQueue = std::queue<OH_AVBuffer *>();
    AVCODEC_SAMPLE_LOGI("AudioVivid encoder stopped");
    return 0;
}

int32_t AudioVividEncoder::Release()
{
    if (encoder_ != nullptr) {
        OH_AudioCodec_Destroy(encoder_);
        encoder_ = nullptr;
    }
    {
        std::unique_lock<std::mutex> lock(context_.outputMutex);
        while (!context_.outputQueue.empty()) {
            AudioVividEncoderOutputData &data = context_.outputQueue.front();
            if (data.encodedData) {
                delete[] data.encodedData;
            }
            context_.outputQueue.pop();
        }
    }
    if (currentMetadata_) {
        delete[] currentMetadata_;
        currentMetadata_ = nullptr;
    }
    currentMetadataSize_ = 0;
    context_.eos = false;
    context_.errorCode = 0;
    return 0;
}