/*
 * 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 "AudioVividDecoder.h"
#include <multimedia/player_framework/native_avbuffer.h>
#include <cstring>

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

int32_t AudioVividDecoder::Create()
{
    decoder_ = OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_VIVID, false);
    if (decoder_ == nullptr) {
        AVCODEC_SAMPLE_LOGE("Create AudioVivid decoder failed");
        return -1;
    }
    AVCODEC_SAMPLE_LOGI("Create AudioVivid decoder success");
    return 0;
}

int32_t AudioVividDecoder::Config(int32_t sampleRate, int32_t channelCount, int64_t channelLayout)
{
    sampleRate_ = sampleRate;
    channelCount_ = channelCount;
    channelLayout_ = channelLayout;
    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(decoder_);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Prepare failed, ret: %{public}d", ret);
        return -1;
    }
    AVCODEC_SAMPLE_LOGI(
        "Config AudioVivid decoder: sampleRate=%{public}d, channelCount=%{public}d", sampleRate_, channelCount_);
    return 0;
}

int32_t AudioVividDecoder::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_SAMPLE_FORMAT, SAMPLE_S24LE);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, channelCount_);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleRate_);
    OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, channelLayout_);
    int32_t ret = OH_AudioCodec_Configure(decoder_, format);
    OH_AVFormat_Destroy(format);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Configure decoder failed, ret: %{public}d", ret);
        return -1;
    }
    return 0;
}

int32_t AudioVividDecoder::SetCallback()
{
    auto onError = [](OH_AVCodec *codec, int32_t errorCode, void *userData) {
        AVCODEC_SAMPLE_LOGE("Decoder error: %{public}d", errorCode);
    };
    auto onFormatChange = [](OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
        AVCODEC_SAMPLE_LOGI("Decoder format change");
    };
    auto onNeedInputBuffer = [](OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
        AudioVividDecoderContext *context = static_cast<AudioVividDecoderContext *>(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) {
        AudioVividDecoderContext *context = static_cast<AudioVividDecoderContext *>(userData);
        AudioVividDecoderData outputData;
        OH_AVCodecBufferAttr attr;
        OH_AVBuffer_GetBufferAttr(buffer, &attr);
        outputData.pcmSize = attr.size;
        outputData.presentationTimeUs = attr.pts;
        uint8_t *pcmAddr = OH_AVBuffer_GetAddr(buffer);
        if (pcmAddr && attr.size > 0) {
            outputData.pcmData = new uint8_t[attr.size];
            memcpy(outputData.pcmData, pcmAddr, attr.size);
        }
        OH_AVFormat *format = OH_AVBuffer_GetParameter(buffer);
        uint8_t *metadata = nullptr;
        size_t metadataSize = 0;
        OH_AVFormat_GetBuffer(format, OH_MD_KEY_AUDIO_VIVID_METADATA, &metadata, &metadataSize);
        if (metadata && metadataSize > 0) {
            outputData.metadataSize = metadataSize;
            outputData.metadata = new uint8_t[metadataSize];
            memcpy(outputData.metadata, metadata, metadataSize);
        }
        {
            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(
        decoder_, {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 AudioVividDecoder::Start()
{
    if (decoder_ == nullptr) {
        AVCODEC_SAMPLE_LOGE("Decoder is null");
        return -1;
    }
    int32_t ret = OH_AudioCodec_Start(decoder_);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Start decoder failed, ret: %{public}d", ret);
        return -1;
    }
    AVCODEC_SAMPLE_LOGI("AudioVivid decoder started");
    return 0;
}

int32_t AudioVividDecoder::PushInputBuffer(uint8_t *data, int32_t size)
{
    if (decoder_ == nullptr || data == nullptr || size <= 0) {
        AVCODEC_SAMPLE_LOGE("Invalid parameters");
        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);
    if (bufferAddr == nullptr) {
        AVCODEC_SAMPLE_LOGE("input buffer nullptr");
        return -1;
    }
    int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
    if (size > capacity) {
        size = capacity;
    }
    memcpy(bufferAddr, data, size);
    OH_AVCodecBufferAttr attr = {0, size, 0, AVCODEC_BUFFER_FLAGS_NONE};
    OH_AVBuffer_SetBufferAttr(buffer, &attr);
    int32_t ret = OH_AudioCodec_PushInputBuffer(decoder_, index);
    if (ret != AV_ERR_OK) {
        AVCODEC_SAMPLE_LOGE("Push input buffer failed, ret: %{public}d", ret);
        return -1;
    }
    return 0;
}
AudioVividDecoderData *AudioVividDecoder::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;
    }
    AudioVividDecoderData &frontData = context_.outputQueue.front();
    AudioVividDecoderData *data = new AudioVividDecoderData();
    data->pcmData = frontData.pcmData;
    data->pcmSize = frontData.pcmSize;
    data->metadata = frontData.metadata;
    data->metadataSize = frontData.metadataSize;
    data->presentationTimeUs = frontData.presentationTimeUs;
    frontData.pcmData = nullptr;
    frontData.metadata = nullptr;
    return data;
}

void AudioVividDecoder::FreeOutputBuffer()
{
    std::unique_lock<std::mutex> lock(context_.outputMutex);
    if (!context_.outputQueue.empty()) {
        context_.outputQueue.pop();
    }
}

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

int32_t AudioVividDecoder::Release()
{
    if (decoder_ != nullptr) {
        OH_AudioCodec_Flush(decoder_);
        OH_AudioCodec_Destroy(decoder_);
        decoder_ = nullptr;
    }
    {
        std::unique_lock<std::mutex> lock(context_.outputMutex);
        while (!context_.outputQueue.empty()) {
            AudioVividDecoderData &data = context_.outputQueue.front();
            if (data.pcmData) {
                delete[] data.pcmData;
            }
            if (data.metadata) {
                delete[] data.metadata;
            }
            context_.outputQueue.pop();
        }
    }
    context_.eos = false;
    context_.errorCode = 0;
    return 0;
}