/*
 * 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 "picture_in_picture_controller_ani.h"

#include <chrono>
#include "picture_in_picture_manager.h"
#include "float_window_manager.h"
#include "singleton_container.h"
#include "window_manager_hilog.h"

namespace OHOS {
namespace Rosen {
namespace {
    const char* ETS_NS = "@ohos.PiPWindow.PiPManager";
    const char* RUN_UPDATE_NODE_CALLBACK = "runUpdateNodeCallback";
    const char* RUN_STATE_CHANGE_CALLBACK = "runStateChangeCallback";
    const std::string PIP_CONTENT_PATH = "/system/framework/pipcontent_ani.abc";
    const std::string DESTROY_TIMEOUT_TASK = "PipDestroyTimeout";
    const std::string STATE_CHANGE = "stateChange";
    const std::string UPDATE_NODE = "nodeUpdate";
    const int32_t INVALID_HANDLE_ID = -1;
}

PictureInPictureControllerAni::PictureInPictureControllerAni(sptr<PipOptionAni> pipOption,
                                                             sptr<Window> mainWindow,
                                                             uint32_t windowId,
                                                             ani_env* env)
{
    PictureInPictureControllerBase::pipOption_ = pipOption;  // base class use the same pipOption_
    pipOption_ = pipOption;
    mainWindow_ = mainWindow;
    mainWindowId_ = windowId;
    curState_ = PiPWindowState::STATE_UNDEFINED;
    createTimestamp_ = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::system_clock::now().time_since_epoch()).count();
    if (pipOption_ != nullptr) {
        pipOption_->SetCreateTimestamp(createTimestamp_);
    }
    if (env->GetVM(&vm_) != ANI_OK || !vm_) {
        TLOGE(WmsLogTag::WMS_PIP, "PictureInPictureControllerAni Get VM failed");
    }
    weakRef_ = this;
    TLOGI(WmsLogTag::WMS_PIP, "PictureInPictureControllerAni created");
}

PictureInPictureControllerAni::~PictureInPictureControllerAni()
{
    TLOGI(WmsLogTag::WMS_PIP, "~PictureInPictureControllerAni start");
    if (!isAutoStartEnabled_) {
        return;
    }
    PictureInPictureManager::DetachAutoStartController(handleId_, weakRef_);
}

ani_env* PictureInPictureControllerAni::GetEnv() const
{
    ani_env* env_ = nullptr;
    ani_status ret = vm_->GetEnv(ANI_VERSION_1, &env_);
    if (ret != ANI_OK || !env_) {
        TLOGE(WmsLogTag::WMS_PIP, "PictureInPictureControllerAni Get Env failed ret:%{public}u", ret);
    }
    return env_;
}

WMError PictureInPictureControllerAni::CreatePictureInPictureWindow(StartPipType startType)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (pipOption_ == nullptr || pipOption_->GetContext() == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "Create pip failed, invalid pipOption");
        return WMError::WM_ERROR_PIP_CREATE_FAILED;
    }
    mainWindowXComponentController_ = pipOption_->GetXComponentController();
    if ((mainWindowXComponentController_ == nullptr && !IsTypeNodeEnabled()) || mainWindow_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "mainWindowXComponentController or mainWindow is nullptr");
        return WMError::WM_ERROR_PIP_CREATE_FAILED;
    }
    TLOGI(WmsLogTag::WMS_PIP, "mainWindow:%{public}u, mainWindowState:%{public}u",
        mainWindowId_, mainWindow_->GetWindowState());
    mainWindowLifeCycleListener_ =
        sptr<PictureInPictureControllerAni::WindowLifeCycleListener>::MakeSptr(mainWindowId_);
    mainWindow_->RegisterLifeCycleListener(mainWindowLifeCycleListener_);
    if (startType != StartPipType::AUTO_START && mainWindow_->GetWindowState() != WindowState::STATE_SHOWN) {
        TLOGE(WmsLogTag::WMS_PIP, "mainWindow is not shown. create failed.");
        return WMError::WM_ERROR_PIP_CREATE_FAILED;
    }
    UpdateWinRectByComponent();
    auto windowOption = sptr<WindowOption>::MakeSptr();
    windowOption->SetWindowName(MakePipWindowName(createTimestamp_));
    windowOption->SetWindowType(WindowType::WINDOW_TYPE_PIP);
    windowOption->SetWindowMode(WindowMode::WINDOW_MODE_PIP);
    windowOption->SetWindowRect(windowRect_);
    windowOption->SetKeepScreenOn(true);
    windowOption->SetTouchable(false);
    WMError errCode = WMError::WM_OK;
    PiPTemplateInfo pipTemplateInfo;
    pipOption_->GetPiPTemplateInfo(pipTemplateInfo);
    pipTemplateInfo.createTimestamp = createTimestamp_;
    auto context = static_cast<std::weak_ptr<AbilityRuntime::Context>*>(pipOption_->GetContext());
    const std::shared_ptr<AbilityRuntime::Context>& abilityContext = context->lock();
    SingletonContainer::Get<PiPReporter>().SetCurrentPackageName(abilityContext->GetApplicationInfo()->name);
    sptr<Window> window = FloatWindowManager::CreatePipWindow(windowOption, pipTemplateInfo, context->lock(), errCode);
    if (window == nullptr || errCode != WMError::WM_OK) {
        TLOGW(WmsLogTag::WMS_PIP, "Window create failed, reason: %{public}d", errCode);
        return errCode == WMError::WM_ERROR_FLOAT_CONFLICT_WITH_OTHERS ? errCode : WMError::WM_ERROR_PIP_CREATE_FAILED;
    }
    window_ = window;
    window_->UpdatePiPRect(windowRect_, WindowSizeChangeReason::PIP_START);
    PictureInPictureManager::PutPipControllerInfo(window_->GetWindowId(), this);
    return WMError::WM_OK;
}

WMError PictureInPictureControllerAni::StartPictureInPicture(StartPipType startType)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (pipOption_ == nullptr || pipOption_->GetContext() == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "pipOption is null or Get PictureInPictureOption failed");
        return WMError::WM_ERROR_PIP_CREATE_FAILED;
    }
    if (curState_ == PiPWindowState::STATE_STARTING || curState_ == PiPWindowState::STATE_STARTED) {
        TLOGW(WmsLogTag::WMS_PIP, "pipWindow is starting, state: %{public}u, id: %{public}u, mainWindow: %{public}u",
            curState_, (window_ == nullptr) ? INVALID_WINDOW_ID : window_->GetWindowId(), mainWindowId_);
        SingletonContainer::Get<PiPReporter>().ReportPiPStartWindow(static_cast<int32_t>(startType),
            pipOption_->GetPipTemplate(), PipConst::FAILED, "Pip window is starting");
        return WMError::WM_ERROR_PIP_REPEAT_OPERATION;
    }
    if (!IsPullPiPAndHandleNavigation()) {
        TLOGE(WmsLogTag::WMS_PIP, "Navigation operate failed");
        return WMError::WM_ERROR_PIP_CREATE_FAILED;
    }
    curState_ = PiPWindowState::STATE_STARTING;
    startTimestamp_ = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::system_clock::now().time_since_epoch()).count();
    bool reachLimit = PictureInPictureManager::IsPipGroupLimitReached(
        static_cast<PiPTemplateType>(pipOption_->GetPipTemplate()));
    if (reachLimit) {
        window_ = PictureInPictureManager::GetSameGroupWindowByMainWindowId(mainWindowId_,
            static_cast<PiPTemplateType>(pipOption_->GetPipTemplate()));
        if (window_ != nullptr) {
            TLOGI(WmsLogTag::WMS_PIP, "Reuse pipWindow: %{public}u as attached to the same mainWindow: %{public}u",
                window_->GetWindowId(), mainWindowId_);
            PictureInPictureManager::DoClose(window_->GetWindowId(), false, false);
            mainWindowXComponentController_ = IsTypeNodeEnabled() ? nullptr : pipOption_->GetXComponentController();
            UpdateWinRectByComponent();
            UpdateContentSize(windowRect_.width_, windowRect_.height_);
            PictureInPictureManager::PutPipControllerInfo(window_->GetWindowId(), this);
            WMError err = ShowPictureInPictureWindow(startType);
            if (err != WMError::WM_OK) {
                curState_ = PiPWindowState::STATE_UNDEFINED;
            } else {
                curState_ = PiPWindowState::STATE_STARTED;
            }
            return err;
        }
    }
    WMError errCode = StartPictureInPictureInner(startType);
    if (errCode != WMError::WM_OK) {
        DeletePIPMode();
    }
    return errCode;
}

void PictureInPictureControllerAni::SetAutoStartEnabled(bool enable)
{
    TLOGI(WmsLogTag::WMS_PIP, "start %{public}u, mainWindow: %{public}u", enable, mainWindowId_);
    isAutoStartEnabled_ = enable;
    if (mainWindow_ == nullptr) {
        return;
    }
    if (!pipOption_) {
        TLOGE(WmsLogTag::WMS_PIP, "pipOption is null");
        return;
    }
    uint32_t priority = pipOption_->GetPipPriority(pipOption_->GetPipTemplate());
    uint32_t contentWidth = 0;
    uint32_t contentHeight = 0;
    pipOption_->GetContentSize(contentWidth, contentHeight);
    if (isAutoStartEnabled_) {
        // cache navigation here as we cannot get containerId while BG
        if (!IsPullPiPAndHandleNavigation()) {
            TLOGE(WmsLogTag::WMS_PIP, "Navigation operate failed");
            isAutoStartEnabled_ = false;
            mainWindow_->SetAutoStartPiP(false, priority, contentWidth, contentHeight);
            return;
        }
        mainWindow_->SetAutoStartPiP(true, priority, contentWidth, contentHeight);
        PictureInPictureManager::AttachAutoStartController(handleId_, weakRef_);
    } else {
        mainWindow_->SetAutoStartPiP(false, priority, contentWidth, contentHeight);
        PictureInPictureManager::DetachAutoStartController(handleId_, weakRef_);
        if (IsTypeNodeEnabled()) {
            TLOGI(WmsLogTag::WMS_PIP, "typeNode enabled");
            return;
        }
        std::string navId = pipOption_->GetNavigationId();
        if (!navId.empty()) {
            auto navController = NavigationController::GetNavigationController(mainWindow_->GetUIContent(), navId);
            if (navController) {
                navController->DeletePIPMode(handleId_);
                TLOGI(WmsLogTag::WMS_PIP, "Delete pip mode id: %{public}d", handleId_);
            }
        }
    }
}

void PictureInPictureControllerAni::IsAutoStartEnabled(bool& enable) const
{
    enable = isAutoStartEnabled_;
    TLOGI(WmsLogTag::WMS_PIP, "IsAutoStartEnabled: %{public}u", enable);
}

void PictureInPictureControllerAni::SetUIContent() const
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    ani_object storage = static_cast<ani_object>(pipOption_->GetStorageRef());
    if (storage) {
        TLOGI(WmsLogTag::WMS_PIP, "startPiP with localStorage");
    }
    ani_env* env_ = GetEnv();
    window_->AniSetUIContentByAbc(PIP_CONTENT_PATH, env_, storage, nullptr);
}

void PictureInPictureControllerAni::UpdateContentSize(int32_t width, int32_t height)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (width <= 0 || height <= 0) {
        TLOGE(WmsLogTag::WMS_PIP, "invalid size");
        return;
    }
    if (pipOption_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "pipOption_ is nullptr");
        return;
    }
    if (mainWindow_ != nullptr) {
        TLOGI(WmsLogTag::WMS_PIP, "mainWindow width:%{public}u height:%{public}u", width, height);
        uint32_t priority = pipOption_->GetPipPriority(pipOption_->GetPipTemplate());
        uint32_t contentWidth = static_cast<uint32_t>(width);
        uint32_t contentHeight = static_cast<uint32_t>(height);
        mainWindow_->SetAutoStartPiP(isAutoStartEnabled_, priority, contentWidth, contentHeight);
    }
    pipOption_->SetContentSize(static_cast<uint32_t>(width), static_cast<uint32_t>(height));
    if (curState_ != PiPWindowState::STATE_STARTED) {
        TLOGD(WmsLogTag::WMS_PIP, "UpdateContentSize is disabled when state: %{public}u", curState_);
        return;
    }
    if (window_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "pipWindow not exist");
        return;
    }
    if (mainWindowXComponentController_ && !IsTypeNodeEnabled()) {
        float posX = 0;
        float posY = 0;
        float newWidth = 0;
        float newHeight = 0;
        mainWindowXComponentController_->GetGlobalPosition(posX, posY);
        mainWindowXComponentController_->GetSize(newWidth, newHeight);
        bool isSizeChange = IsContentSizeChanged(newWidth, newHeight, posX, posY);
        if (isSizeChange) {
            Rect r = {static_cast<int32_t>(posX), static_cast<int32_t>(posY),
                static_cast<uint32_t>(newWidth), static_cast<uint32_t>(newHeight)};
            window_->UpdatePiPRect(r, WindowSizeChangeReason::TRANSFORM);
        }
    }
    TLOGI(WmsLogTag::WMS_PIP, "window: %{public}u width:%{public}u height:%{public}u",
        window_->GetWindowId(), width, height);
    Rect rect = {0, 0, width, height};
    window_->UpdatePiPRect(rect, WindowSizeChangeReason::PIP_RATIO_CHANGE);
    SingletonContainer::Get<PiPReporter>().ReportPiPRatio(width, height);
}

void PictureInPictureControllerAni::UpdateContentNodeRef(ani_ref nodeRef)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (!pipOption_) {
        TLOGE(WmsLogTag::WMS_PIP, "option is null");
        SingletonContainer::Get<PiPReporter>().ReportPiPUpdateContent(static_cast<int32_t>(IsTypeNodeEnabled()),
            0, PipConst::FAILED, "option is null");
        return;
    }
    pipOption_->SetTypeNodeRef(nodeRef);
    if (IsTypeNodeEnabled()) {
        NotifyNodeUpdate(nodeRef);
        return;
    }
    ResetExtController();
    NotifyNodeUpdate(nodeRef);
    if (isAutoStartEnabled_) {
        std::string navId = pipOption_->GetNavigationId();
        if (!navId.empty()) {
            auto navController = NavigationController::GetNavigationController(mainWindow_->GetUIContent(), navId);
            if (navController) {
                navController->DeletePIPMode(handleId_);
                TLOGI(WmsLogTag::WMS_PIP, "Delete pip mode id: %{public}d", handleId_);
            }
        }
    }
    pipOption_->SetTypeNodeEnabled(true);
}

void PictureInPictureControllerAni::NotifyNodeUpdate(ani_ref nodeRef)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    ani_env* env_ = GetEnv();

    if (!nodeRef) {
        TLOGE(WmsLogTag::WMS_PIP, "invalid nodeRef");
        SingletonContainer::Get<PiPReporter>().ReportPiPUpdateContent(static_cast<int32_t>(IsTypeNodeEnabled()),
            pipOption_->GetPipTemplate(), PipConst::FAILED, "invalid nodeRef");
        return;
    }
    if (PictureInPictureManager::IsActiveController(weakRef_)) {
        ani_ref updateNodeCallbackRef = GetANIPipContentCallbackRef(UPDATE_NODE);
        if (!updateNodeCallbackRef) {
            TLOGE(WmsLogTag::WMS_PIP, "updateNodeCallbackRef is null");
            SingletonContainer::Get<PiPReporter>().ReportPiPUpdateContent(static_cast<int32_t>(IsTypeNodeEnabled()),
                pipOption_->GetPipTemplate(), PipConst::FAILED, "updateNodeCallbackRef is null");
            return;
        }

        ani_status ret {};
        ani_function fn {};
        ani_namespace ns {};
        if ((ret = env_->FindNamespace(ETS_NS, &ns)) != ANI_OK) {
            TLOGE(WmsLogTag::WMS_PIP, "Find namespace failed, ret: %{public}u", ret);
            return;
        }
        if ((ret = env_->Namespace_FindFunction(ns, RUN_UPDATE_NODE_CALLBACK, nullptr, &fn)) != ANI_OK) {
            TLOGE(WmsLogTag::WMS_PIP, "Find function failed, ret: %{public}u", ret);
            return;
        }

        ani_object typeNode = static_cast<ani_object>(nodeRef);
        env_->Function_Call_Void(fn, updateNodeCallbackRef, typeNode);
        SingletonContainer::Get<PiPReporter>().ReportPiPUpdateContent(static_cast<int32_t>(IsTypeNodeEnabled()),
            pipOption_->GetPipTemplate(), PipConst::PIP_SUCCESS, "updateNode success");
    }
    TLOGI(WmsLogTag::WMS_PIP, "finish");
}

void PictureInPictureControllerAni::PrepareSource()
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (IsTypeNodeEnabled()) {
        TLOGI(WmsLogTag::WMS_PIP, "typeNode enabled");
        return;
    }
    if (mainWindow_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "mainWindow is nullptr");
        return;
    }
    std::string navId = pipOption_->GetNavigationId();
    if (navId != "") {
        auto navController = NavigationController::GetNavigationController(mainWindow_->GetUIContent(), navId);
        if (navController) {
            navController->PushInPIP(handleId_);
            TLOGI(WmsLogTag::WMS_PIP, "Push in pip handleId: %{public}d", handleId_);
        } else {
            TLOGE(WmsLogTag::WMS_PIP, "navController is nullptr");
        }
    }
}

void PictureInPictureControllerAni::RestorePictureInPictureWindow()
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    StopPictureInPicture(true, StopPipType::NULL_STOP, true);
    SingletonContainer::Get<PiPReporter>().ReportPiPRestore();
    TLOGI(WmsLogTag::WMS_PIP, "restore pip main window finished");
}

void PictureInPictureControllerAni::UpdateWinRectByComponent()
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (IsTypeNodeEnabled()) {
        uint32_t contentWidth = 0;
        uint32_t contentHeight = 0;
        pipOption_->GetContentSize(contentWidth, contentHeight);
        if (contentWidth == 0 || contentHeight == 0) {
            contentWidth = PipConst::DEFAULT_ASPECT_RATIOS[0];
            contentHeight = PipConst::DEFAULT_ASPECT_RATIOS[1];
        }
        windowRect_.posX_ = 0;
        windowRect_.posY_ = 0;
        windowRect_.width_ = contentWidth;
        windowRect_.height_ = contentHeight;
        return;
    }
    if (!mainWindowXComponentController_) {
        TLOGE(WmsLogTag::WMS_PIP, "main window xComponent not set");
        return;
    }
    float posX = 0;
    float posY = 0;
    float width = 0;
    float height = 0;
    mainWindowXComponentController_->GetGlobalPosition(posX, posY);
    mainWindowXComponentController_->GetSize(width, height);
    windowRect_.width_ = static_cast<uint32_t>(width);
    windowRect_.height_ = static_cast<uint32_t>(height);
    if (windowRect_.width_ == 0 || windowRect_.height_ == 0) {
        uint32_t contentWidth = 0;
        uint32_t contentHeight = 0;
        pipOption_->GetContentSize(contentWidth, contentHeight);
        windowRect_.width_ = contentWidth;
        windowRect_.height_ = contentHeight;
    }
    windowRect_.posX_ = static_cast<int32_t>(posX);
    windowRect_.posY_ = static_cast<int32_t>(posY);
    TLOGD(WmsLogTag::WMS_PIP, "position width: %{public}u, height: %{public}u, posX: %{public}d, posY: %{public}d",
        windowRect_.width_, windowRect_.height_, windowRect_.posX_, windowRect_.posY_);
}

void PictureInPictureControllerAni::UpdatePiPSourceRect() const
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (IsTypeNodeEnabled() && window_ != nullptr) {
        Rect rect = {0, 0, 0, 0};
        TLOGI(WmsLogTag::WMS_PIP, "use typeNode, unable to locate source rect");
        window_->UpdatePiPRect(rect, WindowSizeChangeReason::PIP_RESTORE);
        return;
    }
    if (mainWindowXComponentController_ == nullptr || window_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "xcomponent controller not valid");
        return;
    }
    float posX = 0;
    float posY = 0;
    float width = 0;
    float height = 0;
    mainWindowXComponentController_->GetGlobalPosition(posX, posY);
    mainWindowXComponentController_->GetSize(width, height);
    Rect rect = { posX, posY, width, height };
    TLOGI(WmsLogTag::WMS_PIP, "result rect: [%{public}d, %{public}d, %{public}u, %{public}u]",
        rect.posX_, rect.posY_, rect.width_, rect.height_);
    window_->UpdatePiPRect(rect, WindowSizeChangeReason::PIP_RESTORE);
}

void PictureInPictureControllerAni::ResetExtController()
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (IsTypeNodeEnabled()) {
        TLOGI(WmsLogTag::WMS_PIP, "skip resetExtController as nodeController enabled");
        return;
    }
    if (!mainWindowXComponentController_ || !pipXComponentController_) {
        TLOGE(WmsLogTag::WMS_PIP, "error when resetExtController, one of the xComponentController is null");
        return;
    }
    XComponentControllerErrorCode errorCode =
        mainWindowXComponentController_->ResetExtController(pipXComponentController_);
    if (errorCode != XComponentControllerErrorCode::XCOMPONENT_CONTROLLER_NO_ERROR) {
        TLOGE(WmsLogTag::WMS_PIP, "swap xComponent failed, errorCode: %{public}u", errorCode);
    }
}

WMError PictureInPictureControllerAni::SetXComponentController(
    std::shared_ptr<XComponentController> xComponentController)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (IsTypeNodeEnabled()) {
        TLOGI(WmsLogTag::WMS_PIP, "skip as nodeController enabled");
        return WMError::WM_OK;
    }
    pipXComponentController_ = xComponentController;
    if (window_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "window is nullptr when set XComponentController");
        return WMError::WM_ERROR_PIP_STATE_ABNORMALLY;
    }
    if (mainWindowXComponentController_ == nullptr || pipXComponentController_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "error when setXController, one of the xComponentController is null");
        return WMError::WM_ERROR_PIP_STATE_ABNORMALLY;
    }
    XComponentControllerErrorCode errorCode =
        mainWindowXComponentController_->SetExtController(pipXComponentController_);
    if (errorCode != XComponentControllerErrorCode::XCOMPONENT_CONTROLLER_NO_ERROR) {
        TLOGE(WmsLogTag::WMS_PIP, "swap xComponent failed, errorCode: %{public}u", errorCode);
        return WMError::WM_ERROR_PIP_INTERNAL_ERROR;
    }
    OnPictureInPictureStart();
    return WMError::WM_OK;
}

WMError PictureInPictureControllerAni::RegisterPipContentListenerWithType(const std::string& type,
    ani_ref callbackRef)
{
    TLOGI(WmsLogTag::WMS_PIP, "Register type:%{public}s", type.c_str());
    if (pipOption_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "Get PictureInPicture option failed");
        return WMError::WM_ERROR_PIP_STATE_ABNORMALLY;
    }
    pipOption_->RegisterPipContentListenerWithType(type, callbackRef);
    return WMError::WM_OK;
}

WMError PictureInPictureControllerAni::UnRegisterPipContentListenerWithType(const std::string& type)
{
    TLOGI(WmsLogTag::WMS_PIP, "Unregister type:%{public}s", type.c_str());
    if (pipOption_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "Get PictureInPicture option failed");
        return WMError::WM_ERROR_PIP_STATE_ABNORMALLY;
    }
    pipOption_->UnRegisterPipContentListenerWithType(type);
    return WMError::WM_OK;
}

ani_ref PictureInPictureControllerAni::GetANIPipContentCallbackRef(const std::string& type)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    return pipOption_ == nullptr ? nullptr : pipOption_->GetANIPipContentCallbackRef(type);
}

void PictureInPictureControllerAni::NotifyStateChangeInner(PiPState state)
{
    ani_env* env_ = GetEnv();
    NotifyStateChangeInner(env_, state);
}

void PictureInPictureControllerAni::NotifyStateChangeInner(ani_env* env, PiPState state)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    ani_ref innerCallbackRef = GetANIPipContentCallbackRef(STATE_CHANGE);
    if (innerCallbackRef == nullptr) {
        return;
    }

    ani_status ret {};
    ani_function fn {};
    ani_namespace ns {};
    if ((ret = env->FindNamespace(ETS_NS, &ns)) != ANI_OK) {
        TLOGE(WmsLogTag::WMS_PIP, "Find namespace failed, ret: %{public}u", ret);
        return;
    }
    if ((ret = env->Namespace_FindFunction(ns, RUN_STATE_CHANGE_CALLBACK, nullptr, &fn)) != ANI_OK) {
        TLOGE(WmsLogTag::WMS_PIP, "Find function failed, ret: %{public}u", ret);
        return;
    }
    
    env->Function_Call_Void(fn, innerCallbackRef, state);
    TLOGI(WmsLogTag::WMS_PIP, "end");
}

bool PictureInPictureControllerAni::IsTypeNodeEnabled() const
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    return pipOption_ != nullptr ? pipOption_->IsTypeNodeEnabled() : false;
}

bool PictureInPictureControllerAni::IsPullPiPAndHandleNavigation()
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (IsTypeNodeEnabled()) {
        TLOGI(WmsLogTag::WMS_PIP, "App use typeNode");
        return true;
    }
    if (pipOption_->GetNavigationId() == "") {
        TLOGI(WmsLogTag::WMS_PIP, "App not use navigation");
        return true;
    }
    if (!mainWindow_) {
        TLOGE(WmsLogTag::WMS_PIP, "Main window init error");
        return false;
    }
    std::string navId = pipOption_->GetNavigationId();
    int32_t pipOptionHandleId = pipOption_->GetHandleId();
    auto navController = GetNavigationController(navId);
    if (!navController) {
        TLOGE(WmsLogTag::WMS_PIP, "Get navController error");
        return false;
    }

    if (pipOptionHandleId != INVALID_HANDLE_ID) {
        navController->SetInPIPMode(pipOptionHandleId);
        handleId_ = pipOptionHandleId;
        TLOGI(WmsLogTag::WMS_PIP, "SetInPIPMode handleId_: %{public}d, top result: %{public}d",
            handleId_, navController->GetTopHandle());
        return true;
    }

    if (!navController->IsNavDestinationInTopStack()) {
        TLOGE(WmsLogTag::WMS_PIP, "Top is not navDestination");
        return false;
    }

    TLOGI(WmsLogTag::WMS_PIP, "IsPullPiPAndHandleNavigation IsNavDestinationInTopStack");
    handleId_ = navController->GetTopHandle();
    if (handleId_ == INVALID_HANDLE_ID) {
        TLOGE(WmsLogTag::WMS_PIP, "Get top handle error");
        return false;
    }
    if (firstHandleId_ != INVALID_HANDLE_ID) {
        handleId_ = firstHandleId_;
        navController->SetInPIPMode(handleId_);
        TLOGI(WmsLogTag::WMS_PIP, "Cache first navigation");
    } else {
        TLOGI(WmsLogTag::WMS_PIP, "First top handle id: %{public}d", handleId_);
        firstHandleId_ = handleId_;
        navController->SetInPIPMode(handleId_);
    }
    return true;
}

std::string PictureInPictureControllerAni::GetPiPNavigationId() const
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    return (pipOption_ != nullptr && !IsTypeNodeEnabled()) ? pipOption_->GetNavigationId() : "";
}

ani_ref PictureInPictureControllerAni::GetANICustomNodeController()
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    return pipOption_ == nullptr ? nullptr : pipOption_->GetNodeControllerRef();
}

ani_ref PictureInPictureControllerAni::GetANITypeNode() const
{
    TLOGI(WmsLogTag::WMS_PIP, "GetTypeNode start");
    return pipOption_ == nullptr ? nullptr : pipOption_->GetTypeNodeRef();
}

NavigationController* PictureInPictureControllerAni::GetNavigationController(const std::string& navId)
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    return NavigationController::GetNavigationController(mainWindow_->GetUIContent(), navId);
}

void PictureInPictureControllerAni::DeletePIPMode()
{
    TLOGI(WmsLogTag::WMS_PIP, "start");
    if (mainWindow_ == nullptr) {
        TLOGE(WmsLogTag::WMS_PIP, "Main window is null");
        return;
    }
    std::string navId = pipOption_->GetNavigationId();
    if (!navId.empty()) {
        auto navController = GetNavigationController(navId);
        if (navController) {
            navController->DeletePIPMode(handleId_);
            TLOGI(WmsLogTag::WMS_PIP, "Delete pip mode id: %{public}d", handleId_);
        }
    }
}
} // namespace Rosen
} // namespace OHOS