/**
 * Copyright (c) 2021-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.
 */

#ifndef ES2PANDA_COMPILER_CORE_ETS_EMITTER_H
#define ES2PANDA_COMPILER_CORE_ETS_EMITTER_H

#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "annotation.h"
#include "assembly-literals.h"
#include "emitter.h"

namespace ark::es2panda::parser {
class Program;
}  // namespace ark::es2panda::parser

namespace ark::es2panda::varbinder {
class RecordTable;
}  // namespace ark::es2panda::varbinder

namespace ark::es2panda::ir {
class ClassDefinition;
}  // namespace ark::es2panda::ir

namespace ark::es2panda::checker {
class ETSObjectType;
class ETSArrayType;
class Signature;
}  // namespace ark::es2panda::checker

namespace ark::pandasm {
struct Field;
struct Record;
class ItemMetadata;
class AnnotationData;
}  // namespace ark::pandasm

namespace ark::es2panda::compiler {

using LiteralArrayPair = std::pair<std::string, std::vector<pandasm::LiteralArray::Literal>>;
using LiteralArrayVector = std::vector<LiteralArrayPair>;

class ETSFunctionEmitter : public FunctionEmitter {
public:
    ETSFunctionEmitter(const CodeGen *cg, ProgramElement *programElement) : FunctionEmitter(cg, programElement) {}
    ~ETSFunctionEmitter() override = default;
    NO_COPY_SEMANTIC(ETSFunctionEmitter);
    NO_MOVE_SEMANTIC(ETSFunctionEmitter);

    static bool IsEmissionRequired(ir::ScriptFunction *func, parser::Program *globalProgram);

protected:
    pandasm::Function *GenFunctionSignature() override;

    void GenFunctionAnnotations(pandasm::Function *func) override;
    void GenVariableSignature(pandasm::debuginfo::LocalVariable &variableDebug,
                              varbinder::LocalVariable *variable) const override;
    void GenSourceFileDebugInfo(pandasm::Function *func) override;
};

namespace detail {
class EmitterDependencies;
}  // namespace detail

class ETSEmitter : public Emitter {
public:
    explicit ETSEmitter(const public_lib::Context *context);
    ~ETSEmitter() override;
    NO_COPY_SEMANTIC(ETSEmitter);
    NO_MOVE_SEMANTIC(ETSEmitter);

    void EmitRecords() override;
    std::unordered_map<std::string, std::unique_ptr<ark::pandasm::Program>> EmitRecordsSimultIncMode();
    void AddProgramElement(ProgramElement *programElement) override;

    bool IsETSEmitter() override
    {
        return true;
    }

    static std::string NormalizePathSeparators(std::string_view path);
    static std::string GetNormalizedSourceFilePath(const public_lib::Context *context);
    static std::string GetNormalizedSourceFilePath(const CodeGen *cg);
    // Handle a broken dependence between annotation handling and shared emitter code
    std::vector<pandasm::AnnotationData> GenCustomAnnotations(
        const ArenaVector<ir::AnnotationUsage *> &annotationUsages, const std::string &baseName);
    pandasm::AnnotationData GenAnnotationAsync(ir::ScriptFunction *scriptFunc);
    std::string const &AddDependence(std::string const &str);
    void SetupDependenciesForTheProgram(const parser::Program *prg);
    void GenSyntheticRuntimeTypeRecord(util::StringView assemblerType);

private:
    pandasm::Program *GetOrCreatePandasmProgram(const parser::Program *prg);
    detail::EmitterDependencies *GetOrCreateDependenciesForTheProgram(const parser::Program *prg);
    void EmitRecordsImpl(bool isIncrementalBuild = false);
    void EmitRecordTable(varbinder::RecordTable *table, bool programIsExternal, bool traverseExternals);
    void GenGlobalArrayRecord(const checker::ETSArrayType *arrayType);
    std::vector<pandasm::AnnotationData> GenAnnotations(const ir::ClassDefinition *classDef);
    void GenClassRecord(const ir::ClassDefinition *classDef, bool external);
    pandasm::AnnotationElement ProcessArrayType(const ir::ClassProperty *prop, std::string &baseName,
                                                const ir::Expression *init);
    pandasm::AnnotationElement GenCustomAnnotationElement(const ir::ClassProperty *prop, std::string &baseName);
    pandasm::AnnotationData GenCustomAnnotation(ir::AnnotationUsage *anno, std::string &baseName);
    void ProcessArrayElement(const ir::Expression *elem, std::vector<pandasm::LiteralArray::Literal> &literals,
                             std::string &baseName, LiteralArrayVector &result);
    LiteralArrayVector CreateLiteralArray(std::string &baseName, const ir::Expression *array);
    void CreateLiteralArrayProp(const ir::ClassProperty *prop, std::string &baseName, pandasm::Field &field);
    void GenCustomAnnotationProp(const ir::ClassProperty *prop, std::string &baseName, pandasm::Record &record,
                                 bool external);
    void GenCustomAnnotationRecord(const ir::AnnotationDeclaration *annoDecl, std::string &baseName, bool external);
    void GenEnumRecord(const ir::TSEnumDeclaration *enumDecl, bool external);
    void GenInterfaceRecord(const ir::TSInterfaceDeclaration *interfaceDecl, bool external);
    void EmitDefaultFieldValue(pandasm::Field &classField, const ir::Expression *init);
    void GenClassField(const ir::ClassProperty *prop, pandasm::Record &classRecord, bool external);

    void GenMethodDefinition(ir::MethodDefinition const *method, bool external);
    void GenFunction(ir::ScriptFunction const *scriptFunc, bool external);
    void GenClassInheritedFields(const checker::ETSObjectType *baseType, pandasm::Record &classRecord);
    pandasm::AnnotationData GenAnnotationModule(const ir::ClassDefinition *classDef);
    pandasm::AnnotationData GenAnnotationFunctionalReference(const ir::ClassDefinition *classDef);
    ir::MethodDefinition *FindAsyncImpl(ir::ScriptFunction *asyncFunc);
    void ProcessArrayExpression(std::string &baseName, LiteralArrayVector &result,
                                std::vector<pandasm::LiteralArray::Literal> &literals, const ir::Expression *elem);

    // NOTE(mshimenkov): Since functions are emitted concurrently and those functions can be from different programs
    // (in simultaneous build mode), corresponding dependencies_ for those programs can be set at the same time
    // leading to dependencies confusion
    thread_local static detail::EmitterDependencies *dependencies_;

    // NOTE(mshimenkov): Is used in simultaneous mode after code gen stage to add functions functions from different
    // modules to the corresponding pandasm::Programs
    // The created pandasm::Programs are then passed outside and the caller is responsible for the memory free
    std::unordered_map<std::string_view, pandasm::Program *> prgMaps_;

    // NOTE(mshimenkov): Is used in simultaneous mode during code gen stage to collect info which records should be
    // emitted for the current pandasm::Program (external and non-external records)
    std::unordered_map<std::string_view, detail::EmitterDependencies *> depMaps_;
};
}  // namespace ark::es2panda::compiler

#endif