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

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

/*!
 * @file GTPin configuration (KNOB) interface
 */

#ifndef GT_KNOB_H_
#define GT_KNOB_H_

#include <stdint.h>
#include <assert.h>
#include <string>
#include <vector>
#include <iostream>

#include "gt_knob_defs.h"
#include "igt_knob_arg.h"
#include "igt_knob_registry.h"

namespace gtpin
{
#pragma pack(push, 8)

/* ===================================================================================================== */
// Class Knob
/* ===================================================================================================== */
/*! @ingroup GTPIN_KNOB
 * @brief Template class that provides interface to %Knob argument of a type 'T'
 */
template <typename T> class Knob
{
public:
    /*!
     * @brief Construct a new %Knob argument, and register it in the %Knob database.
     * @param[in]   name        Unique identifier of the %Knob
     * @param[in]   initValue   The initial value of the %Knob. If this is a vector-value %Knob,
     *                          'initValue' will be the first element in the vector of values.
     * @param[in]   helpMsg     Description of the %Knob in the help message
     * @param[in]   flags       A combination of KNOB_FLAG properties
     * @param[in]   symId       A single-symbol identifier of the %Knob in the command line,
     *                          or \0 character if such identifier does not exist
     */
    inline Knob(const std::string& name, const T& initValue, const std::string& helpMsg = "",
                uint32_t flags = KNOB_FLAG_NONE, char symId = 0);

    /// Construct %Knob object for the specified %Knob handle
    inline Knob(GTPinKnob knob) : _knob(knob) {}

    inline void AddValue(const T& val);                 ///< Assign new value to this %Knob
    inline T GetValue(uint32_t idx = 0) const;          ///< Get the %Knob's value element at position 'idx'
    inline void Reset();                                ///< Clear the vector of %Knob values
    inline uint32_t NumValues() const;                  ///< Get the number of the %Knob value elements

    
    operator T() const               { return GetValue(); }                     ///< Conversion operator
    Knob& operator =  (const T& val) { Reset(); AddValue(val); return *this;}   ///< Assignment operator
    Knob& operator += (const T& val) { AddValue(val); return *this;}            ///< Addition assignment operator

private:
    GTPinKnob _knob;    ///< %Knob handle
};

/// Comparison operators for %Knob objects and their values
template <typename T> inline bool operator==(const Knob<T>& knob, const T& val) { return val == T(knob); }
template <typename T> inline bool operator==(const T& val, const Knob<T>& knob) { return val == T(knob); }
template <typename T> inline bool operator!=(const Knob<T>& knob, const T& val) { return val != T(knob); }
template <typename T> inline bool operator!=(const T& val, const Knob<T>& knob) { return val != T(knob); }

inline bool operator==(const Knob<std::string>& knob, const char* val) { return std::string(val) == std::string(knob); }
inline bool operator==(const char* val, const Knob<std::string>& knob) { return std::string(val) == std::string(knob); }
inline bool operator!=(const Knob<std::string>& knob, const char* val) { return std::string(val) != std::string(knob); }
inline bool operator!=(const char* val, const Knob<std::string>& knob) { return std::string(val) != std::string(knob); }

/// Streaming operator for %Knob objects
template <typename T> inline std::ostream& operator<<(std::ostream& os, const Knob<T>& knob) { return (os << T(knob));}  

/* ===================================================================================================== */
// Class KnobVector
/* ===================================================================================================== */
/*! @ingroup GTPIN_KNOB
 * @brief Template class that provides constructor for %Knob arguments associated with a vector of values of type 'T'
 */
template <typename T> class KnobVector : public Knob<T>
{
public:
    typedef std::vector<T> Vector; ///< Container of %Knob values

    /*!
     * Construct a new vector-value %Knob argument, and register it in the %Knob database.
     * @param[in]   name        Unique identifier of the %Knob
     * @param[in]   initValue   The initial vector of values
     * @param[in]   helpMsg     Description of the %Knob in the help message
     * @param[in]   flags       A combination of KNOB_FLAG properties. Specifying KNOB_FLAG_VECTOR
     *                          flag is not necessary; this flag is implied
     * @param[in]   symId       A single-symbol identifier of the %Knob in the command line,
     *                          or \0 character if such identifier does not exist
     */
    inline KnobVector(const std::string& name, const Vector& initValue, const std::string& helpMsg = "",
                      uint32_t flags = KNOB_FLAG_NONE, char symId = 0);

    KnobVector& operator =  (const T& val) { Knob<T>::operator =  (val); return *this;} ///< Assignment operator
    KnobVector& operator += (const T& val) { Knob<T>::operator += (val); return *this;} ///< Addition assignment operator
};

/// Comparison operators for %KnobVector objects
template <typename T> inline bool operator==(const KnobVector<T>& lh, const KnobVector<T>& rh)
{
    uint32_t numValues = lh.NumValues();
    if (numValues != rh.NumValues())                                                  { return false; }
    for (uint32_t i = 0; i != numValues; ++i) { if (lh.GetValue(i) != rh.GetValue(i)) { return false; } }
    return true;
}
template <typename T> inline bool operator!=(const KnobVector<T>& lh, const KnobVector<T>& rh) { return !(lh == rh); }

/// Streaming operator for %KnobVector objects
template <typename T> inline std::ostream& operator<<(std::ostream& os, const KnobVector<T>& knob)
{
    uint32_t numValues = knob.NumValues();
    for (uint32_t i = 0; i != numValues; ++i) { os << knob.GetValue(i); }
    return os;
}

/* ===================================================================================================== */
// %Knob implementation
/* ===================================================================================================== */
template <typename T> inline
Knob<T>::Knob(const std::string& name, const T& initValue, const std::string& helpMsg, uint32_t flags, char symId)
{
    _knob = Knobs().CreateArg(name.c_str(), helpMsg.c_str(), flags, symId, KnobType<T>());
    assert(_knob != nullptr);
    AddValue(initValue);
}

template <typename T> inline
T Knob<T>::GetValue(uint32_t idx) const
{
    KnobValue knobVal;
    bool status = _knob->GetValue(knobVal, idx);
    assert(status); (void)status;
    return ExtractKnobValue<T>(knobVal);
}

template <typename T> inline void Knob<T>::AddValue(const T& val)
{
    KnobValue knobVal = MakeKnobValue(val);
    _knob->AddValue(knobVal);
}

template <typename T> inline void Knob<T>::Reset()
{
    _knob->Reset();
}

template <typename T> inline uint32_t Knob<T>::NumValues() const
{
    return _knob->NumValues();
}

/* ===================================================================================================== */
// KnobVector implementation
/* ===================================================================================================== */
template <typename T> inline
KnobVector<T>::KnobVector(const std::string& name, const Vector& initValue, const std::string& helpMsg,
    uint32_t flags, char symId):
    Knob<T>(Knobs().CreateArg(name.c_str(), helpMsg.c_str(), (flags | KNOB_FLAG_VECTOR), symId, KnobType<T>()))
{
    for (const auto& val : initValue) { this->AddValue(val); }
}

#pragma pack(pop)
}
#endif
