/*
 * 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 "player_jni.h"
#include "plugins/interfaces/native/inner_api/plugin_utils_inner.h"

#include "media_errors.h"
#include "media_log.h"
#include "plugin_utils.h"
#include "mock_avsharedmemory.h"
#include "uri_helper.h"
#include "directory_ex.h"

namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_PLAYER, "PlayerJni"};
}

namespace OHOS::Plugin {
namespace {
const char Player_CLASS_NAME[] = "ohos/ace/plugin/playerplugin/PlayerPlugin";
static const JNINativeMethod METHODS[] = {
    {"nativeInit", "()V", reinterpret_cast<void *>(PlayerJni::NativeInit)},
    {"nativeOnError", "(JI)V", reinterpret_cast<void *>(PlayerJni::NativeOnError)},
    {"nativeOnInfo", "(JII)V", reinterpret_cast<void *>(PlayerJni::NativeOnInfo)},
    {"nativeOnBufferingUpdate", "(JI)V", reinterpret_cast<void *>(PlayerJni::NativeOnBufferingUpdate)},
    {"nativeOnSeekComplete", "(JI)V", reinterpret_cast<void *>(PlayerJni::NativeOnSeekComplete)},
    {"nativeOnVideoSizeChanged", "(JII)V", reinterpret_cast<void *>(PlayerJni::NativeOnVideoSizeChanged)},
    {"nativeOnVolumnChanged", "(JF)V", reinterpret_cast<void *>(PlayerJni::NativeOnVolumnChanged)},
    {"nativeReadAt", "(JJ[BII)I", reinterpret_cast<void *>(PlayerJni::NativeReadAt)},
};

static jstring StringToJavaString(JNIEnv* env, const std::string &string)
{
    std::u16string str = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes(string);
    return env->NewString(reinterpret_cast<const jchar *>(str.data()), str.length());
}

static const char METHOD_CREATE_PLAYER[] = "addMediaPlayer";
static const char METHOD_RELEASE_PLAYER[] = "releaseMediaPlayer";
static const char METHOD_PREPARE[] = "prepare";
static const char METHOD_PREPARE_ASYNC[] = "prepareAsync";
static const char METHOD_PLAY[] = "play";
static const char METHOD_PAUSE[] = "pause";
static const char METHOD_STOP[] = "stop";
static const char METHOD_RESET[] = "reset";
static const char METHOD_RELEASE[] = "release";
static const char METHOD_SEAK_TO[] = "seekTo";
static const char METHOD_SET_VOLUME[] = "setVolume";
static const char METHOD_GET_TRACKINDEX[] = "getTrackIndex";
static const char METHOD_GET_TRACKINFO_STRING[] = "getTrackInfoString";
static const char METHOD_SET_DATASOURCE[] = "setDataSource";
static const char METHOD_SET_DATASOURCE_WITH_URL[] = "setDataSourceWithUrl";
static const char METHOD_SET_DATASOURCE_WITH_FD[] = "setDataSourceWithFd";
static const char METHOD_IS_LOOPING[] = "isLooping";
static const char METHOD_SET_LOOPING[] = "setLooping";
static const char METHOD_GET_CURRENT_POSITION[] = "getCurrentPosition";
static const char METHOD_GET_DURATION[] = "getDuration";
static const char METHOD_SET_SURFACE[] = "setSurface";
static const char METHOD_GET_VIDEO_WIDTH[] = "getVideoWidth";
static const char METHOD_GET_VIDEO_HEIGHT[] = "getVideoHeight";
static const char METHOD_SET_VIDEO_SCALING_MODE[] = "setVideoScalingMode";
static const char METHOD_SET_PLAYBACK_PARAMS[] = "setPlaybackParams";
static const char METHOD_GET_PLAYBACK_PARAMS[] = "getPlaybackParams";
static const char METHOD_SELECT_BITRATE[] = "selectBitrate";

static const char SIGNATURE_PLAY[] = "(J)V";
static const char SIGNATURE_SEEK[] = "(JII)V";
static const char SIGNATURE_SET_SURFACE[] = "(JIJ)V";
static const char SIGNATURE_SET_MODE[] = "(JI)V";
static const char SIGNATURE_SET_PLAYBACKPARAM[] = "(JI)V";
static const char SIGNATURE_GET_PLAYBACKPARAM[] = "(J)I";
static const char SIGNATURE_SET_VOLUME[] = "(JFF)V";
static const char SIGNATURE_IS_LOOPING[] = "(J)Z";
static const char SIGNATURE_SET_DATESOURCE_URL[] = "(JLjava/lang/String;)V";
static const char SIGNATURE_SET_DATESOURCE_FD[] = "(JLjava/lang/String;JJ)V";
static const char SIGNATURE_SET_LOOPING[] = "(JZ)V";
static const char SIGNATURE_GET_CURRENT_POSITION[] = "(J)I";
static const char SIGNATURE_GET_TRACK_STR[] = "(JILjava/lang/String;)Ljava/lang/String;";
static const char SIGNATURE_GET_TRACK_INDEX[] = "(JI)I";
static const char SIGNATURE_REGISTER_NET_CONN_CALLBACK[] = "(J[I)V";
static const char SIGNATURE_UNREGISTER_NET_CONN_CALLBACK[] = "(J)V";
static const char SIGNATURE_IS_DEFAULT_NETWORK_ACTIVE[] = "()Z";

static const int TRACK_TYPE_VIDEO = 1;
static const int TRACK_TYPE_AUDIO = 2;
static const int TRACK_TYPE_SUBTITLE = 4;

static const std::string TRACK_KEYS[] = {
    "codec_mime",
    "duration",
    "bitrate",
    "width",
    "height",
    "frame_rate",
    "channel_count",
    "sample_rate",
};

struct {
    jmethodID createPlayer;
    jmethodID releasePlayer;
    jmethodID prepare;
    jmethodID prepareAsync;
    jmethodID play;
    jmethodID pause;
    jmethodID stop;
    jmethodID reset;
    jmethodID release;
    jmethodID seekTo;
    jmethodID setVolume;
    jmethodID getTrackIndex;
    jmethodID getTrackInfoString;
    jmethodID setDataSource;
    jmethodID setDataSourceWithUrl;
    jmethodID setDataSourceWithFd;
    jmethodID isLooping;
    jmethodID setLooping;
    jmethodID getCurrentPosition;
    jmethodID getDuration;
    jmethodID setSurface;
    jmethodID getVideoWidth;
    jmethodID getVideoHeight;
    jmethodID setVideoScalingMode;
    jmethodID setPlaybackParams;
    jmethodID getPlaybackParams;
    jmethodID selectBitrate;
    jobject globalRef;
} g_playerpluginClass;
static const std::map<std::string, jmethodID> voidFuncMap = {
    {"createPlayer", g_playerpluginClass.createPlayer},
    {"releasePlayer", g_playerpluginClass.releasePlayer},
    {"prepare", g_playerpluginClass.prepare},
    {"prepareAsync", g_playerpluginClass.prepareAsync},
    {"play", g_playerpluginClass.play},
    {"pause", g_playerpluginClass.pause},
    {"stop", g_playerpluginClass.stop},
    {"reset", g_playerpluginClass.reset},
    {"release", g_playerpluginClass.release},
};
}
std::map<long, std::shared_ptr<Media::PlayerCallback>> PlayerJni::callbacks_;
std::map<long, std::shared_ptr<Media::IMediaDataSource>> PlayerJni::mediaDataSources_;

bool PlayerJni::Register(void *env)
{
    MEDIA_LOGI("PlayerJni::Register");
    auto *jniEnv = static_cast<JNIEnv *>(env);
    CHECK_AND_RETURN_RET(jniEnv != nullptr, false);
    jclass cls = jniEnv->FindClass(Player_CLASS_NAME);
    CHECK_AND_RETURN_RET(cls != nullptr, false);
    bool ret = jniEnv->RegisterNatives(cls, METHODS, sizeof(METHODS) / sizeof(METHODS[0])) == 0;
    jniEnv->DeleteLocalRef(cls);
    if (!ret) {
        MEDIA_LOGE("PlayerJni JNI: RegisterNatives fail.");
        return false;
    }

    return true;
}

void PlayerJni::NativeInit(JNIEnv *env, jobject jobj)
{
    MEDIA_LOGI("PlayerJni::NativeInit");
    CHECK_AND_RETURN(env != nullptr);
    g_playerpluginClass.globalRef = env->NewGlobalRef(jobj);
    CHECK_AND_RETURN(g_playerpluginClass.globalRef != nullptr);
    jclass cls = env->GetObjectClass(jobj);
    CHECK_AND_RETURN(cls != nullptr);

    g_playerpluginClass.createPlayer = env->GetMethodID(cls, METHOD_CREATE_PLAYER, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.createPlayer != nullptr);

    g_playerpluginClass.releasePlayer = env->GetMethodID(cls, METHOD_RELEASE_PLAYER, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.releasePlayer != nullptr);

    g_playerpluginClass.prepare = env->GetMethodID(cls, METHOD_PREPARE, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.prepare != nullptr);

    g_playerpluginClass.prepareAsync = env->GetMethodID(cls, METHOD_PREPARE_ASYNC, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.prepareAsync != nullptr);

    g_playerpluginClass.play = env->GetMethodID(cls, METHOD_PLAY, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.play != nullptr);

    g_playerpluginClass.pause = env->GetMethodID(cls, METHOD_PAUSE, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.pause != nullptr);

    g_playerpluginClass.stop = env->GetMethodID(cls, METHOD_STOP, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.stop != nullptr);

    g_playerpluginClass.reset = env->GetMethodID(cls, METHOD_RESET, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.reset != nullptr);

    g_playerpluginClass.release = env->GetMethodID(cls, METHOD_RELEASE, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.release != nullptr);

    g_playerpluginClass.seekTo = env->GetMethodID(cls, METHOD_SEAK_TO, SIGNATURE_SEEK);
    CHECK_AND_RETURN(g_playerpluginClass.seekTo != nullptr);

    g_playerpluginClass.setVolume = env->GetMethodID(cls, METHOD_SET_VOLUME, SIGNATURE_SET_VOLUME);
    CHECK_AND_RETURN(g_playerpluginClass.setVolume != nullptr);

    g_playerpluginClass.getTrackIndex = env->GetMethodID(cls, METHOD_GET_TRACKINDEX, SIGNATURE_GET_TRACK_INDEX);
    CHECK_AND_RETURN(g_playerpluginClass.getTrackIndex != nullptr);

    g_playerpluginClass.getTrackInfoString = env->GetMethodID(cls, METHOD_GET_TRACKINFO_STRING, SIGNATURE_GET_TRACK_STR);
    CHECK_AND_RETURN(g_playerpluginClass.getTrackInfoString != nullptr);

    g_playerpluginClass.setDataSource = env->GetMethodID(cls, METHOD_SET_DATASOURCE, SIGNATURE_PLAY);
    CHECK_AND_RETURN(g_playerpluginClass.setDataSource != nullptr);

    g_playerpluginClass.setDataSourceWithUrl = env->GetMethodID(
        cls, METHOD_SET_DATASOURCE_WITH_URL, SIGNATURE_SET_DATESOURCE_URL);
    CHECK_AND_RETURN(g_playerpluginClass.setDataSourceWithUrl != nullptr);

    g_playerpluginClass.setDataSourceWithFd = env->GetMethodID(
        cls, METHOD_SET_DATASOURCE_WITH_FD, SIGNATURE_SET_DATESOURCE_FD);
    CHECK_AND_RETURN(g_playerpluginClass.setDataSourceWithFd != nullptr);

    g_playerpluginClass.isLooping = env->GetMethodID(cls, METHOD_IS_LOOPING, SIGNATURE_IS_LOOPING);
    CHECK_AND_RETURN(g_playerpluginClass.isLooping != nullptr);

    g_playerpluginClass.setLooping = env->GetMethodID(cls, METHOD_SET_LOOPING, SIGNATURE_SET_LOOPING);
    CHECK_AND_RETURN(g_playerpluginClass.setLooping != nullptr);

    g_playerpluginClass.getCurrentPosition = env->GetMethodID(
        cls, METHOD_GET_CURRENT_POSITION, SIGNATURE_GET_CURRENT_POSITION);
    CHECK_AND_RETURN(g_playerpluginClass.getCurrentPosition != nullptr);

    g_playerpluginClass.getDuration = env->GetMethodID(cls, METHOD_GET_DURATION, SIGNATURE_GET_CURRENT_POSITION);
    CHECK_AND_RETURN(g_playerpluginClass.getDuration != nullptr);

    g_playerpluginClass.setSurface = env->GetMethodID(cls, METHOD_SET_SURFACE, SIGNATURE_SET_SURFACE);
    CHECK_AND_RETURN(g_playerpluginClass.setSurface != nullptr);

    g_playerpluginClass.getVideoWidth = env->GetMethodID(cls, METHOD_GET_VIDEO_WIDTH, SIGNATURE_GET_CURRENT_POSITION);
    CHECK_AND_RETURN(g_playerpluginClass.getVideoWidth != nullptr);

    g_playerpluginClass.getVideoHeight = env->GetMethodID(cls, METHOD_GET_VIDEO_HEIGHT, SIGNATURE_GET_CURRENT_POSITION);
    CHECK_AND_RETURN(g_playerpluginClass.getVideoHeight != nullptr);

    g_playerpluginClass.setVideoScalingMode = env->GetMethodID(cls, METHOD_SET_VIDEO_SCALING_MODE, SIGNATURE_SET_MODE);
    CHECK_AND_RETURN(g_playerpluginClass.setVideoScalingMode != nullptr);

    g_playerpluginClass.setPlaybackParams = env->GetMethodID(
        cls, METHOD_SET_PLAYBACK_PARAMS, SIGNATURE_SET_PLAYBACKPARAM);
    CHECK_AND_RETURN(g_playerpluginClass.setPlaybackParams != nullptr);

    g_playerpluginClass.getPlaybackParams = env->GetMethodID(
        cls, METHOD_GET_PLAYBACK_PARAMS, SIGNATURE_GET_PLAYBACKPARAM);
    CHECK_AND_RETURN(g_playerpluginClass.getPlaybackParams != nullptr);

    g_playerpluginClass.selectBitrate = env->GetMethodID(cls, METHOD_SELECT_BITRATE, SIGNATURE_SET_MODE);
    CHECK_AND_RETURN(g_playerpluginClass.selectBitrate != nullptr);

    return;
}

int32_t PlayerJni::CallVoidFunc(long key, std::string funcName)
{
    MEDIA_LOGD("PlayerJni CallVoidFunc");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    jmethodID methodId = nullptr;
    auto iter = voidFuncMap.find(funcName);
    if (iter != voidFuncMap.end()) {
        methodId = iter->second;
    }
    CHECK_AND_RETURN_RET(methodId != nullptr, Media::MSERR_SERVICE_DIED);
    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef, methodId, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call %{public}s has exception", funcName.c_str());
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }
    return Media::MSERR_OK;
}

int32_t PlayerJni::SetSurface(long key, int32_t instanceId, long id)
{
    MEDIA_LOGD("PlayerJni SetSurface");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setSurface != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(
        g_playerpluginClass.globalRef,g_playerpluginClass.setSurface, playerId, (jint)instanceId, (jlong)id);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call SetSurface has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }
    return Media::MSERR_OK;
}

int32_t PlayerJni::GetCurrentPosition(long key, int32_t &value)
{
    MEDIA_LOGD("PlayerJni GetCurrentPosition");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.getCurrentPosition != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    jint ret = env->CallIntMethod(
        g_playerpluginClass.globalRef,g_playerpluginClass.getCurrentPosition, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call GetCurrentPosition has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }
    value = static_cast<int32_t>(ret);
    return Media::MSERR_OK;
}

int32_t PlayerJni::GetDuration(long key, int32_t &value)
{
    MEDIA_LOGD("PlayerJni GetDuration");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.getDuration != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    jint ret = env->CallIntMethod(
        g_playerpluginClass.globalRef,g_playerpluginClass.getDuration, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call GetDuration has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }
    value = static_cast<int32_t>(ret);
    return Media::MSERR_OK;
}

int32_t PlayerJni::GetVideoWidth(long key)
{
    MEDIA_LOGD("PlayerJni GetVideoWidth");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.getVideoWidth != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    jint ret = env->CallIntMethod(
        g_playerpluginClass.globalRef,g_playerpluginClass.getVideoWidth, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call GetVideoWidth has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return static_cast<int32_t>(ret);
}

int32_t PlayerJni::GetVideoHeight(long key)
{
    MEDIA_LOGD("PlayerJni GetVideoHeight");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.getVideoHeight != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    jint ret = env->CallIntMethod(
        g_playerpluginClass.globalRef,g_playerpluginClass.getVideoHeight, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call GetVideoHeight has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return static_cast<int32_t>(ret);
}

int32_t PlayerJni::Seek(long key, int64_t mSeconds, Media::PlayerSeekMode mode)
{
    MEDIA_LOGD("PlayerJni Seek");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.seekTo != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(
        g_playerpluginClass.globalRef, g_playerpluginClass.seekTo, playerId, (jint)mSeconds, (jint)mode);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call Seek has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

int32_t PlayerJni::SetVolume(long key, float leftVolume, float rightVolume)
{
    MEDIA_LOGD("PlayerJni SetVolume");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setVolume != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef,
        g_playerpluginClass.setVolume, playerId, (jfloat)leftVolume, (jfloat)rightVolume);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call SetVolume has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

int32_t PlayerJni::SetLooping(long key, bool loop)
{
    MEDIA_LOGD("PlayerJni SetLooping");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setLooping != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef,
        g_playerpluginClass.setLooping, playerId, (jboolean)loop);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call SetLooping has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

int32_t PlayerJni::SetVideoScalingMode(long key, int32_t mode)
{
    MEDIA_LOGD("PlayerJni SetVideoScalingMode");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setVideoScalingMode != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef, g_playerpluginClass.setVideoScalingMode, playerId, (jint)mode);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call SetVideoScalingMode has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

bool PlayerJni::IsLooping(long key)
{
    MEDIA_LOGD("PlayerJni IsLooping");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, false);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, false);
    CHECK_AND_RETURN_RET(g_playerpluginClass.isLooping != nullptr, false);

    jlong playerId = (jlong)key;
    jboolean ret = env->CallBooleanMethod(g_playerpluginClass.globalRef, g_playerpluginClass.isLooping, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call IsLooping has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return false;
    }

    return static_cast<bool>(ret);
}

int32_t PlayerJni::SetSpeed(long key, Media::PlaybackRateMode mode)
{
    MEDIA_LOGD("PlayerJni SetSpeed");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setPlaybackParams != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef,
        g_playerpluginClass.setPlaybackParams, playerId, (jint)mode);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call SetSpeed has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

int32_t PlayerJni::GetSpeed(long key, Media::PlaybackRateMode &mode)
{
    MEDIA_LOGD("PlayerJni SetSpeed");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.getPlaybackParams != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    jint value = env->CallIntMethod(g_playerpluginClass.globalRef,
        g_playerpluginClass.getPlaybackParams, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call SetSpeed has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }
    mode = static_cast<Media::PlaybackRateMode>(value);
    return Media::MSERR_OK;
}

int32_t PlayerJni::SelectBitRate(long key, uint32_t bitRate)
{
    MEDIA_LOGD("PlayerJni SelectBitRate");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.selectBitrate != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef,
        g_playerpluginClass.selectBitrate, playerId, (jint)bitRate);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call SetSpeed has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

int32_t PlayerJni::setDataSource(long key, std::string path)
{
    MEDIA_LOGD("PlayerJni setDataSource");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setDataSourceWithUrl != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef,
        g_playerpluginClass.setDataSourceWithUrl, playerId, StringToJavaString(env, path));
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call setDataSource has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

int32_t PlayerJni::setDataSource(long key, int32_t fd, int64_t offset, int64_t size)
{
    MEDIA_LOGD("PlayerJni setDataSource");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setDataSourceWithFd != nullptr, Media::MSERR_SERVICE_DIED);

    char filePath[PATH_MAX] = {'\0'};
    char buf[128] = {'\0'};

    snprintf(buf, sizeof(buf), "proc/self/fd/%d", fd);

    CHECK_AND_RETURN_RET(readlink(buf, filePath, PATH_MAX) >= 0, Media::MSERR_SERVICE_DIED);
    MEDIA_LOGD("PlayerJni setDataSource   %{public}s", filePath);

    env->CallVoidMethod(g_playerpluginClass.globalRef, g_playerpluginClass.setDataSourceWithFd,
        (jlong)key, StringToJavaString(env, filePath), (jlong)offset, (jlong)size);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call setDataSourceWithFd has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_INVALID_OPERATION;
    }
    return Media::MSERR_OK;
}

int32_t PlayerJni::setDataSource(long key, const std::shared_ptr<Media::IMediaDataSource> &dataSrc)
{
    MEDIA_LOGD("PlayerJni setDataSource");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.setDataSource != nullptr, Media::MSERR_SERVICE_DIED);

    mediaDataSources_[key] = dataSrc;

    jlong playerId = (jlong)key;
    env->CallVoidMethod(g_playerpluginClass.globalRef, g_playerpluginClass.setDataSource, playerId);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call setDataSource has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    return Media::MSERR_OK;
}

int32_t PlayerJni::GetVideoTrackInfo(long key, std::vector<Media::Format> &videoTrack)
{
    return GetTrackInfo(key, videoTrack, TRACK_TYPE_VIDEO);
}

int32_t PlayerJni::GetAudioTrackInfo(long key, std::vector<Media::Format> &audioTrack)
{
    return GetTrackInfo(key, audioTrack, TRACK_TYPE_AUDIO);
}

int32_t PlayerJni::GetSubtitleTrackInfo(long key, std::vector<Media::Format> &subtitleTrack)
{
    return GetTrackInfo(key, subtitleTrack, TRACK_TYPE_SUBTITLE);
}

int32_t PlayerJni::GetTrackInfo(long key, std::vector<Media::Format> &Track, int type)
{
    MEDIA_LOGD("PlayerJni GetTrackInfo");
    auto env = ARKUI_X_Plugin_GetJniEnv();
    CHECK_AND_RETURN_RET(env != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.globalRef != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.getTrackInfoString != nullptr, Media::MSERR_SERVICE_DIED);
    CHECK_AND_RETURN_RET(g_playerpluginClass.getTrackIndex != nullptr, Media::MSERR_SERVICE_DIED);

    jlong playerId = (jlong)key;
    jint index = env->CallIntMethod(g_playerpluginClass.globalRef,
        g_playerpluginClass.getTrackIndex, playerId, (jint)type);
    if (env->ExceptionCheck()) {
        MEDIA_LOGE("PlayerJni JNI: call getTrackIndex has exception");
        env->ExceptionDescribe();
        env->ExceptionClear();
        return Media::MSERR_SERVICE_DIED;
    }

    if ((int)index < 0) {
        return Media::MSERR_OK;
    }

    Media::Format infoBody;
    infoBody.PutIntValue("track_type", type);
    infoBody.PutIntValue("track_index", (int)index);
    for (const auto &str : TRACK_KEYS) {
        jstring value = (jstring)env->CallObjectMethod(g_playerpluginClass.globalRef,
            g_playerpluginClass.getTrackInfoString, playerId, (jint)type, StringToJavaString(env, str));
        if (env->ExceptionCheck()) {
            MEDIA_LOGE("PlayerJni JNI: call GetTrackInfo has exception");
            env->ExceptionDescribe();
            env->ExceptionClear();
            return Media::MSERR_SERVICE_DIED;
        }
        const char* utf8Str = env->GetStringUTFChars(value, NULL);
        int len = strlen(utf8Str);
        if (len > 0) {
            std::string result(utf8Str, len);
            infoBody.PutStringValue(str, result);
        }
    }
    Track.emplace_back(infoBody);
    return Media::MSERR_OK;
}

int32_t PlayerJni::SetCallback(long key, const std::shared_ptr<Media::PlayerCallback> &callback)
{
    MEDIA_LOGD("PlayerJni SetCallback");
    if (callbacks_.find(key) != callbacks_.end()) {
        return Media::MSERR_OK;
    }
    callbacks_[key] = callback;
    return Media::MSERR_OK;
}

void PlayerJni::NativeOnInfo(JNIEnv *env, jobject jobj, jlong key, jint what, jint extra)
{
    MEDIA_LOGD("PlayerJni NativeOnInfo type = %{public}d", what);
    auto iter = callbacks_.find((long)key);
    if (iter == callbacks_.end()) {
        MEDIA_LOGD("PlayerJni NativeOnInfo callback not found");
        return;
    }
    Media::Format infoBody;
    (iter->second)->OnInfo((Media::PlayerOnInfoType)what, (int32_t)extra, infoBody);
}

void PlayerJni::NativeOnError(JNIEnv *env, jobject jobj, jlong key, jint code)
{
    auto iter = callbacks_.find((long)key);
    if (iter == callbacks_.end()) {
        MEDIA_LOGD("PlayerJni NativeOnError callback not found");
        return;
    }
    (iter->second)->OnError((int32_t)code, " ");
}

void PlayerJni::NativeOnBufferingUpdate(JNIEnv *env, jobject jobj, jlong key, jint percent)
{
    auto iter = callbacks_.find((long)key);
    if (iter == callbacks_.end()) {
        MEDIA_LOGD("PlayerJni NativeOnBufferingUpdate callback not found");
        return;
    }
    Media::Format infoBody;
    infoBody.PutIntValue(std::string(Media::PlayerKeys::PLAYER_BUFFERING_PERCENT), percent);
    (iter->second)->OnInfo(Media::INFO_TYPE_BUFFERING_UPDATE, 0, infoBody);
}

void PlayerJni::NativeOnSeekComplete(JNIEnv *env, jobject jobj, jlong key, jint position)
{
    auto iter = callbacks_.find((long)key);
    if (iter == callbacks_.end()) {
        MEDIA_LOGD("PlayerJni NativeOnSeekComplete callback not found");
        return;
    }
    Media::Format infoBody;
    (iter->second)->OnInfo(Media::INFO_TYPE_SEEKDONE, position, infoBody);
}

void PlayerJni::NativeOnVideoSizeChanged(JNIEnv *env, jobject jobj, jlong key, jint width, jint height)
{
    auto iter = callbacks_.find((long)key);
    if (iter == callbacks_.end()) {
        MEDIA_LOGD("PlayerJni NativeOnVideoSizeChanged callback not found");
        return;
    }
    Media::Format infoBody;
    infoBody.PutIntValue(Media::PlayerKeys::PLAYER_WIDTH, width);
    infoBody.PutIntValue(Media::PlayerKeys::PLAYER_HEIGHT, height);
    (iter->second)->OnInfo(Media::INFO_TYPE_RESOLUTION_CHANGE, 0, infoBody);
}

void PlayerJni::NativeOnVolumnChanged(JNIEnv *env, jobject jobj, jlong key, jfloat vol)
{
    auto iter = callbacks_.find((long)key);
    if (iter == callbacks_.end()) {
        MEDIA_LOGD("PlayerJni NativeOnVolumnChanged callback not found");
        return;
    }
    Media::Format infoBody;
    infoBody.PutFloatValue(Media::PlayerKeys::PLAYER_VOLUME_LEVEL, vol);
    (iter->second)->OnInfo(Media::INFO_TYPE_VOLUME_CHANGE, 0, infoBody);
}

jint PlayerJni::NativeReadAt(
        JNIEnv *env, jobject jthiz, jlong key, jlong position, jbyteArray buffer, jint offset, jint size)
{
    auto iter = mediaDataSources_.find((long)key);
    if (iter == mediaDataSources_.end()) {
        MEDIA_LOGD("PlayerJni NativeReadAt callback not found");
        return 0;
    }
    std::shared_ptr<Media::MockAVSharedMemory> dataSrc_ptr =
        std::make_shared<Media::MockAVSharedMemory>((int)size, 2);

    (iter->second)->ReadAt(dataSrc_ptr, (uint32_t)size, (int64_t)position);

    jbyte *data = (jbyte *)dataSrc_ptr->GetBase();

    env->SetByteArrayRegion(buffer, 0, (jint)dataSrc_ptr->GetSize(), data);
    return (jint)dataSrc_ptr->GetSize();
}
} // namespace OHOS::Plugin