/*========================== begin_copyright_notice ============================
Copyright (C) 2025 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/

/*!
 * @file Simple tool to do nothing using HLIF interface
 */

#include <string>
#include <vector>
#include <map>
#include <utility>

#include "gtpin_api.h"
#include "gtpin_tool_utils.h"

using namespace gtpin;
using namespace std;

Knob<bool> KNOB_NO_COUT("no_cout", false, "Do not send results to standard output device");

/* ============================================================================================= */
// Class HlifNothing
/* ============================================================================================= */
class HlifNothing : public GtTool
{
public:
    // Implementation of the IGtTool interface
    const char* Name()          const                   override { return "HLIF nothing"; }
    void        OnKernelBuild(IGtKernelInstrument&)     override; 
    void        OnKernelRun(IGtKernelDispatch&)         override;
    void        OnKernelComplete(IGtKernelDispatch&)    override;

    void               LoadHliLibrary();                ///< Compile and load library of HLI functions
    static HlifNothing* Instance();                      ///< Return single instance of this class
    static void        OnFini();                        ///< Termination handler registered with atexit()

private:
    HlifNothing() : _notingFunc("NothingFunc"), _hliModule(nullptr) {} ///< Constructor

    GtHliFunction<uint32_t, uint32_t>  _notingFunc;
    IGtHliModuleHandle                  _hliModule;

    std::string                         _profileStr;
};

/* ============================================================================================= */
// HlifNothing implementation
/* ============================================================================================= */
HlifNothing* HlifNothing::Instance()
{
    static HlifNothing instance;
    return &instance;
}

void HlifNothing::LoadHliLibrary()
{
    std::string installDir = GetKnobValue<std::string>("installDir");
    _hliModule = GTPin_GetCore()->HliLibrary().CompileModuleFromFile(JoinPath(installDir, "Examples", "hli_nothing.cl").c_str());
    GTPIN_ASSERT(_hliModule != nullptr);
}

void HlifNothing::OnFini()
{
    // Dump results of all kernel runs
    ofstream fs(JoinPath(GTPin_GetCore()->ProfileDir(), "hlif_nothing.txt"));
    GTPIN_ASSERT(fs.is_open());
    const std::string& profileStr = Instance()->_profileStr;
    fs << profileStr;
    if (!KNOB_NO_COUT)
    {
        std::cout << profileStr;
    }
}

void HlifNothing::OnKernelBuild(IGtKernelInstrument& instrumentor)
{
    // Link the kernel with the library of HLI functions
    instrumentor.LinkHliModule(_hliModule);

    // Instrument basic blocks
    for (auto bblPtr : instrumentor.Cfg().Bbls())
    {
        int bblId = bblPtr->Id();
        if (bblId <= knobMaxInstrumentBbl && knobMinInstrumentBbl <= bblId)
        {
            if (!bblPtr->IsEmpty())
            {
                _notingFunc.InsertCallAtBbl(instrumentor, *bblPtr, GtIpoint::Before(), NullReg(), 0);
            }
        }
    }
}

void HlifNothing::OnKernelRun(IGtKernelDispatch& dispatcher)
{
    // Tell GTPin to run instrumented code
    dispatcher.SetProfilingMode(true);
}

void HlifNothing::OnKernelComplete(IGtKernelDispatch& dispatcher)
{
    std::ostringstream os;
    os << setw(4) << dispatcher.DispatchId() << ":    " <<  ExtendedKernelName(dispatcher.Kernel()) << std::endl;

    // Add profile data of this kernel dispatch to _profileStr
    _profileStr += os.str();
}

/* ============================================================================================= */
// GTPin_Entry
/* ============================================================================================= */
EXPORT_C_FUNC void GTPin_Entry(int argc, const char *argv[])
{
    // Parse command line and configure GTPin
    ConfigureGTPin(argc, argv);

    // Register the tool (callbacks) with the GTPin core
    HlifNothing::Instance()->Register();

    // Compile and load library of HLI functions
    HlifNothing::Instance()->LoadHliLibrary();

    // Register the termination function
    atexit(HlifNothing::OnFini);
}
