/*
 * 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.
 */

import * as fs from 'fs'
import * as path from 'path'
import * as idl from '@idlizer/core/idl'
import {
    asPromise,
    createConstructor,
    createMethod,
    createParameter,
    forceAsNamedNode,
    getFQName,
    hasExtAttribute,
    IDLCallback,
    IDLConstructor,
    IDLEnum,
    IDLExtendedAttributes,
    IDLInterface,
    IDLMethod,
    IDLNumberType,
    IDLParameter,
    IDLPrimitiveType,
    IDLProperty,
    IDLType,
    IDLVoidType,
    isConstructor,
    isContainerType,
    isEnum,
    isInterface,
    isOptionalType,
    isReferenceType,
    isUnionType,
    linearizeNamespaceMembers
} from '@idlizer/core/idl'
import {
    ArgConvertor,
    capitalize,
    generateCallbackAPIArguments,
    generatorConfiguration,
    generatorTypePrefix,
    IndentedPrinter,
    Language,
    LanguageWriter,
    qualifiedName,
    isMaterialized,
    PeerLibrary,
    CppLanguageWriter,
    MethodSignature,
    PrimitiveTypesInstance,
    CppConvertor,
    CppReturnTypeConvertor,
    isStructureType,
    PeerMethod,
    dropSuffix,
    MaterializedClass,
    isInIdlize,
    isStaticMaterialized,
    isInCurrentModule,
    currentModule,
    sorted,
    getSuper,
    PeerMethodSignature,
    createOutArgConvertor,
} from '@idlizer/core'
import {
    getUniquePropertiesFromSuperTypes,
    printCallbacksKinds,
    printManagedCaller,
    // writeDeserializer,
    // writeSerializer,
    CppSourceFile,
    StructPrinter,
    TargetFile,
    BridgeApi,
    BridgeCcVisitor,
    BridgeHeaderVisitor,
    createSyntheticGlobalScope,
    isGlobalScope,
    createSerializerPrinter,
    createDeserializeAndCallPrinter,
    readTemplate,
    peerGeneratorConfiguration,
    libraryCcDeclaration,
    createCSerializerPrinter,
    getDeclarationUniqueName,
    printKotlinCInteropDefFile,
    readInteropTypesHeader
} from '@idlizer/libohos'
import { OhosInstall } from './OhosInstall'

class NameType {
    constructor(public name: string, public type: string) { }
}

interface SignatureDescriptor {
    params: NameType[]
    returnType: string
    paramsCString?: string
}

const apiGetterTemplate = `const OH_AnyAPI* GetAnyImpl(int kind, int version, std::string* result = nullptr);
static const %API_NAME%* Get%API_NAME%(int32_t apiVersion) {
    return reinterpret_cast<const %API_NAME%*>(
        GetAnyImpl(static_cast<int>(%API_KIND%),
        apiVersion, nullptr));
}`

const apiImplPrologueTemplate = `/*
 * 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 "%API_HEADER_PATH%"

#define KOALA_INTEROP_MODULE %INTEROP_MODULE_NAME%
#include "common-interop.h"
#include "callback-resource.h"
#include "SerializerBase.h"
#include "DeserializerBase.h"
#include <unordered_map>

#if defined(KOALA_USE_PANDA_VM)
    #if defined(KOALA_ETS_NAPI)
        KOALA_ETS_INTEROP_MODULE_CLASSPATH(KOALA_INTEROP_MODULE, KOALA_QUOTE(ETS_MODULE_CLASSPATH_PREFIX) KOALA_QUOTE(KOALA_INTEROP_MODULE));
    #elif defined(KOALA_ANI)
        KOALA_ANI_INTEROP_MODULE_CLASSPATH(KOALA_INTEROP_MODULE, KOALA_QUOTE(ETS_MODULE_CLASSPATH_PREFIX) KOALA_QUOTE(KOALA_INTEROP_MODULE));
    #endif
#endif
CustomDeserializer * DeserializerBase::customDeserializers = nullptr;

%CALLBACK_KINDS%

OH_NativePointer getManagedCallbackCaller(CallbackKind kind);
OH_NativePointer getManagedCallbackCallerSync(CallbackKind kind);`

