/*========================== begin_copyright_notice ============================
Copyright (C) 2023-2024 Intel Corporation

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

/*!
 * @file Abstract representation and handling of events (errors, warnings, etc.) reported by GTPin
 */

#ifndef IGT_EVENT_H_
#define IGT_EVENT_H_

#include <sstream>
#include <iomanip>
#include <string>

#include "gt_basic_defs.h"
#include "igt_core.h"

namespace gtpin
{
#pragma pack(push, 8)

/* ===================================================================================================== */
// Forward and external declarations
/* ===================================================================================================== */
class IGtEventHandler;
class IGtKernel;

/* ===================================================================================================== */
// Class GtStatus
/* ===================================================================================================== */
/*! @ingroup GTPIN_EVENT
 *  @brief Status of the GTPin operation
 */
enum GT_STATUS :uint32_t
{
    GTPIN_STATUS_SUCCESS                    = 0,                                ///< Success

    GTPIN_STATUS_WARNING                    = 0x20000000,                       ///< Generic warning
    GTPIN_STATUS_WARNING_KERNEL_FILTERED    = GTPIN_STATUS_WARNING + 1,         ///< Instrumentation is skipped. The kernel is filtered out
    GTPIN_STATUS_WARNING_INTERNAL           = GTPIN_STATUS_WARNING + 0x1000000, ///< Internal warning

    GTPIN_STATUS_ERROR                      = 0x40000000,                       ///< Generic error
    GTPIN_STATUS_ERROR_NO_DRIVER            = GTPIN_STATUS_ERROR + 1,           ///< The communication with the driver is not established/broken
    GTPIN_STATUS_ERROR_UNSUPPORTED_CFG      = GTPIN_STATUS_ERROR + 2,           ///< Kernel could not be instrumented: CFG is too complex
    GTPIN_STATUS_ERROR_REG_ALLOC_FAILURE    = GTPIN_STATUS_ERROR + 3,           ///< Kernel could not be instrumented: register allocation failed
    GTPIN_STATUS_ERROR_INSTRUMENT_FAILURE   = GTPIN_STATUS_ERROR + 4,           ///< Kernel instrumentation failed
    GTPIN_STATUS_ERROR_API_VERSION_MISMATCH = GTPIN_STATUS_ERROR + 5,           ///< GTPin/tool API version mismatch
    GTPIN_STATUS_ERROR_INVALID_API_ARGUMENT = GTPIN_STATUS_ERROR + 6,           ///< Invalid API argument specified by the tool
    GTPIN_STATUS_ERROR_ACCESS_DENIED        = GTPIN_STATUS_ERROR + 7,           ///< Generic access error, e.g. memory/file access failure
    GTPIN_STATUS_ERROR_INVALID_API_CALL     = GTPIN_STATUS_ERROR + 8,           ///< API is not available in the current context (callback)
    GTPIN_STATUS_ERROR_BUFFER_OVERFLOW      = GTPIN_STATUS_ERROR + 9,           ///< Memory buffer is too small
    GTPIN_STATUS_ERROR_HLI_COMPILE_FAILURE  = GTPIN_STATUS_ERROR + 10,          ///< HLI module compilation failed
    GTPIN_STATUS_ERROR_HLI_BUILD_FAILURE    = GTPIN_STATUS_ERROR + 11,          ///< HLI binary module build failed
    GTPIN_STATUS_ERROR_GFX_RUNTIME_FAILURE  = GTPIN_STATUS_ERROR + 12,          ///< GFX runtime error 
    GTPIN_STATUS_ERROR_UNSUPPORTED_FEATURE  = GTPIN_STATUS_ERROR + 13,          ///< Unsupported feature/API
    GTPIN_STATUS_ERROR_BIN_LINK_FAILURE     = GTPIN_STATUS_ERROR + 14,          ///< Binary link failed
    GTPIN_STATUS_ERROR_D3D_PROTOCOL_ERROR   = GTPIN_STATUS_ERROR + 15,          ///< D3D protocol error
    GTPIN_STATUS_ERROR_GFX_DRIVER_VERSION   = GTPIN_STATUS_ERROR + 16,          ///< Gfx driver version mismatch
    GTPIN_STATUS_ERROR_INTERNAL             = GTPIN_STATUS_ERROR + 0x1000000    ///< Internal error
};

/*! @ingroup GTPIN_EVENT
 *  @brief Status of the GTPin operation
 */
class GtStatus
{
public:
    inline constexpr GtStatus(GT_STATUS v = GTPIN_STATUS_SUCCESS) : _value(v) {}        ///< Conversion constructor
    inline constexpr operator GT_STATUS() const { return _value; }                      ///< Conversion operator

