// Copyright (c) Huawei Technologies Co., Ltd. 2026. 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 declares auxiliary methods for Cangjie Native FFI implementation with different targets
 */
#ifndef CANGJIE_SEMA_NATIVE_FFI_UTILS
#define CANGJIE_SEMA_NATIVE_FFI_UTILS

#include "cangjie/AST/Create.h"
#include "cangjie/AST/Match.h"
#include "cangjie/AST/Utils.h"
#include "cangjie/Mangle/BaseMangler.h"
#include "cangjie/Modules/ImportManager.h"
#include "cangjie/Sema/TypeManager.h"

namespace Cangjie::Native::FFI {
constexpr std::string_view NSSTRING_CLASS_IDENT = "NSString";
constexpr std::string_view NSOBJECT_CLASS_IDENT = "NSObject";
constexpr std::string_view TOSTRING_METHOD_IDENT = "toString";
constexpr std::string_view INIT_IDENT = "init";

using namespace AST;

/*
Generic Config Example:
generic_object_configuration = [
    { name = "GenericClass", type_arguments = ["Int32"]},
    { name = "GenericClass<Int32>", symbols = [
        "getValue",
        "GenericClass",
        "value",
        "setValue"
    ]}
]
*/
struct GenericConfigInfo {
    // Reference type symbol name
    std::string declSymbolName;
    // Definition name with generics, such as: GenericClassint32
    std::string declInstName;
    // item: <"T", "int32">
    std::vector<std::pair<std::string, std::string>> instTypes;
    // Config func symbol name
    std::unordered_set<std::string> funcNames;
    GenericConfigInfo(std::string name, std::string declInstName,
        std::vector<std::pair<std::string, std::string>>& insts, std::unordered_set<std::string>& funcs)
        : declSymbolName(name), declInstName(declInstName), instTypes(insts), funcNames(funcs)
    {
    }
};

enum class ArrayOperationKind : uint8_t { CREATE, GET, SET, GET_LENGTH };

OwnedPtr<RefExpr> CreateThisRef(Ptr<Decl> target, Ptr<Ty> ty, Ptr<File> curFile);

OwnedPtr<CallExpr> CreateThisCall(
    Decl& target, FuncDecl& baseTarget, Ptr<Ty> funcTy, Ptr<File> curFile, std::vector<OwnedPtr<FuncArg>> args = {});

OwnedPtr<PrimitiveType> CreateUnitType(Ptr<File> curFile);

std::vector<Ptr<Ty>> GetParamTys(FuncParamList& params);

OwnedPtr<RefExpr> CreateSuperRef(Ptr<Decl> target, Ptr<Ty> ty);

OwnedPtr<CallExpr> CreateSuperCall(Decl& target, FuncDecl& baseTarget, Ptr<Ty> funcTy);

ArrayOperationKind GetArrayOperationKind(Decl& decl);

template <typename Ret = Node, typename... Args> std::vector<OwnedPtr<Ret>> Nodes(OwnedPtr<Args>&&... args)
{
    std::vector<OwnedPtr<Ret>> nodes;
    (nodes.push_back(std::forward<OwnedPtr<Args>>(args)), ...);
    return nodes;
}

namespace Details {

template <typename T> void WrapArg(std::vector<OwnedPtr<FuncArg>>* funcArgs, OwnedPtr<T>&& e)
{
    CJC_ASSERT(e);
    if (auto ptr = As<ASTKind::FUNC_ARG>(e.get())) {
        funcArgs->emplace_back(ptr);
    } else {
        funcArgs->push_back(CreateFuncArg(std::forward<OwnedPtr<T>>(e)));
    }
}

} // namespace Details

template <typename T> OwnedPtr<T> WithinFile(OwnedPtr<T> node, Ptr<File> curFile)
{
    CJC_NULLPTR_CHECK(curFile);
    node->curFile = curFile;
    return node;
}

template <typename... Args> OwnedPtr<CallExpr> CreateCall(Ptr<FuncDecl> fd, Ptr<File> curFile, OwnedPtr<Args>&&... args)
{
    if (!fd) {
        return nullptr;
    }

    std::vector<OwnedPtr<FuncArg>> funcArgs;

    (Details::WrapArg(&funcArgs, std::forward<OwnedPtr<Args>>(args)), ...);

    auto funcTy = StaticCast<FuncTy*>(fd->GetTy());

    return CreateCallExpr(WithinFile(CreateRefExpr(*fd), curFile), std::move(funcArgs), fd, funcTy->retTy,
        CallKind::CALL_DECLARED_FUNCTION);
}

template <typename... Args>
OwnedPtr<CallExpr> CreateMemberCall(OwnedPtr<Expr> receiver, Ptr<FuncDecl> fd, OwnedPtr<Args>&&... args)
{
    CJC_NULLPTR_CHECK(receiver);
    CJC_NULLPTR_CHECK(fd);
    std::vector<OwnedPtr<FuncArg>> funcArgs;

    (Details::WrapArg(&funcArgs, std::forward<OwnedPtr<Args>>(args)), ...);

    auto funcTy = StaticCast<FuncTy*>(fd->GetTy());
    auto ma = CreateMemberAccess(std::move(receiver), *fd);
    CopyBasicInfo(ma->baseExpr, ma);
    return CreateCallExpr(std::move(ma), std::move(funcArgs), fd, funcTy->retTy, CallKind::CALL_DECLARED_FUNCTION);
}

OwnedPtr<Type> CreateType(Ptr<Ty> ty);
OwnedPtr<Type> CreateFuncType(Ptr<FuncTy> ty);

OwnedPtr<Expr> CreateBoolMatch(
    OwnedPtr<Expr> selector, OwnedPtr<Expr> trueBranch, OwnedPtr<Expr> falseBranch, Ptr<Ty> ty);

StructDecl& GetStringDecl(const ImportManager& importManager);

/**
 * Returns synthetic lambda call that includes nodes. The result of the call expr is the last node:
 *
 * {
 *     node1;
 *     node2;
 *     ...
 *     return noden;
 * }()
 */
OwnedPtr<CallExpr> WrapReturningLambdaCall(TypeManager& typeManager, std::vector<OwnedPtr<Node>> nodes);

/**
 * Returns lambda expression of nodes returning `Unit`.
 */
OwnedPtr<LambdaExpr> WrapUnitLambdaExpr(TypeManager& typeManager, std::vector<OwnedPtr<Node>> nodes,
    std::vector<OwnedPtr<FuncParam>> lambdaParams = {});
OwnedPtr<LambdaExpr> WrapReturningLambdaExpr(
    TypeManager& typeManager, std::vector<OwnedPtr<Node>> nodes, std::vector<OwnedPtr<FuncParam>> lambdaParams = {});

/**
 * Returns trimmed cangjie library name.
 * For a filename in [outputLibPath] matched to "lib{libname}.{ext}" it returns {libname} if [trimmed] = `true`
 * and "lib{libname}.{ext}" if [trimmed] = `false`.
 * For other cases, it returns [fullPackageName]
 */
std::string GetCangjieLibName(
    const std::string& outputLibPath, const std::string& fullPackageName, bool trimmed = true);

std::string GetMangledMethodName(const BaseMangler& mangler, const std::vector<OwnedPtr<FuncParam>>& params,
    const std::string& methodName, TypeManager& typeManager, GenericConfigInfo* genericConfig = nullptr);

Ptr<Annotation> GetForeignNameAnnotation(const Decl& decl);
Ptr<Annotation> GetAnnotation(const Decl& decl, AnnotationKind annotationKind);

Ptr<std::string> GetSingleArgumentAnnotationValue(const Decl& target, AnnotationKind annotationKind);
std::string GetObjCMirrorForeignName(const ClassLikeDecl& target);
bool IsObjCGeneratedNSStringCtor(const Decl& target);
bool IsObjCGeneratedNSObjectToString(const Decl& target);
bool IsObjCGeneratedMember(const Decl& target);

bool IsSuperConstructorCall(const CallExpr& call);
bool IsThisConstructorCall(const CallExpr& call);

OwnedPtr<PrimitiveType> GetPrimitiveType(std::string typeName, AST::TypeKind typekind);
OwnedPtr<Type> GetTypeByName(std::string typeStr);
OwnedPtr<Type> GetGenericInstType(const GenericConfigInfo* config, std::string genericName);
OwnedPtr<Type> GetGenericInstType(const GenericConfigInfo* config, const Ptr<Ty>& genericTy, TypeManager& typeManager);
std::string GetGenericActualType(const GenericConfigInfo* config, std::string genericName);
TypeKind GetActualTypeKind(std::string configType);
Ptr<Ty> GetGenericInstTy(const GenericConfigInfo* config, std::string genericName);
Ptr<Ty> GetGenericInstTy(const GenericConfigInfo* config, const Ptr<Ty>& genericTy, TypeManager& typeManager);
Ptr<Ty> GetTyByName(std::string typeStr);

bool IsGenericParam(const Ptr<Ty> ty, const AST::Decl& decl, Native::FFI::GenericConfigInfo* genericConfig);

bool IsVisibalFunc(const FuncDecl& funcDecl, const AST::Decl& decl, Native::FFI::GenericConfigInfo* genericConfig);

bool IsCJMappingGeneric(const Decl& decl);

void SplitAndTrim(std::string str, std::vector<std::string>& types);

std::string JoinVector(const std::vector<std::string>& vec, const std::string& delimiter = "");

void InitGenericConfigs(
    const File& file, const AST::Decl* decl, std::vector<GenericConfigInfo*>& genericConfigs, bool& isGenericGlueCode);

/**
 * If function param or return param is generic ty, replace it to instance ty by genericConfig.
 */
void ReplaceGenericTyForFunc(Ptr<FuncDecl> funcDecl, GenericConfigInfo* genericConfig, TypeManager& typeManager);

void ReplaceGenericTyForFuncTy(Ptr<Ty> ty, GenericConfigInfo* genericConfig, TypeManager& typeManager);

void GetArgsAndRetGenericActualTyVector(const GenericConfigInfo* config, FuncDecl& ctor,
    std::unordered_map<std::string, Ptr<Ty>>& actualTyArgMap, std::vector<Ptr<Ty>>& funcTyParams,
    std::vector<OwnedPtr<Type>>& actualPrimitiveType, TypeManager& typeManager);

Ptr<Ty> GetInstantyForGenericTy(
    Decl& decl, const std::unordered_map<std::string, Ptr<Ty>>& actualTyArgMap, TypeManager& typeManager);

std::string GetLambdaJavaClassName(LambdaPattern& pattern);
std::string GetLambdaJavaClassName(Ptr<Ty> ty);

std::string GetCjMappingTupleName(const Ty& tupleTy);

ClassDecl& GetExceptionDecl(const ImportManager& importManager);
OwnedPtr<ThrowExpr> CreateThrowExceptionCall(const ImportManager& importManager,
    TypeManager& typeManager, const std::string& msg, Ptr<File> curFile);
bool AreParamTypeKindsValid(const FuncDecl& fd, const std::vector<TypeKind>& typeKinds);

} // namespace Cangjie::Native::FFI

#endif // CANGJIE_SEMA_NATIVE_FFI_UTILS