const apiImplEpilogueTemplate = `const OH_AnyAPI* impls[16] = { 0 };


const OH_AnyAPI* GetAnyAPIImpl(int kind, int version) {
    switch (kind) {
        case OH_%LIBRARY_NAME%_API_KIND:
            return reinterpret_cast<const OH_AnyAPI*>(Get%LIBRARY_NAME%APIImpl(version));
        default:
            return nullptr;
    }
}

extern "C" const OH_AnyAPI* GENERATED_GetArkAnyAPI(int kind, int version) {
    if (kind < 0 || kind > 15) return nullptr;
    if (!impls[kind]) {
        impls[kind] = GetAnyAPIImpl(kind, version);
    }
    return impls[kind];
}`

const ohosApiPrologueTemplate = `/*
 * 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.
 */

#ifndef %INCLUDE_GUARD_DEFINE%
#define %INCLUDE_GUARD_DEFINE%

%INTEROP_TYPES_HEADER

#define %LIBRARY_NAME%_API_VERSION 1

#include <stdint.h>

/* clang-format off */

#ifdef __cplusplus
extern "C" {
#endif

typedef InteropTag OH_Tag;
typedef InteropRuntimeType OH_%LIBRARY_NAME%_RuntimeType;

typedef InteropFloat32 OH_Float32;
typedef InteropFloat64 OH_Float64;
typedef InteropInt32 OH_Int32;
typedef InteropUInt32 OH_UInt32;
typedef InteropInt64 OH_Int64;
typedef InteropUInt64 OH_UInt64;
typedef InteropInt8 OH_Int8;
typedef InteropUInt8 OH_UInt8;
typedef InteropBoolean OH_Boolean;
typedef InteropCharPtr OH_CharPtr;
typedef InteropNativePointer OH_NativePointer;
typedef InteropString OH_String;
typedef InteropCallbackResource OH_%LIBRARY_NAME%_CallbackResource;
typedef InteropNumber OH_Number;
typedef InteropMaterialized OH_Materialized;
typedef InteropCustomObject OH_CustomObject;
typedef InteropUndefined OH_Undefined;
// typedef InteropAPIKind OH_APIKind;
typedef InteropVMContext OH_%LIBRARY_NAME%_VMContext;
typedef InteropAsyncWorker OH_%LIBRARY_NAME%_AsyncWorker;
typedef InteropAsyncWorkerPtr OH_%LIBRARY_NAME%_AsyncWorkerPtr;
typedef InteropBuffer OH_Buffer;
typedef InteropFunction OH_Function;
typedef InteropObject OH_Object;

typedef enum OH_%LIBRARY_NAME%_APIKind {
    OH_%LIBRARY_NAME%_API_KIND = %API_KIND%
} OH_%LIBRARY_NAME%_APIKind;`

const ohosApiEpilogueTemplate = `
#ifdef __cplusplus
}  // extern "C"
#endif

#endif // %INCLUDE_GUARD_DEFINE%
/* clang-format on */`

class OHOSNativeVisitor {
    implementationStubsFile: CppSourceFile
    implementationApiFile: CppSourceFile
    cinteropHeader: CppSourceFile

    private readonly argTypeConvertor = new CppConvertor(this.library)
    private readonly returnTypeConvertor = new ReturnTypeConvertor(this.library)

    hWriter = new CppLanguageWriter(new IndentedPrinter(), this.library, this.argTypeConvertor, PrimitiveTypesInstance)
    cppWriter = new CppLanguageWriter(new IndentedPrinter(), this.library, this.argTypeConvertor, PrimitiveTypesInstance)
    libraryName: string = ""

    interfaces = new Array<IDLInterface>()
    data = new Array<IDLInterface>()
    enums = new Array<IDLEnum>()
    callbacks = new Array<IDLCallback>()
    callbackInterfaces = new Array<IDLInterface>()
    cjInterfaces = new Map<TargetFile, string>()

