/*
 * Copyright (c) 2025 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 "thread/thread_holder-inl.h"

#include "common_components/common_runtime/hooks.h"
#include "common_components/mutator/mutator.h"
#include "common_components/mutator/thread_local.h"
#include "base_runtime.h"
#include "thread/base_thread.h"
#include "thread/thread_holder_manager.h"
#include "thread/thread_state_transition.h"

#ifdef PANDA_JS_ETS_HYBRID_MODE
namespace common {
InterOpCoroutineToNativeHookFunc interOpCoroutineToNativeHook = nullptr;
InterOpCoroutineToRunningHookFunc interOpCoroutineToRunningHook = nullptr;

bool InterOpCoroutineToNative([[maybe_unused]] ThreadHolder *current)
{
#ifndef CMC_LCOV_EXCL
    if (interOpCoroutineToNativeHook == nullptr) {
        return false;
    }
    return interOpCoroutineToNativeHook(current);
#else
    return false;
#endif
}

bool InterOpCoroutineToRunning([[maybe_unused]] ThreadHolder *current)
{
#ifndef CMC_LCOV_EXCL
    if (interOpCoroutineToRunningHook == nullptr) {
        return false;
    }
    return interOpCoroutineToRunningHook(current);
#else
    return false;
#endif
}

void RegisterInterOpCoroutineToNativeHook(InterOpCoroutineToNativeHookFunc func)
{
    interOpCoroutineToNativeHook = func;
}
void RegisterInterOpCoroutineToRunningHook(InterOpCoroutineToRunningHookFunc func)
{
    interOpCoroutineToRunningHook = func;
}
}
#endif

namespace common {
thread_local ThreadHolder *currentThreadHolder = nullptr;

ThreadHolder *ThreadHolder::CreateAndRegisterNewThreadHolder(void *vm)
{
    if (ThreadLocal::IsArkProcessor()) {
        LOG_COMMON(FATAL) << "CreateAndRegisterNewThreadHolder fail";
        return nullptr;
    }
    Mutator* mutator = Mutator::NewMutator();
    CHECK_CC(mutator != nullptr);
    mutator->SetEcmaVMPtr(vm);
    ThreadHolder *holder = mutator->GetThreadHolder();
    BaseRuntime::GetInstance()->GetThreadHolderManager().RegisterThreadHolder(holder);
    return holder;
}

void ThreadHolder::ReleaseAllocBuffer()
{
    ThreadManagedScope scope(this);
    if (allocBuffer_) {
        auto buf = reinterpret_cast<AllocationBuffer*>(allocBuffer_);
        if (buf->DecreaseRefCount()) {
            buf->Unregister();
            delete buf;
        }
        allocBuffer_ = nullptr;
    }
}

void ThreadHolder::DestroyThreadHolder(ThreadHolder *holder)
{
    holder->ReleaseAllocBuffer();
    holder->TransferToNative();
    BaseRuntime::GetInstance()->GetThreadHolderManager().UnregisterThreadHolder(holder);
}

ThreadHolder *ThreadHolder::GetCurrent()
{
    return currentThreadHolder;
}

void ThreadHolder::SetCurrent(ThreadHolder *holder)
{
    currentThreadHolder = holder;
}

void ThreadHolder::RegisterJSThread(JSThread *jsThread)
{
    DCHECK_CC(!IsInRunningState());
    TransferToRunning();
    DCHECK_CC(jsThread_ == nullptr);
    jsThread_ = jsThread;
    mutatorBase_->RegisterJSThread(jsThread);
    SynchronizeGCPhaseToJSThread(jsThread, mutatorBase_->GetMutatorPhase());
    TransferToNative();
}

void ThreadHolder::UnregisterJSThread([[maybe_unused]] JSThread *jsThread)
{
#ifndef CMC_LCOV_EXCL
    DCHECK_CC(!IsInRunningState());
    TransferToRunning();
    DCHECK_CC(jsThread_ == jsThread);
    jsThread_ = nullptr;
    mutatorBase_->UnregisterJSThread();
    if (coroutines_.empty()) {
        ThreadHolder::DestroyThreadHolder(this);
    }
#endif
}

void ThreadHolder::RegisterCoroutine(Coroutine *coroutine)
{
    // Expect in Native when calling this func
    ThreadManagedScope scope(this);
    DCHECK_CC(coroutines_.find(coroutine) == coroutines_.end());
    coroutines_.insert(coroutine);
}

void ThreadHolder::UnregisterCoroutine([[maybe_unused]] Coroutine *coroutine)
{
#ifndef CMC_LCOV_EXCL
    DCHECK_CC(!IsInRunningState());
    TransferToRunning();
    DCHECK_CC(coroutines_.find(coroutine) != coroutines_.end());
    coroutines_.erase(coroutine);
    if (coroutines_.empty() && jsThread_ == nullptr) {
        ThreadHolder::DestroyThreadHolder(this);
    }
#endif
}

bool ThreadHolder::TryBindMutator()
{
    if (ThreadLocal::IsArkProcessor()) {
        return false;
    }

    BaseRuntime::GetInstance()->GetThreadHolderManager().BindMutator(this);
    allocBuffer_ = ThreadLocal::GetAllocBuffer();
    reinterpret_cast<AllocationBuffer*>(allocBuffer_)->IncreaseRefCount();
    DCHECK_CC(allocBuffer_ != nullptr);
    return true;
}

void ThreadHolder::BindMutator()
{
#ifndef CMC_LCOV_EXCL
    if (!TryBindMutator()) {
        LOG_COMMON(FATAL) << "BindMutator fail";
        return;
    }
#endif
}

void ThreadHolder::UnbindMutator()
{
    allocBuffer_ = nullptr;
    BaseRuntime::GetInstance()->GetThreadHolderManager().UnbindMutator(this);
}

void ThreadHolder::WaitSuspension()
{
    mutatorBase_->HandleSuspensionRequest();
}

void ThreadHolder::VisitAllThreads(CommonRootVisitor visitor)
{
    if (jsThread_ != nullptr) {
        VisitJSThread(jsThread_, visitor);
    }
    for ([[maybe_unused]] auto *coroutine : coroutines_) {
        // Depending on the integrated so
    }
}

ThreadHolder::TryBindMutatorScope::TryBindMutatorScope(ThreadHolder *holder) : holder_(nullptr)
{
    if (holder->TryBindMutator()) {
        holder_ = holder;
    }
}

ThreadHolder::TryBindMutatorScope::~TryBindMutatorScope()
{
    if (holder_ != nullptr) {
        holder_->ReleaseAllocBuffer();
        holder_->UnbindMutator();
        holder_ = nullptr;
    }
}
} // namespace common