/*
* Copyright (c) 2024 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 "VideoDecoder.h"
#include "PluginManager.h"
#include <cstdint>
#include <multimedia/player_framework/native_averrors.h>
#include "dfx/error/AVCodecSampleError.h"
#include "AVCodecSampleLog.h"


#undef LOG_TAG
#define LOG_TAG "VideoDecoder"

namespace {
constexpr int LIMIT_LOGD_FREQUENCY = 50;
constexpr int ROTATION_ANGLE = 90;
}

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

int32_t VideoDecoder::Create(const std::string &videoCodecMime) {
    decoder_ = OH_VideoDecoder_CreateByMime(videoCodecMime.c_str());
    CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create VideoDecoder failed");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::Config(SampleInfo &sampleInfo, CodecUserData *codecUserData) {
    int32_t ret = Configure(sampleInfo);
    CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Configure failed");
    if (sampleInfo.videoIndex == 0) {
        ret = OH_VideoDecoder_SetSurface(decoder_, NativeXComponentSample::PluginManager::GetInstance()->renderWindow);
        CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && NativeXComponentSample::PluginManager::GetInstance()->renderWindow, 
                                AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder SetSurface failed");
    } else if (sampleInfo.videoIndex == 1) {
        if (sampleInfo.processType) {
            NativeXComponentSample::PluginManager::GetInstance()->PrepareSurface(sampleInfo);
            if (NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_ != nullptr) {
                ret = OH_VideoDecoder_SetSurface(decoder_, NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_);
                CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_, 
                                    AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder SetSurface failed");
            }
        } else {
            ret = OH_VideoDecoder_SetSurface(decoder_, NativeXComponentSample::PluginManager::GetInstance()->windowOut);
            CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && NativeXComponentSample::PluginManager::GetInstance()->windowOut, 
                                AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder SetSurface failed");
        }
    }
    ret = SetCallback(codecUserData);
    CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder SetCallback failed");
    {
        ret = OH_VideoDecoder_Prepare(decoder_);
        CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder Prepare failed");
    }
    if (sampleInfo.videoIndex == 1 && sampleInfo.processType) {
        NativeXComponentSample::PluginManager::GetInstance()->InitProcessing(sampleInfo);
        int32_t rotation = sampleInfo.rotation == 0 ? 0 : (360 - sampleInfo.rotation) / 90;
        AVCODEC_SAMPLE_LOGI("Set Surface Rotation: %{public}d", rotation);
        OH_NativeWindow_NativeWindowHandleOpt(NativeXComponentSample::PluginManager::GetInstance()->windowOut, SET_TRANSFORM, rotation);
        OH_NativeWindow_NativeWindowHandleOpt(NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_, SET_USAGE, NATIVEBUFFER_USAGE_CPU_READ);
    }
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::PushInputBuffer(CodecBufferInfo &info) {
    int32_t ret = OH_VideoDecoder_PushInputBuffer(decoder_, info.bufferIndex);
    CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder PushInputBuffer failed");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::FreeOutputBuffer(uint32_t bufferIndex, bool render) {
    int32_t ret = AV_ERR_OK;
    if (render) {
        ret = OH_VideoDecoder_RenderOutputBuffer(decoder_, bufferIndex);
    } else {
        ret = OH_VideoDecoder_FreeOutputBuffer(decoder_, bufferIndex);
    }
    CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder FreeOutputBuffer failed");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::Start() {
    CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "decoder_ is null");
    int32_t ret = OH_VideoDecoder_Start(decoder_);
    CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder Start failed, ret: %{public}d", ret);
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::Release() {
    if (decoder_ != nullptr) {
        OH_VideoDecoder_Flush(decoder_);
        OH_VideoDecoder_Stop(decoder_);
        OH_VideoDecoder_Destroy(decoder_);
        decoder_ = nullptr;
    }
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::SetCallback(CodecUserData *codecUserData){
    int32_t ret = OH_VideoDecoder_RegisterCallback(decoder_, 
                                                {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange, 
                                                SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer}, 
                                                codecUserData);
    CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Create SetCallback failed");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t VideoDecoder::Configure(const SampleInfo &sampleInfo) {
    OH_AVFormat *format = OH_AVFormat_Create();
    CHECK_AND_RETURN_RET_LOG(format != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create AVFormat failed");
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, sampleInfo.videoWidth);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, sampleInfo.videoHeight);
    OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, sampleInfo.frameRate);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, sampleInfo.pixelFormat);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, sampleInfo.rotation);
    int ret = OH_VideoDecoder_Configure(decoder_, format);
    CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "VideoDecoder Configure failed");
    OH_AVFormat_Destroy(format);
    format = nullptr;
    return AVCODEC_SAMPLE_ERR_OK;
}