    constructor(protected library: PeerLibrary, libraryName: string) {
        if (this.library.files.length == 0)
            throw new Error("No files in library")

        this.libraryName = libraryName
        this.library.name = libraryName

        const fileNamePrefix = this.libraryName.toLowerCase()
        this.implementationStubsFile = new CppSourceFile(`${fileNamePrefix}Impl_template${Language.CPP.extension}`, library)
        this.implementationStubsFile.addInclude("common-interop.h")
        this.implementationStubsFile.addInclude(`${fileNamePrefix}.h`)
        this.implementationApiFile = new CppSourceFile(`${fileNamePrefix}Impl_template${Language.CPP.extension}`, library)
        this.implementationApiFile.addInclude("common-interop.h")
        this.implementationApiFile.addInclude(`${fileNamePrefix}.h`)
        this.cinteropHeader = new CppSourceFile(`cinterop-${fileNamePrefix}.h`, library)
        this.cinteropHeader.addInclude("kotlin-cinterop.h")
    }

    private apiName(clazz: IDLInterface): string {
        return capitalize(qualifiedName(clazz, "_", "namespace.name"))
    }

    makeSignature(returnType: IDLType, parameters: IDLParameter[]): MethodSignature {
        return new MethodSignature(returnType, parameters.map(it => it.type!))
    }

    private writeCallback(callback: IDLCallback) {
        // TODO commonize with StructPrinter.ts
        const callbackTypeName = this.mangleTypeName(callback.name);
        const args = generateCallbackAPIArguments(this.library, callback)
        let _ = this.hWriter
        _.print(`typedef struct ${callbackTypeName} {`)
        _.pushIndent()
        _.print(`${this.mangleTypeName("CallbackResource")} resource;`)
        _.print(`void (*call)(${args.join(', ')});`)
        _.popIndent()
        _.print(`} ${callbackTypeName};`)
    }

    private impls = new Array<{ name: string, signature: SignatureDescriptor }>()

    private getPropertiesFromInterfaces(decl: IDLInterface) {
        const resolvedType = getSuper(decl, this.library)
        const propertiesFromInterface: IDLProperty[] = []
        if (!resolvedType || !isMaterialized(resolvedType, this.library)) {
            propertiesFromInterface.push(...getUniquePropertiesFromSuperTypes(decl, this.library)[0])
        }
        return propertiesFromInterface
    }

