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

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


/*!
 * @file Registry of GTPin configuration arguments (KNOBs)
 */

#ifndef IGT_KNOB_REGISTRY_H_
#define IGT_KNOB_REGISTRY_H_

#include <stdint.h>

#include "gt_basic_defs.h"
#include "gt_knob_defs.h"
#include "igt_knob_arg.h"

#if !defined(GTPIN_IMPL)
#include "igt_core.h"
#endif

namespace gtpin
{
#pragma pack(push, 8)

/*! @addtogroup GTPIN_KNOB 
@{
*/

/* ===================================================================================================== */
// Class IGtKnobRegistry
/* ===================================================================================================== */
/*!
 * @brief Abstract singleton that provides interface to the container of GTPin configuration arguments (KNOBs)
 */
class IGtKnobRegistry
{
public:
    /*!
     * Create a new %Knob argument
     * @param[in]   name        Unique identifier of the %Knob
     * @param[in]   message     Description of the %Knob to be printed in the help message
     * @param[in]   flags       Combination of KNOB_FLAG properties
     * @param[in]   symId       Single-symbol identifier of the %Knob in the command line,
     *                          or \0 character if such identifier does not exist
     * @param[in]   knobType    Type of the %Knob value(s)
     * @return  Handle to the new %Knob, or NULL if requested %Knob cannot be created (e.g. due to name duplication)
     *          In case of error, the IGtCore::LastError() function provides the specific failure reason
     */
    virtual GTPinKnob CreateArg(const char* name, const char* message, uint32_t flags, char symId, KNOB_TYPE knobType) = 0;

    /*!
     * Find an existing %Knob argument with the specified name
     * @param[in]   name        Identifier of the requested %Knob
     * @return  Handle to the requested %Knob, or NULL if a %Knob with the specified name does not exist
     */
    virtual GTPinKnob FindArg(const char* name) const = 0;

    /*!
     * @return Total number of %Knob arguments in the registry
     */
    virtual uint32_t NumArgs() const = 0;

    /*!
     * Get %Knob argument with the specified ordinal number.
     * This function can be used to iterate elements of this %Knob registry.
     * @param[in]   argOrdinal  Ordinal number of a %Knob argument - any integer in the [0, NumArgs()) range
     * @return  Handle to the requested %Knob
     */
    virtual GTPinKnob GetArg(uint32_t argOrdinal) const = 0;

    /*!
     * Get value of the %Knob specified by its name
     * @param[out]  value    Structure that receives the requested %Knob value and its type
     * @param[in]   name     The name of the %Knob argument
     * @param[in]   index    Index of the requested element in the vector of values
     * @return  true - success; false - the requested %Knob is not found
     */
    inline bool GetArgValue(KnobValue& value, const char* name, uint32_t index = 0) const;

    /*!
     * Set value of the %Knob specified by its name
     * @param[in]   value    The %Knob value to be set/added
     * @param[in]   name     The name of the %Knob argument
     * @return  true - success; false - the requested %Knob is not found
     */
    inline bool SetArgValue(const KnobValue& value, const char* name);

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

/// @return Single instance of the IGtKnobRegistry class
#if !defined(GTPIN_IMPL)
inline IGtKnobRegistry& Knobs()
{
    static IGtKnobRegistry& knobs = GTPin_GetCore()->Knobs();
    return knobs;
}
#else
IGtKnobRegistry& Knobs();
#endif
/*! @} */

/* ===================================================================================================== */
// IGtKnobRegistry inline implementation and template wrappers
/* ===================================================================================================== */
bool IGtKnobRegistry::GetArgValue(KnobValue& value, const char* name, uint32_t index) const
{
    GTPinKnob knob = Knobs().FindArg(name);
    return ((knob == nullptr) ? false : knob->GetValue(value, index));
}

bool IGtKnobRegistry::SetArgValue(const KnobValue& value, const char* name)
{
    GTPinKnob knob = Knobs().FindArg(name);
    return ((knob == nullptr) ? false : knob->AddValue(value));
}

/*! @ingroup GTPIN_KNOB
 * Get value of the %Knob specified by its name
 * 
 * @param[in]   name     The name of the %Knob argument
 * @param[in]   index    Index of the requested element in the vector of values
 * @return  The requested %Knob value
 */
template <typename T> inline T GetKnobValue(const char* name, uint32_t index = 0)
{
    KnobValue knobValue;
    GTPinKnob knob   = Knobs().FindArg(name);            GTPIN_ASSERT(knob != nullptr);
    bool      status = knob->GetValue(knobValue, index); GTPIN_ASSERT(status);
    return ExtractKnobValue<T>(knobValue);
}

/*! @ingroup GTPIN_KNOB
 * Set value of the %Knob specified by its name
 * 
 * @param[in]   value    The %Knob value to be set/added
 * @param[in]   name     The name of the %Knob argument
 */
template <typename T> inline void SetKnobValue(const T& value, const char* name)
{
    GTPinKnob knob   = Knobs().FindArg(name);                GTPIN_ASSERT(knob != nullptr);
    bool      status = knob->AddValue(MakeKnobValue(value)); GTPIN_ASSERT(status);
}

#pragma pack(pop)
}
#endif
