/*

 * Copyright (c) 2022-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 ECMASCRIPT_COMPILER_LOG_H

#define ECMASCRIPT_COMPILER_LOG_H



#include <algorithm>

#include <cstdint>

#include <iostream>

#include <map>

#include <set>

#include <sstream>

#include <string>

#include <vector>

#include <iomanip>

#include "ecmascript/log_wrapper.h"

#include "ecmascript/mem/clock_scope.h"

#include "ecmascript/mem/c_string.h"

#include "ecmascript/compiler/argument_accessor.h"



namespace panda::ecmascript::kungfu {

class AotMethodLogList;

class MethodLogList;



class CompilerLog {

public:

    explicit PUBLIC_API CompilerLog(const std::string &logOpt);

    CompilerLog() = default;

    ~CompilerLog() = default;



    bool AllMethod() const

    {

        return allMethod_;

    }



    bool CertainMethod() const

    {

        return cerMethod_;

    }



    bool NoneMethod() const

    {

        return noneMethod_;

    }



    bool OutputCIR() const

    {

        return outputCIR_;

    }



    bool OutputLLIR() const

    {

        return outputLLIR_;

    }



    bool OutputASM() const

    {

        return outputASM_;

    }



    bool OutputType() const

    {

        return outputType_;

    }



    bool GetEnableCompilerLogTime() const

    {

        return compilerLogTime_;

    }



    void SetEnableCompilerLogTime(bool compilerLogTime)

    {

        compilerLogTime_ = compilerLogTime;

    }



    bool GetEnableMethodLog() const

    {

        return enableMethodLog_;

    }



    void SetEnableMethodLog(bool enableMethodLog)

    {

        enableMethodLog_ = enableMethodLog;

    }



    bool GetEnableCompilerLogAllMethodsTime() const

    {

        return enableCompilerLogAllMethodsTime_;

    }



    void SetEnableCompilerLogAllMethodsTime(bool enable)

    {

        enableCompilerLogAllMethodsTime_ = enable;

    }



    bool EnableMethodCIRLog() const

    {

        return GetEnableMethodLog() && OutputCIR();

    }



    bool EnableMethodASMLog() const

    {

        return GetEnableMethodLog() && OutputASM();

    }



    void SetMethodLog(const std::string &fileName, const std::string &methodName, AotMethodLogList *logList);

    void SetStubLog(const std::string& stubName, MethodLogList* logList);

    void PUBLIC_API Print() const;

    void AddMethodTime(const std::string& name, uint32_t id, double time);

    void AddPassTime(const std::string& name, double time);

    double GetPassTime(const std::string& name) const;

    double GetMethodTime(const std::string& name, uint32_t id) const;

    int GetIndex();



    std::map<std::string, int> nameIndex_;



private:

    static constexpr int RECORD_LENS = 64;

    static constexpr int PASS_LENS = 32;

    static constexpr int METHOD_LENS = 16;

    static constexpr int OFFSET_LENS = 8;

    static constexpr int PERCENT_LENS = 4;

    static constexpr int TIME_LENS = 8;

    static constexpr int MILLION_TIME = 1000;

    static constexpr int HUNDRED_TIME = 100;



    void PrintPassTime() const;

    void PrintMethodTime() const;

    void PrintTime() const;



    int idx_ {0};

    bool allMethod_ {false};

    bool cerMethod_ {false};

    bool noneMethod_ {false};

    bool outputCIR_ {false};

    bool outputLLIR_ {false};

    bool outputASM_ {false};

    bool outputType_ {false};

    bool compilerLogTime_ {false};

    bool enableMethodLog_ {false};

    bool enableCompilerLogAllMethodsTime_ {false};

    std::map<std::string, double> timePassMap_ {};

    std::map<std::pair<uint32_t, std::string>, double> timeMethodMap_ {};

};



class MethodLogList {

public:

    explicit MethodLogList(const std::string &logMethods) : methods_(logMethods) {}

    ~MethodLogList() = default;

    bool IncludesMethod(const std::string &methodName) const;

private:

    std::string methods_ {};

};



class AotMethodLogList : public MethodLogList {

public:

    static const char fileSplitSign = ':';

    static const char methodSplitSign = ',';



    explicit AotMethodLogList(const std::string &logMethods) : MethodLogList(logMethods)

    {

        ParseFileMethodsName(logMethods);

    }

    ~AotMethodLogList() = default;



    bool IncludesMethod(const std::string &fileName, const std::string &methodName) const;



private:

    std::vector<std::string> spiltString(const std::string &str, const char ch);

    void PUBLIC_API ParseFileMethodsName(const std::string &logMethods);

    std::map<std::string, std::vector<std::string>> fileMethods_ {};

};



class TimeScope : public ClockScope {

public:

    TimeScope(std::string name,

              std::string methodName,

              uint32_t methodOffset,

              CompilerLog* log,

              Circuit* circuit = nullptr);

    TimeScope(std::string name, CompilerLog* log);

    ~TimeScope();



private:

    static constexpr int PASS_LENS = 32;

    static constexpr int METHOD_LENS = 24;

    static constexpr int OFFSET_LENS = 16;

    static constexpr int NODE_COUNT_LENS = 16;

    static constexpr int TIME_LENS = 16;

    static constexpr int INVALID_NODE_COUNT = -1;



    std::string name_ {""};

    std::string methodName_ {""};

    uint32_t methodOffset_ {0};

    size_t initialNodeCount_ {0};

    Circuit* circuit_ {nullptr};

    CompilerLog *log_ {nullptr};



    const std::string GetShortName(const std::string& methodName);

    size_t GetCurrentNodeCount() const;

};



class PGOTypeLogList {

public:

    explicit PGOTypeLogList(Circuit *circuit) : acc_(circuit) {}

    ~PGOTypeLogList() = default;

    void CollectGateTypeLogInfo(GateRef gate, bool isBinOp);

    void PrintPGOTypeLog();

private:

    GateAccessor acc_;

    std::string log_ {};

};



struct LogFormatter {

    std::ostringstream oss;



    template<typename T>

    LogFormatter& Left(std::string_view key, const T& value, int width)

    {

        std::ostringstream ctx;

        if (key.empty()) {

            ctx << value;

        } else {

            ctx << key << value;

        }

        oss << std::left << std::setw(width) << ctx.str();

        return *this;

    }



    template<typename T>

    LogFormatter& Right(std::string_view key, const T& value, int width)

    {

        std::ostringstream ctx;

        if (key.empty()) {

            ctx << value;

        } else {

            ctx << key << value;

        }

        oss << std::right << std::setw(width) << ctx.str();

        return *this;

    }



    std::string str() const

    {

        return oss.str();

    }

};

}  // namespace panda::ecmascript::kungfu

#endif  // ECMASCRIPT_COMPILER_LOG_H