    private writeModifier(clazz: IDLInterface, writer: CppLanguageWriter) {
        if (peerGeneratorConfiguration().isResource(clazz.name)) return
        let name = this.modifierName(clazz)
        let handleType = this.handleType(clazz)
        let className = qualifiedName(clazz, "_", "namespace.name")
        let _h = this.hWriter
        let _c = writer
        _h.print(`struct ${handleType}Opaque;`)
        _h.print(`typedef struct ${handleType}Opaque* ${handleType};`)
        _h.print(`typedef struct ${name} {`)
        _c.print(`const ${name}* ${name}Impl() {`)
        _c.pushIndent()
        _c.print(`const static ${name} instance = {`)
        _c.pushIndent()
        _h.pushIndent()
        if (!isGlobalScope(clazz) && !isStaticMaterialized(clazz, this.library)) {
            let ctors = [...clazz.constructors]
            const useCtorIndex = ctors.length > 1
            if (ctors.length == 0) {
                ctors.push(createConstructor([], undefined)) // Add empty fake constructor
            }
            ctors.forEach((ctor, index) => {
                let name = `construct${useCtorIndex ? index.toString() : ""}`
                let params = ctor.parameters.map(it =>
                    new NameType(_h.escapeKeyword(it.name), this.argTypeConvertor.convert(it.type!)))
                let argConvertors = ctor.parameters.map(param => generateArgConvertor(this.library, param))
                let cppArgs = this.generateCParameters(ctor, argConvertors, _h)
                _h.print(`${handleType} (*${name})(${cppArgs});`) // TODO check
                let implName = `${className}_${name}Impl`
                _c.print(`&${implName},`)
                this.impls.push({ name: implName, signature: { params, returnType: handleType, paramsCString: cppArgs } })
            })
            {
                let destructName = `${className}_destructImpl`
                let params = [new NameType("thisPtr", handleType)]
                _h.print(`void (*destruct)(${params.map(it => `${it.type} ${it.name}`).join(", ")});`)
                _c.print(`&${destructName},`)
                this.impls.push({ name: destructName, signature: { params, returnType: 'void' } })
            }
        }
        generatePostfixForOverloads(clazz.methods).forEach(({ method, overloadPostfix }) => {
            const adjustedSignature = adjustSignature(this.library, method.parameters, method.returnType)
            let params = new Array<NameType>()
            if (!method.isStatic && !method.isFree) {
                params.push(new NameType("thisPtr", handleType))
            }
            params = params.concat(adjustedSignature.parameters.map(it =>
                new NameType(_h.escapeKeyword(it.name), this.argTypeConvertor.convert(it.type!))))
            let returnType = this.returnTypeConvertor.convert(adjustedSignature.returnType)
            const args = this.generateCParameters(method, adjustedSignature.convertors, _h)
            _h.print(`${returnType} (*${method.name}${overloadPostfix})(${args});`)
            let implName = `${className}_${method.name}${overloadPostfix}Impl`
            _c.print(`&${implName},`)
            this.impls.push({ name: implName, signature: { params, returnType, paramsCString: args } })
        })

        const propertiesFromInterface: IDLProperty[] = this.getPropertiesFromInterfaces(clazz)
        propertiesFromInterface.concat(clazz.properties).forEach(property => {
            let accessorMethods = []
            let getterMethod = createMethod(
                `get${capitalize(property.name)}`, [], property.type, {
                isStatic: property.isStatic,
                isAsync: false, isOptional: false, isFree: false})
            accessorMethods.push(getterMethod)
            if (!property.isReadonly) {
                let setterMethod = createMethod(
                    `set${capitalize(property.name)}`,
                    [createParameter("value", property.type)],
                    IDLVoidType, {
                    isStatic: property.isStatic,
                    isAsync: false, isOptional: false, isFree: false})
                accessorMethods.push(setterMethod)
            }

            for (const method of accessorMethods) {
                const adjustedSignature = adjustSignature(this.library, method.parameters, method.returnType)
                let params = new Array<NameType>()
                if (!isGlobalScope(clazz)) {
                    params.push(new NameType("thisPtr", handleType))
                }
                params = params.concat(adjustedSignature.parameters.map(it =>
                    new NameType(_h.escapeKeyword(it.name), this.argTypeConvertor.convert(it.type!))))
                let returnType = this.returnTypeConvertor.convert(adjustedSignature.returnType)
                const args = this.generateCParameters(method, adjustedSignature.convertors, _h)
                _h.print(`${returnType} (*${method.name})(${args});`)
                let implName = `${className}_${method.name}Impl`
                _c.print(`&${implName},`)
                this.impls.push({ name: implName, signature: { params, returnType, paramsCString: args } })
            }
        })
        _h.popIndent()
        _h.print(`} ${name};`)

        _c.popIndent()
        _c.print(`};`)
        _c.writeStatement(_c.makeReturn(_c.makeString("&instance")))
        _c.popIndent()
        _c.print(`}`)
    }

    // TODO drop this method
    private generateCParameters(method: IDLMethod | IDLConstructor, argConvertors: ArgConvertor[], writer: LanguageWriter): string {
        const args = argConvertors.map(it => {
            const typeName = writer.getNodeName(it.nativeType())
            const argName = writer.escapeKeyword(it.param)
            return it.isPointerType()
                ? `const ${typeName}* ${argName}`
                : `${typeName} ${argName}`
        })
        if (!isConstructor(method) && !method.isStatic)
            args.unshift(`${PrimitiveTypesInstance.NativePointer} thisPtr`)
        if (!!asPromise(method.returnType))
            args.unshift(`${generatorConfiguration().TypePrefix}${this.libraryName}_AsyncWorkerPtr asyncWorker`)
        if (hasExtAttribute(method, IDLExtendedAttributes.Throws) || !!asPromise(method.returnType))
            args.unshift(`${generatorConfiguration().TypePrefix}${this.libraryName}_VMContext vmContext`)
        return args.join(", ")
    }

    private modifierName(clazz: IDLInterface): string {
        return this.mangleTypeName(`${qualifiedName(clazz, "_", "namespace.name")}Modifier`)
    }
    private handleType(clazz: IDLInterface): string {
        return this.mangleTypeName(`${qualifiedName(clazz, "_", "namespace.name")}Handle`)
    }