    inline constexpr bool IsSuccess() const { return _value == GTPIN_STATUS_SUCCESS; }
    inline constexpr bool IsWarning() const { return (_value & GTPIN_STATUS_WARNING) == GTPIN_STATUS_WARNING; }
    inline constexpr bool IsError()   const { return (_value & GTPIN_STATUS_ERROR) == GTPIN_STATUS_ERROR; }

    inline const char*    ToString()  const;    ///< Return string representation
private:
    GT_STATUS _value;
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtStatus);

/* ===================================================================================================== */
// Class GtEventReaction
/* ===================================================================================================== */
/*! @ingroup GTPIN_EVENT
 *  @brief Possible action that may be requested by the GTPin event handler
 */
enum EVENT_REACTION :uint32_t
{
    EVENT_REACTION_DEFAULT,       ///< Default action on the event of a specific type
    EVENT_REACTION_CONTINUE,      ///< Ignore event, e.g. skip instrumentation of the problematic kernel
    EVENT_REACTION_RETRY,         ///< Retry the last operation, e.g. redo instrumentation for the problematic kernel
    EVENT_REACTION_TERMINATE,     ///< Terminate (abort) the application
    EVENT_REACTION_SHUTDOWN_GTPIN ///< Shuts down GTPin operation by keeping the application process alive
};

/*! @ingroup GTPIN_EVENT
 *  @brief Wrapper class of the EVENT_REACTION enumeration that defines return value of the event handling callback
 */
class GtEventReaction
{
public:
    inline constexpr GtEventReaction(EVENT_REACTION v = EVENT_REACTION_DEFAULT) : _value(v) {} ///< Conversion constructor
    inline constexpr operator EVENT_REACTION() const { return _value; }                        ///< Conversion operator

    inline constexpr bool IsDefault()       const { return _value == EVENT_REACTION_DEFAULT; }
    inline constexpr bool IsContinue()      const { return _value == EVENT_REACTION_CONTINUE; }
    inline constexpr bool IsRetry()         const { return _value == EVENT_REACTION_RETRY; }
    inline constexpr bool IsTerminate()     const { return _value == EVENT_REACTION_TERMINATE; }
    inline constexpr bool IsShutdownGTPin() const { return _value == EVENT_REACTION_SHUTDOWN_GTPIN; }

    inline const char*    ToString()    const;                                                 ///< Return string representation

private:
    EVENT_REACTION _value;
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtEventReaction);

/* ===================================================================================================== */
// Class IGtEvent
/* ===================================================================================================== */
/*! @ingroup GTPIN_EVENT
 *  @brief Abstract representation of the event generated by GTPin
 */
class IGtEvent
{
public:
    /// Return status of the operation that reported this event
    virtual GtStatus Status() const = 0;

    /// Return default reaction on this event
    virtual GtEventReaction DefaultReaction() const = 0;

    /// Return human-readable representation of this event
    virtual GtCoreString ToString() const = 0;

    /// Return copy of this event created in the GTPin core memory storage
    virtual GtCoreUniquePtr<IGtEvent> Clone() const = 0;

    /*!
     * @brief Handle this event with the specified event handler
     * @param[in] eventHandler   Event handler to be called: eventHandler.OnEvent(*this)
     * @return Action requested by the event handler
     */
    virtual GtEventReaction Handle(IGtEventHandler& eventHandler) const = 0;

