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

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


/*!
 * @file Common definitions of the configuration (KNOB) interface.
 * @note Currently, KNOB interface in GTPin supports a few basic types of Knobs: bool, int, string, etc.
 *       Adding support for a new type 'T' is as simple as following:
 *       - Define the corresponding value in the KNOB_TYPE enumerator
 *       - Add the corresponding member to the KnobValue structure
 *       - Provide 'T'-specializations for the following template functions: 
 *         KnobType(), MakeKnobValue() and ExtractKnobValue()
 *       - Extend implementation of KnobTypeName() and DispatchKnobType() functions
 */

#ifndef KNOB_DEFS_H
#define KNOB_DEFS_H

#include <stddef.h>
#include <stdint.h>
#include <assert.h>

/// Handle to a Knob argument
#if defined(__cplusplus)
typedef class KnobArg*  GTPinKnob;
#else
typedef void*           GTPinKnob;
#endif

#if defined(__cplusplus)
extern "C"
{
#endif

/// Possible types of Knob values
typedef enum KNOB_TYPE
{
    KNOB_TYPE_INVALID,      ///< Indicates invalid Knob type
    KNOB_TYPE_BOOL,         ///< Boolean (bool) type
    KNOB_TYPE_INTEGER,      ///< Integer (int) type
    KNOB_TYPE_DOUBLE,       ///< Floating-point (double) type
    KNOB_TYPE_STRING        ///< String (string) type
} KNOB_TYPE;

/// Knob flags; can be combined to specify various Knob properties 
typedef enum KNOB_FLAG
{
    KNOB_FLAG_NONE      = 0,    ///< Regular Knob
    KNOB_FLAG_INTERNAL  = 0x1,  ///< GTPin internal (development) Knob
    KNOB_FLAG_REQUIRED  = 0x2,  ///< Indicates that the Knob value must be specified in the command line
    KNOB_FLAG_VECTOR    = 0x4   ///< A Knob associated with a vector of values. If this flag is turned off,
                                    ///< the Knob is assumed to have a single, scalar value
} KNOB_FLAG;

/// Status values returned by the Knob interface functions
typedef enum KNOB_STATUS
{
    KNOB_STATUS_SUCCESS,            ///< Success
    KNOB_STATUS_ERROR,              ///< General error
    KNOB_STATUS_ERROR_SHORT_BUFFER, ///< The buffer specified by the client is too short
    KNOB_STATUS_ERROR_TYPE_MISMATCH ///< The value specified by the client does not match the Knob type
} KNOB_STATUS;

/// Knob descriptor: set of immutable Knob properties
typedef struct KnobDescriptor
{
    const char*  name;      ///< Unique identifier of the Knob
    const char*  helpMsg;   ///< Description of the Knob in the help message
    uint32_t     flags;     ///< A combination of KNOB_FLAG properties
    char         symId;     ///< Single-symbol identifier of the Knob in the command line,
                            ///< or \0 character if such identifier does not exist
    KNOB_TYPE    knobType;  ///< Type of the Knob value(s)
} KnobDescriptor;

/// Structure that holds knob value along with its type
typedef struct KnobValue
{
    KNOB_TYPE       type;   ///< Type of the Knob value
    union
    {
        bool        _bool;      ///< knob value for 'type' = KNOB_TYPE_BOOL
        int         _int;       ///< knob value for 'type' = KNOB_TYPE_INTEGER
        double      _double;    ///< knob value for 'type' = KNOB_TYPE_DOUBLE
        const char* _str;       ///< knob value for 'type' = KNOB_TYPE_STRING
    }value;
} KnobValue;

#if defined(__cplusplus)
}
#endif


#if defined(__cplusplus)
#include <string>

/*!
 * Template function that converts type 'T' into corresponding KNOB_TYPE enumeration value
 */
template <typename T>   inline KNOB_TYPE KnobType()                 { return KNOB_TYPE_INVALID; }
template<>              inline KNOB_TYPE KnobType<bool>()           { return KNOB_TYPE_BOOL; }
template<>              inline KNOB_TYPE KnobType<int>()            { return KNOB_TYPE_INTEGER; }
template<>              inline KNOB_TYPE KnobType<double>()         { return KNOB_TYPE_DOUBLE; }
template<>              inline KNOB_TYPE KnobType<std::string>()    { return KNOB_TYPE_STRING; }

/*!
 * Template function that constructs KnobValue out of the value of type 'T'
 */
template <typename T> inline KnobValue MakeKnobValue(const T& val);

template <> inline KnobValue MakeKnobValue<bool>(const bool& val)
{ KnobValue knobVal; knobVal.type = KNOB_TYPE_BOOL; knobVal.value._bool = val; return knobVal; }

template <> inline KnobValue MakeKnobValue<int>(const int& val)
{ KnobValue knobVal; knobVal.type = KNOB_TYPE_INTEGER; knobVal.value._int = val; return knobVal; }

template <> inline KnobValue MakeKnobValue<double>(const double& val)
{ KnobValue knobVal; knobVal.type = KNOB_TYPE_DOUBLE; knobVal.value._double = val; return knobVal; }

template <> inline KnobValue MakeKnobValue<std::string>(const std::string& val)
{ KnobValue knobVal; knobVal.type = KNOB_TYPE_STRING; knobVal.value._str = val.c_str(); return knobVal; }

/*!
 * Template function that extracts value of type 'T' out of KnobValue
 */
template <typename T> inline T ExtractKnobValue(const KnobValue& knobVal);

template <> inline bool ExtractKnobValue<bool>(const KnobValue& knobVal)
{ assert(knobVal.type == KNOB_TYPE_BOOL); return knobVal.value._bool; }

template <> inline int ExtractKnobValue<int>(const KnobValue& knobVal)
{ assert(knobVal.type == KNOB_TYPE_INTEGER); return knobVal.value._int; }

template <> inline double ExtractKnobValue<double>(const KnobValue& knobVal)
{ assert(knobVal.type == KNOB_TYPE_DOUBLE); return knobVal.value._double; }

template <> inline std::string ExtractKnobValue<std::string>(const KnobValue& knobVal)
{ assert(knobVal.type == KNOB_TYPE_STRING); return knobVal.value._str; }

/*!
 * @return Human readable name of the specified Knob type
 */
inline const char* KnobTypeName(KNOB_TYPE knobType)
{
    switch (knobType)
    {
    case KNOB_TYPE_BOOL:    return "bool";
    case KNOB_TYPE_INTEGER: return "int";
    case KNOB_TYPE_DOUBLE:  return "double";
    case KNOB_TYPE_STRING:  return "string";
    default:                return "";
    };
}

/*!
 * Template function that converts the specified 'knobType' into corresponding C++type 'T',
 * and invokes method void F::ProcessKnobType<T>() in the specified object 'f'.
 */
template <typename F> inline void DispatchKnobType(F& f, KNOB_TYPE knobType)
{
    switch (knobType)
    {
    case KNOB_TYPE_BOOL:    f.template ProcessKnobType<bool>();         return;
    case KNOB_TYPE_INTEGER: f.template ProcessKnobType<int>();          return;
    case KNOB_TYPE_DOUBLE:  f.template ProcessKnobType<double>();       return;
    case KNOB_TYPE_STRING:  f.template ProcessKnobType<std::string>();  return;
    default:                                                            return;
    };
}

#endif

#endif