    private writeImpls() {
        let _ = this.implementationApiFile.content
        let _stubs = this.implementationStubsFile.content
        const impls = sorted(this.impls, "name")
        impls.forEach(({name, signature}) => {
            const declaration = `${signature.returnType} ${name}(${signature.paramsCString ?? signature.params.map(it => `${it.type} ${it.name}`).join(", ")})`
            _.print(`${declaration};`)
            _stubs.print(`${declaration} {`)
            _stubs.pushIndent()
            if (signature.returnType != "void") {
                _stubs.print('return {};')
            }
            _stubs.popIndent()
            _stubs.print(`}`)
        })
    }

    private writeApiGetter(writer: CppLanguageWriter): void {
        writer.writeLines(apiGetterTemplate
            .replaceAll("%API_KIND%", `OH_${this.libraryName}_APIKind::OH_${this.libraryName}_API_KIND`)
            .replaceAll("%API_NAME%", `${generatorConfiguration().TypePrefix}${this.libraryName}_API`))
    }

    private writeModifiers(writer: CppLanguageWriter) {
        this.callbacks.forEach(it => {
            this.writeCallback(it)
        })
        this.interfaces.forEach(it => {
            this.writeModifier(it, writer)
        })
        // Create API.
        let api = this.libraryName
        let _c = writer
        _c.print(`extern "C" const ${generatorConfiguration().TypePrefix}${api}_API* Get${api}APIImpl(int version) {`)
        _c.pushIndent()
        _c.print(`const static ${generatorConfiguration().TypePrefix}${api}_API api = {`)
        _c.pushIndent()
        _c.print(`1, // version`)
        this.interfaces.forEach(it => {
            if (!peerGeneratorConfiguration().isResource(it.name))
                _c.print(`&${this.modifierName(it)}Impl,`)
        })
        _c.popIndent()
        _c.print(`};`)
        _c.print(`if (version != api.version) return nullptr;`)
        _c.print(`return &api;`)
        _c.popIndent()
        _c.print(`}`)
        let name = `${generatorConfiguration().TypePrefix}${api}_API`
        let _h = this.hWriter
        _h.print(`typedef struct ${name} {`)
        _h.pushIndent()
        _h.print(`${generatorConfiguration().TypePrefix}Int32 version;`)
        this.interfaces.forEach(it => {
            if (!peerGeneratorConfiguration().isResource(it.name))
                _h.print(`const ${this.modifierName(it)}* (*${this.apiName(it)})();`)
        })
        _h.popIndent()
        _h.print(`} ${name};`)
    }

