/*
 * SPDX-FileCopyrightText: 2004 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#ifndef TANGO_SERVER_ATTRVALUE_H
#define TANGO_SERVER_ATTRVALUE_H

#include <tango/client/ApiExcept.h>
#include <tango/client/DbDatum.h>
#include <tango/common/defines.h>
#include <tango/common/tango_type_traits.h>
#include <tango/common/utils/assert.h>
#include <tango/server/Attr.h>
#include <tango/server/AttrProp.h>
#include <tango/server/DeviceEventSubscriptionState.h>
#include <tango/server/EncodedAttribute.h>
#include <tango/server/exception_reason_consts.h>
#include <tango/server/FwdAttr.h>
#include <tango/server/ImageAttr.h>
#include <tango/server/Logging.h>
#include <tango/server/MultiClassAttribute.h>
#include <tango/server/SpectrumAttr.h>

#include <algorithm>
#include <iterator>
#include <map>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>

namespace Tango
{

typedef union _Attr_CheckVal
{
    short sh;
    DevLong lg;
    double db;
    float fl;
    unsigned short ush;
    unsigned char uch;
    DevLong64 lg64;
    DevULong ulg;
    DevULong64 ulg64;
    DevState d_sta;
    /**
     * Returns a ref to the proper field based on type.
     * Does not manage memory.
     */
    template <class T>
    const T &get_value() const;
} Attr_CheckVal;

template <>
inline const Tango::DevShort &Attr_CheckVal::get_value() const
{
    return sh;
}

template <>
inline const Tango::DevLong &Attr_CheckVal::get_value() const
{
    return lg;
}

template <>
inline const Tango::DevDouble &Attr_CheckVal::get_value() const
{
    return db;
}

template <>
inline const Tango::DevFloat &Attr_CheckVal::get_value() const
{
    return fl;
}

template <>
inline const Tango::DevUShort &Attr_CheckVal::get_value() const
{
    return ush;
}

template <>
inline const Tango::DevUChar &Attr_CheckVal::get_value() const
{
    return uch;
}

template <>
inline const Tango::DevLong64 &Attr_CheckVal::get_value() const
{
    return lg64;
}

template <>
inline const Tango::DevULong &Attr_CheckVal::get_value() const
{
    return ulg;
}

template <>
inline const Tango::DevULong64 &Attr_CheckVal::get_value() const
{
    return ulg64;
}

template <>
inline const Tango::DevState &Attr_CheckVal::get_value() const
{
    return d_sta;
}

class AttrValue
{
  public:
    // Define a variant type that holds unique pointers to each allowed array type.
    using ValueVariant = std::variant<std::monostate, // Represents an empty state.
                                      std::unique_ptr<DevVarShortArray>,
                                      std::unique_ptr<DevVarLongArray>,
                                      std::unique_ptr<DevVarFloatArray>,
                                      std::unique_ptr<DevVarDoubleArray>,
                                      std::unique_ptr<DevVarStringArray>,
                                      std::unique_ptr<DevVarUShortArray>,
                                      std::unique_ptr<DevVarBooleanArray>,
                                      std::unique_ptr<DevVarCharArray>,
                                      std::unique_ptr<DevVarLong64Array>,
                                      std::unique_ptr<DevVarULongArray>,
                                      std::unique_ptr<DevVarULong64Array>,
                                      std::unique_ptr<DevVarStateArray>,
                                      std::unique_ptr<DevVarEncodedArray>>;

    AttrValue() :
        data_{std::monostate{}}
    {
    }

    // Setter: accepts a smart pointer to one of the allowed types.
    template <typename T>
    void set(std::unique_ptr<T> value)
    {
        TANGO_LOG_DEBUG << "AttrValue::set()" << std::endl;
        static_assert(is_valid<T>(), "Type not allowed in AttrValue");
        data_ = std::move(value);
    }

    // Getter: returns a raw pointer (or nullptr if the stored type doesn't match).
    template <typename T>
    T *get()
    {
        TANGO_LOG_DEBUG << "AttrValue::get()" << std::endl;
        static_assert(is_valid<T>(), "Type not allowed in AttrValue");
        if(auto ptr = std::get_if<std::unique_ptr<T>>(&data_))
        {
            return ptr->get(); //
        }
        return nullptr;
    }

    template <typename T>
    std::unique_ptr<T> release()
    {
        TANGO_LOG_DEBUG << "AttrValue::release()" << std::endl;

        static_assert(is_valid<T>(), "Type not allowed in AttrValue");
        if(auto ptr = std::get_if<std::unique_ptr<T>>(&data_))
        {
            std::unique_ptr<T> temp = std::move(*ptr);
            data_ = std::monostate{};
            return temp;
        }
        return nullptr;
    }

    // Reset clears any stored value. After calling reset, has_value() returns false.
    void reset()
    {
        TANGO_LOG_DEBUG << "AttrValue::reset()" << std::endl;
        data_ = std::monostate{};
    }

    // Check if any value is set.
    bool has_value() const
    {
        TANGO_LOG_DEBUG << "AttrValue::has_value()" << std::endl;
        return !std::holds_alternative<std::monostate>(data_);
    }

  private:
    ValueVariant data_;

    // Helper to restrict the types that can be stored.
    template <typename T>
    static constexpr bool is_valid()
    {
        return std::is_same_v<T, DevVarShortArray> || std::is_same_v<T, DevVarLongArray> ||
               std::is_same_v<T, DevVarFloatArray> || std::is_same_v<T, DevVarDoubleArray> ||
               std::is_same_v<T, DevVarStringArray> || std::is_same_v<T, DevVarUShortArray> ||
               std::is_same_v<T, DevVarBooleanArray> || std::is_same_v<T, DevVarCharArray> ||
               std::is_same_v<T, DevVarLong64Array> || std::is_same_v<T, DevVarULongArray> ||
               std::is_same_v<T, DevVarULong64Array> || std::is_same_v<T, DevVarStateArray> ||
               std::is_same_v<T, DevVarEncodedArray>;
    }
};

typedef union _Attr_Value
{
    DevVarShortArray *sh_seq;
    DevVarLongArray *lg_seq;
    DevVarFloatArray *fl_seq;
    DevVarDoubleArray *db_seq;
    DevVarStringArray *str_seq;
    DevVarUShortArray *ush_seq;
    DevVarBooleanArray *boo_seq;
    DevVarCharArray *cha_seq;
    DevVarLong64Array *lg64_seq;
    DevVarULongArray *ulg_seq;
    DevVarULong64Array *ulg64_seq;
    DevVarStateArray *state_seq;
    DevVarEncodedArray *enc_seq;
} Attr_Value;

struct LastAttrValue
{
    bool inited;
    Tango::AttrQuality quality;
    CORBA::Any value;
    bool err;
    DevFailed except;
    AttrValUnion value_4;
    void store(const AttributeValue_5 *,
               const AttributeValue_4 *,
               const AttributeValue_3 *,
               const AttributeValue *,
               DevFailed *);
};

} // namespace Tango

#endif // TANGO_SERVER_ATTRVALUE_H