    /// @internal Destructor
    virtual ~IGtEvent() = default;
};
GTPIN_ASSERT_PURE_ABSTRACT(IGtEvent);

/* ===================================================================================================== */
// Class IGtKernelEvent
/* ===================================================================================================== */
/*! @ingroup GTPIN_EVENT
 *  @brief Abstract representation of the event associated with a concrete kernel
 */
class IGtKernelEvent : public IGtEvent
{
public:
    /// Return Descriptor of the kernel associated with this event
    virtual const IGtKernel* Kernel() const = 0;
};
GTPIN_ASSERT_PURE_ABSTRACT(IGtKernelEvent);

/* ===================================================================================================== */
// Class IGtInternalEvent
/* ===================================================================================================== */
/*! @ingroup GTPIN_EVENT
 *  @brief Abstract representation of the event that reports internal GTPin error, warning, etc.
 */
class IGtInternalEvent : public IGtEvent {};
GTPIN_ASSERT_PURE_ABSTRACT(IGtInternalEvent);

/* ===================================================================================================== */
// Class IGtEventHandler
/* ===================================================================================================== */
/*! @ingroup GTPIN_EVENT
 *  @brief Interface of the GTPin event handler
 *
 *  This class defines methods intended for handling asynchronous GTPin events.
 *  Unlike errors in tool APIs that can be retrieved by the IGtCore::LastError() function, asynchronous
 *  GTPin events are not a direct result of the API invocation. These events can't be reported on return from
 *  the API call because there are no active API calls at the time when they occur. \n
 *  To monitor and handle asynchronous GTPin events, the tool can provide its own implementation of the
 *  IGtEventHandler interface and register it using the IGtCore::RegisterEventHandler function.
 *  If no tool-specific handler is registered, GTPin performs default event handling, as indicated by the
 *  IGtEvent::DefaultReaction() function.
 *
 @verbatim
 * =============================================================================================================================== *
 *                                            Asynchronous  GTPin events                                                           *
 * =============================================================================================================================== *
 *            Description               |             Status                      | Default [possible] reaction |      Type        *
 * =============================================================================================================================== *
 * The kernel is filtered out           | GTPIN_STATUS_WARNING_KERNEL_FILTERED    | CONTINUE  [TERMINATE]       | IGtKernelEvent   *
 * Broken communication with the driver | GTPIN_STATUS_ERROR_NO_DRIVER            | CONTINUE  [TERMINATE]       | IGtEvent         *
 * The kernel CFG is too complex        | GTPIN_STATUS_ERROR_UNSUPPORTED_CFG      | CONTINUE  [TERMINATE]       | IGtKernelEvent   *
 * Register allocation failed           | GTPIN_STATUS_ERROR_REG_ALLOC_FAILURE    | CONTINUE  [TERMINATE]       | IGtKernelEvent   *
 * Instrumentation failed               | GTPIN_STATUS_ERROR_INSTRUMENT_FAILURE   | CONTINUE  [RETRY, TERMINATE]| IGtKernelEvent   *
 * GTPin/tool API version mismatch      | GTPIN_STATUS_ERROR_API_VERSION_MISMATCH | CONTINUE  [TERMINATE]       | IGtEvent         *
 * Invalid API argument                 | GTPIN_STATUS_ERROR_INVALID_API_ARGUMENT | CONTINUE  [TERMINATE]       | IGtEvent         *
 * Generic access error                 | GTPIN_STATUS_ERROR_ACCESS_DENIED        | CONTINUE  [TERMINATE]       | IGtEvent         *
 * API is not available in this context | GTPIN_STATUS_ERROR_INVALID_API_CALL     | CONTINUE  [TERMINATE]       | IGtEvent         *
 * Memory buffer is too small           | GTPIN_STATUS_ERROR_BUFFER_OVERFLOW      | CONTINUE  [TERMINATE]       | IGtEvent         *
 * HLI module compilation failed        | GTPIN_STATUS_ERROR_HLI_COMPILE_FAILURE  | CONTINUE  [TERMINATE]       | IGtEvent         *
 * HLI binary module build failed       | GTPIN_STATUS_ERROR_HLI_BUILD_FAILURE    | CONTINUE  [TERMINATE]       | IGtKernelEvent   *
 * GFX runtime error                    | GTPIN_STATUS_ERROR_GFX_RUNTIME_FAILURE  | CONTINUE  [TERMINATE]       | IGtEvent         *
 * Unsupported feature/API              | GTPIN_STATUS_ERROR_UNSUPPORTED_FEATURE  | CONTINUE  [TERMINATE]       | IGtEvent         *
 * Binary link failed                   | GTPIN_STATUS_ERROR_BIN_LINK_FAILURE     | CONTINUE  [TERMINATE]       | IGtEvent         *
 * Internal error                       | GTPIN_STATUS_ERROR_INTERNAL             | TERMINATE []                | IGtInternalEvent *
 * Internal warning                     | GTPIN_STATUS_WARNING_INTERNAL           | CONTINUE  [TERMINATE]       | IGtInternalEvent *
 * Gfx driver version mismatch          | GTPIN_STATUS_ERROR_GFX_DRIVER_VERSION   | TERMINATE [SHUTDOWN_GTPIN]  | IGtEvent         *
 * =============================================================================================================================== *
@endverbatim
 */
class IGtEventHandler
{
public:
    /*!
     * @brief Handle the specified event
     * @param[in]   event   The event to be handled
     * @return GTPin action requested by the handler
     */
    virtual GtEventReaction OnEvent(const IGtEvent& event) = 0;
    virtual GtEventReaction OnEvent(const IGtKernelEvent& event) = 0;   ///< @copydoc OnEvent(const IGtEvent&)
    virtual GtEventReaction OnEvent(const IGtInternalEvent& event) = 0; ///< @copydoc OnEvent(const IGtEvent&)

protected:
    /// @internal Protected destructor
    virtual ~IGtEventHandler() = default;
};
GTPIN_ASSERT_PURE_ABSTRACT(IGtEventHandler);

/* ===================================================================================================== */
// GtStatus implementation
/* ===================================================================================================== */
inline const char* GtStatus::ToString() const
{
    switch (_value)
    {
    case GTPIN_STATUS_SUCCESS:                      return "SUCCESS";
    case GTPIN_STATUS_WARNING:                      return "GTPIN_WARNING";
    case GTPIN_STATUS_ERROR:                        return "GTPIN_ERROR";
    case GTPIN_STATUS_WARNING_KERNEL_FILTERED:      return "GTPIN_WARNING_KERNEL_FILTERED: Instrumentation is skipped. The kernel is filtered out";
    case GTPIN_STATUS_ERROR_NO_DRIVER:              return "GTPIN_ERROR_NO_DRIVER: The communication with the driver is not established/broken";
    case GTPIN_STATUS_ERROR_UNSUPPORTED_CFG:        return "GTPIN_ERROR_UNSUPPORTED_CFG: Kernel could not be instrumented: CFG is too complex";
    case GTPIN_STATUS_ERROR_REG_ALLOC_FAILURE:      return "GTPIN_ERROR_REG_ALLOC_FAILURE: Kernel could not be instrumented: register allocation failed";
    case GTPIN_STATUS_ERROR_INSTRUMENT_FAILURE:     return "GTPIN_ERROR_INSTRUMENT_FAILURE: Kernel instrumentation failed";
    case GTPIN_STATUS_ERROR_API_VERSION_MISMATCH:   return "GTPIN_ERROR_API_VERSION_MISMATCH: GTPin/tool API version mismatch";
    case GTPIN_STATUS_ERROR_INVALID_API_ARGUMENT:   return "GTPIN_ERROR_INVALID_API_ARGUMENT: Invalid API argument specified by the tool";
    case GTPIN_STATUS_ERROR_ACCESS_DENIED:          return "GTPIN_ERROR_ACCESS_DENIED: Generic access error";
    case GTPIN_STATUS_ERROR_INVALID_API_CALL:       return "GTPIN_ERROR_INVALID_API_CALL: API is not available in the current context (callback)";
    case GTPIN_STATUS_ERROR_BUFFER_OVERFLOW:        return "GTPIN_ERROR_BUFFER_OVERFLOW: Memory buffer is too small";
    case GTPIN_STATUS_ERROR_HLI_COMPILE_FAILURE:    return "GTPIN_ERROR_HLI_COMPILE_FAILURE: HLI module compilation failed";
    case GTPIN_STATUS_ERROR_HLI_BUILD_FAILURE:      return "GTPIN_ERROR_HLI_BUILD_FAILURE: HLI binary module build failed";
    case GTPIN_STATUS_ERROR_GFX_RUNTIME_FAILURE:    return "GTPIN_ERROR_GFX_RUNTIME_FAILURE: GFX runtime error";
    case GTPIN_STATUS_ERROR_UNSUPPORTED_FEATURE:    return "GTPIN_ERROR_UNSUPPORTED_FEATURE: Unsupported feature/API";
    case GTPIN_STATUS_ERROR_BIN_LINK_FAILURE:       return "GTPIN_ERROR_BIN_LINK_FAILURE: Binary link failed";
    case GTPIN_STATUS_ERROR_D3D_PROTOCOL_ERROR:     return "GTPIN_STATUS_ERROR_D3D_PROTOCOL_ERROR: D3D protocol error";
    case GTPIN_STATUS_ERROR_INTERNAL:               return "GTPIN_ERROR_INTERNAL:";
    case GTPIN_STATUS_WARNING_INTERNAL:             return "GTPIN_WARNING_INTERNAL:";
    case GTPIN_STATUS_ERROR_GFX_DRIVER_VERSION:     return "GTPIN_ERROR_GFX_DRIVER_VERSION: mismatch with the driver version";
    default:                                        return "";
    }
}

/* ===================================================================================================== */
// GtEventReaction implementation
/* ===================================================================================================== */
inline const char* GtEventReaction::ToString()  const
{
    switch (_value)
    {
    case EVENT_REACTION_DEFAULT:        return  "Default";
    case EVENT_REACTION_CONTINUE:       return  "Continue";
    case EVENT_REACTION_RETRY:          return  "Retry";
    case EVENT_REACTION_TERMINATE:      return  "Terminate";
    case EVENT_REACTION_SHUTDOWN_GTPIN: return  "Shutdown GTPin";
    default:                            return  "";
    }
}

#pragma pack(pop)
}
#endif