    printC() {
        let callbackKindsPrinter = this.library.createLanguageWriter(Language.CPP);
        printCallbacksKinds(this.library, callbackKindsPrinter)

        this.cppWriter.writeLines(
            apiImplPrologueTemplate
                .replaceAll("%INTEROP_MODULE_NAME%", `${this.libraryName.toUpperCase()}NativeModule`)
                .replaceAll("%API_HEADER_PATH%", `${this.libraryName.toLowerCase()}.h`)
                .replaceAll("%CALLBACK_KINDS%", callbackKindsPrinter.getOutput().join("\n"))
                .replaceAll("%LIBRARY_NAME%", this.libraryName.toUpperCase())
        )
        this.cppWriter.writeLines(libraryCcDeclaration({removeCopyright: true}))
        const interopTypesContent = readInteropTypesHeader()
        this.hWriter.writeLines(
            ohosApiPrologueTemplate
                .replaceAll("%INTEROP_TYPES_HEADER", interopTypesContent)
                .replaceAll("%INCLUDE_GUARD_DEFINE%", `OH_${this.libraryName.toUpperCase()}_H`)
                .replaceAll("%LIBRARY_NAME%", this.libraryName.toUpperCase())
                .replaceAll("%API_KIND%", peerGeneratorConfiguration().ApiKind.toString())
        )

        let toStringsPrinter = this.library.createLanguageWriter(Language.CPP)
        new StructPrinter(this.library).generateStructs(this.hWriter, this.hWriter.printer, toStringsPrinter)
        this.cppWriter.concat(toStringsPrinter)
        const prefix = generatorTypePrefix()
        this.cppWriter.concat(createCSerializerPrinter(this.library, Language.CPP, prefix))

        let writer = new CppLanguageWriter(new IndentedPrinter(), this.library, this.argTypeConvertor, PrimitiveTypesInstance)
        this.writeModifiers(writer)
        this.writeImpls()
        this.implementationApiFile.content.concat(writer)
        this.writeApiGetter(this.cppWriter)
        this.cppWriter.concat(printBridgeCc(this.library).generated)
        this.cinteropHeader.content.concat(printBridgeHeader(this.library).generated)
        createDeserializeAndCallPrinter(this.library.name, Language.CPP)(this.library).forEach(result => {
            this.cppWriter.concat(result.content)
        })
        // this.cppWriter.concat(makeDeserializeAndCall(this.library, Language.CPP, 'serializer.cc').content)
        this.cppWriter.concat(printManagedCaller('', this.library).content)
        this.hWriter.writeLines(readTemplate('any_api.tpl', { removeCopyright: true }))
        this.hWriter.writeLines(readTemplate('generic_service_api.h'))
        this.hWriter.writeLines(
            ohosApiEpilogueTemplate
                .replaceAll("%INCLUDE_GUARD_DEFINE%", `OH_${this.libraryName.toUpperCase()}_H`)
                .replaceAll("%LIBRARY_NAME%", this.libraryName.toUpperCase())
        )
        this.implementationApiFile.content.writeLines(
            apiImplEpilogueTemplate
                .replaceAll("%LIBRARY_NAME%", this.libraryName.toUpperCase())
        )
    }

    prepare() {
        this.library.files.forEach(file => {
            if (isInIdlize(file) || !isInCurrentModule(file))
                return
            linearizeNamespaceMembers(file.entries).forEach(entry => {
                if (isInterface(entry)) {
                    if (isMaterialized(entry, this.library)) {
                        this.interfaces.push(entry)
                    } else {
                        this.data.push(entry)
                    }
                } else if (isEnum(entry)) {
                    this.enums.push(entry)
                }
            })
        })

        this.data = sorted(this.data, it => getFQName(it))
        this.enums = sorted(this.enums, it => getFQName(it))
        this.interfaces = sorted(this.interfaces, it => getFQName(it))

        const global = createSyntheticGlobalScope(this.library)
        if (global.methods.length) {
            this.interfaces.push(global)
        }
    }

    private mangleTypeName(typeName: string): string {
        return `${generatorTypePrefix()}${typeName}`
    }
}

class ReturnTypeConvertor extends CppReturnTypeConvertor {
    override convertPrimitiveType(type: IDLPrimitiveType): string {
        if (type === IDLNumberType)
            return `${generatorConfiguration().TypePrefix}Number`
        return super.convertPrimitiveType(type)
    }
}

// TODO commonize this piece of code
class OhosBridgeCcVisitor extends BridgeCcVisitor {
    protected generateApiCall(context: idl.IDLInterface): string {
        // TODO: may be need some translation tables?
        let clazz = getDeclarationUniqueName(context)
        return capitalize(clazz) + "()"
    }

    protected getApiCall(context: idl.IDLInterface): string {
        const libName = this.library.name;
        return `Get${generatorConfiguration().TypePrefix}${this.library.name}_API(${libName}_API_VERSION)`
    }


    protected getReceiverArgName(): string {
        return "thisPtr"
    }

    protected printReceiverCastCall(context: idl.IDLInterface, method: PeerMethod) {
        // OHOS API does not need to cast native pointer at this moment
    }

    protected getPeerMethodName(method: PeerMethod): string {
        switch (method.sig.name) {
            case PeerMethodSignature.GET_FINALIZER: return "destruct"
            default: return method.sig.name
        }
    }

    protected printAPICall(context: idl.IDLInterface, method: PeerMethod) {
        if (method.sig.name == PeerMethodSignature.GET_FINALIZER) {
            const modifier = this.generateApiCall(context)
            const peerMethod = this.getPeerMethodName(method)
            const apiCall = this.getApiCall(context)
            const call = `return (${PrimitiveTypesInstance.NativePointer}) ${apiCall}->${modifier}->${peerMethod};`
            this.generatedApi.print(call)
        } else {
            super.printAPICall(context, method)
        }
    }

