// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

/**
* @file
*
* This file implements codegen for CHIR Spawn creation.
*/

#include "Base/SpawnExprImpl.h"

#include "Base/CHIRExprWrapper.h"
#include "CGModule.h"
#include "IRBuilder.h"
#include "Utils/CGUtils.h"
#include "cangjie/CHIR/IR/Expression/Terminator.h"
#include "cangjie/CHIR/IR/Type/ClassDef.h"
#include "cangjie/CHIR/IR/Type/Type.h"
#include "cangjie/CHIR/IR/Value/Value.h"

using namespace Cangjie;
using namespace CodeGen;

namespace {
// Check the spawn result and throw an exception if necessary.
void GenerateCheckSpawnResult(IRBuilder2& irBuilder, llvm::Value* result)
{
#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
    auto i8PtrTy = irBuilder.getInt8PtrTy();
    auto failure = llvm::ConstantPointerNull::get(i8PtrTy);

    auto compare = irBuilder.CreateICmpEQ(result, failure);
    auto [throwBB, endBB] =
        Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("if.throw"), GenNameForBB("if.end")}));
    (void)irBuilder.CreateCondBr(compare, throwBB, endBB);

    irBuilder.SetInsertPoint(throwBB);
    irBuilder.CreateSpawnException();
    irBuilder.CreateUnreachable();
    irBuilder.SetInsertPoint(endBB);
#endif
}

llvm::Value* GenerateSpawnWithExecuteFuture(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn)
{
    auto& cgMod = irBuilder.GetCGModule();
    auto futureObj = cgMod | spawn.GetFuture();

    std::optional<CGValue*> threadCtx = std::nullopt;
    if (spawn.GetSpawnArg()) {
        threadCtx = cgMod | spawn.GetSpawnArg();
    }

    auto methods =
        StaticCast<CHIR::ClassType*>(spawn.GetFuture()->GetType()->GetTypeArgs()[0])->GetClassDef()->GetMethods();
    auto it = std::find_if(
        methods.begin(), methods.end(), [](auto method) { return method->GetSrcCodeIdentifier() == "execute"; });
    CJC_ASSERT_WITH_MSG(it != methods.end(), "The execute function is not found in Future.");
    auto cgFunc = cgMod.GetOrInsertCGFunction(*it);
    CJC_NULLPTR_CHECK(cgFunc);
    auto cjThreadHandle = irBuilder.CallSpawnIntrinsic(*futureObj, *cgFunc, threadCtx, true);
    CJC_NULLPTR_CHECK(cjThreadHandle);

    GenerateCheckSpawnResult(irBuilder, cjThreadHandle);

#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
    // The first field of `Future` must be a `Thread` object.
    auto threadObj =
        irBuilder.CreateLoad(irBuilder.getInt8PtrTy(1), irBuilder.CreateGEP(*futureObj, {0}).GetRawValue());
    // Call the setter function.
    auto& llvmCtx = irBuilder.GetLLVMContext();
    auto tiNullVal = llvm::ConstantPointerNull::get(CGType::GetOrCreateTypeInfoPtrType(llvmCtx));
    irBuilder.CreateSetRuntimeCJThreadHandleCall(
        {irBuilder.CreateEntryAlloca(CGType::GetUnitType(llvmCtx)), threadObj, cjThreadHandle, tiNullVal});
#endif
    return futureObj->GetRawValue();
}

llvm::Value* GenerateSpawnWithExecuteClosure(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn)
{
    auto& cgMod = irBuilder.GetCGModule();
    auto closureNode = spawn.GetClosure();
    auto closure = cgMod | closureNode;

    std::optional<CGValue*> threadCtx = std::nullopt;
    if (spawn.GetSpawnArg()) {
        threadCtx = cgMod | spawn.GetSpawnArg();
    }

    CJC_NULLPTR_CHECK(spawn.GetExecuteClosure());
    auto cgFunc = cgMod.GetOrInsertCGFunction(spawn.GetExecuteClosure());
    auto result = irBuilder.CallSpawnIntrinsic(*closure, *cgFunc, threadCtx,
        false, irBuilder.CreateTypeInfo(*spawn.GetResult()->GetType()));
    CJC_NULLPTR_CHECK(result);

    GenerateCheckSpawnResult(irBuilder, result);
    // The return value of the spawn expression (generated by this function) will not be used
    // so, just return nullptr.
    return nullptr;
}
} // namespace

/**
 * Generate the spawn expression.
 * A spawn expression has two types of CHIR node shapes.
 * 1. Spawn(thunk { EXECUTE[ futureObj, threadCtx? ] })
 *   Create a thread and pass a future object as the argument,
 *   the thread entry function is `Future.execute`.
 * 2. Spawn(thunk { EXECUTE_CLOSURE[ closure, threadCtx? ] })
 *   Create a thread and pass a closure as the argument,
 *   the thread entry function is `Future.executeClosure`.
 * A spawn expression may contains a thread context, like `spawn(ctx) { ... }`.
 */
llvm::Value* CodeGen::GenerateSpawn(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn)
{
    if (!spawn.IsExecuteClosure()) {
        return GenerateSpawnWithExecuteFuture(irBuilder, spawn);
    } else {
        // generate for spawn with closure.
        return GenerateSpawnWithExecuteClosure(irBuilder, spawn);
    }
    // Determine which type of the spawn expression to generate
}