    protected printMaterializedClass(clazz: MaterializedClass) {
        if (peerGeneratorConfiguration().isResource(clazz.className)) return
        const modifierName = "";
        for (const method of [...clazz.ctors, clazz.finalizer].concat(clazz.methods)) {
            if (!method) continue
            this.printMethod(clazz.decl, method);
        }
    }
}

export function printBridgeCc(peerLibrary: PeerLibrary): BridgeApi {
    const visitor = new OhosBridgeCcVisitor(peerLibrary, false)
    visitor.print()
    return { generated: visitor.generatedApi, custom: visitor.customApi }
}

export function printBridgeHeader(peerLibrary: PeerLibrary): BridgeApi {
    const visitor = new BridgeHeaderVisitor(peerLibrary)
    visitor.print()
    return { generated: visitor.generatedApi, custom: visitor.customApi }
}

export function generateNativeOhos(peerLibrary: PeerLibrary): Map<TargetFile, string> {
    if (peerLibrary.orderedMaterialized.length == 0 && createSyntheticGlobalScope(peerLibrary).methods.length == 0)
        return new Map
    const libraryName = suggestLibraryName(peerLibrary)
    const visitor = new OHOSNativeVisitor(peerLibrary, libraryName)
    visitor.prepare()
    visitor.printC()
    return new Map([
        [new TargetFile(`${peerLibrary.name.toLowerCase()}.h`), visitor.hWriter.getOutput().join('\n')],
        [new TargetFile(`${peerLibrary.name.toLowerCase()}.cc`), visitor.cppWriter.getOutput().join('\n')],
        [new TargetFile(`${peerLibrary.name.toLowerCase()}Impl_temp.cc`), visitor.implementationStubsFile.printToString()],
        [new TargetFile(`${peerLibrary.name.toLowerCase()}ApiImpl_temp.cc`), visitor.implementationApiFile.printToString()],
        [new TargetFile(visitor.cinteropHeader.name), visitor.cinteropHeader.printToString()],
        [new TargetFile(`cinterop.def`), printKotlinCInteropDefFile([visitor.cinteropHeader.name])],
    ])
}


type AdjustedSignature = {
    convertors: ArgConvertor[]
    parameters: IDLParameter[]
    returnType: IDLType,
};
function adjustSignature(library: PeerLibrary, parameters: IDLParameter[], returnType: IDLType): AdjustedSignature {
    const convertors = parameters.map(parameter => generateArgConvertor(library, parameter))
    const outConvertor = createOutArgConvertor(library, returnType, parameters.map(parameter => parameter.name))
    if (outConvertor)
        return {
            convertors: [...convertors, outConvertor],
            parameters: [...parameters, createParameter(outConvertor.param, outConvertor.idlType)],
            returnType: IDLVoidType
        }
    else
        return { convertors, parameters, returnType }
}

function generateArgConvertor(library: PeerLibrary, param: IDLParameter): ArgConvertor {
    if (!param.type) throw new Error("Type is needed")
    return library.typeConvertor(param.name, param.type, param.isOptional)
}

interface MethodWithPostfix {
    method: IDLMethod,
    overloadPostfix: string
}

function generatePostfixForOverloads(methods: IDLMethod[]): MethodWithPostfix[] {
    const overloads = new Map<string, number>()
    for (const method of methods) {
        overloads.set(method.name, (overloads.get(method.name) ?? 0) + 1)
    }

    const overloadCounter = new Map<string, number>()
    for (const [overloadName, count] of overloads) {
        if (count > 1) {
            overloadCounter.set(overloadName, 0)
        }
    }

    return methods.map(method => {
        let overloadPostfix = ''
        if (overloadCounter.has(method.name)) {
            const postfix = overloadCounter.get(method.name)!
            overloadPostfix = postfix.toString()
            overloadCounter.set(method.name, postfix + 1)
        }
        return {
            method,
            overloadPostfix
        }
    })
}

export function suggestLibraryName(library: PeerLibrary) {
    return currentModule().name.replaceAll(".", "_").toUpperCase()
}