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

#ifndef TANGO_SERVER_ATTRPROP_H
#define TANGO_SERVER_ATTRPROP_H

#include <tango/common/defines.h>
#include <tango/common/tango_const.h>
#include <tango/common/tango_type_traits.h>
#include <tango/server/Except.h>

#include <memory>
#include <sstream>
#include <string>
#include <vector>

namespace Tango
{

//=================================================================================================================
//
//            The AttrProp class
//
// description :
//        This is a template class which holds a value of attribute property and its string representation.
//
//=================================================================================================================

/**
 * This class represents a Tango attribute property.
 *
 *
 *
 *
 * @headerfile tango.h
 * @ingroup Server
 */

template <typename T>
class AttrProp
{
  public:
    /**@name Constructors
     * Miscellaneous constructors */
    //@{
    /**
     * Default constructor.
     */
    AttrProp() :
        is_value(false),
        ext(nullptr)
    {
    }

    /**
     * Create a new AttrProp object.
     *
     * @param value The attribute property value.
     */
    AttrProp(const T &value) :
        val(value),
        is_value(true),
        ext(nullptr)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        st << value;
        str = st.str();
    }

    /**
     * Create a new AttrProp object.
     *
     * @param value_str The 'C string' representation of attribute property.
     */
    AttrProp(const char *value_str) :
        str(std::string(value_str)),
        is_value(false),
        ext(nullptr)
    {
    }

    /**
     * Create a new AttrProp object.
     *
     * @param value_str The string representation of attribute property value.
     */
    AttrProp(const std::string &value_str) :
        str(value_str),
        is_value(false),
        ext(nullptr)
    {
    }

    //@}

    /**@name Assignment operators
     * These operators allow to assign the value of the property by providing
     * the value or its string representation.
     */
    //@{
    /**
     * Assign the value of the attribute property.
     *
     * @param value A value of the attribute property.
     *
     * @return AttrProp object with both value and its string representation set.
     */
    AttrProp &operator=(const T &value)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        st << value;
        str = st.str();
        val = value;
        is_value = true;
        return *this;
    }

    /**
     * Assign the string representation of the attribute property value.
     *
     * @param value_str A 'C string' representation of the attribute property value.
     *
     * @return AttrProp object with the string representation of its value set.
     */
    AttrProp &operator=(const char *value_str)
    {
        str = value_str;
        is_value = false;
        return *this;
    }

    /**
     * Assign the string representation of the attribute property value.
     *
     * @param value_str A string representation of the attribute property value.
     *
     * @return AttrProp object with the string representation of its value set.
     */
    AttrProp &operator=(const std::string &value_str)
    {
        str = value_str;
        is_value = false;
        return *this;
    }

    //@}

    /**@name Get/Set object members.
     * These methods allow the external world to get/set AttrProp instance
     * data members
     */
    //@{
    /**
     * Get the attribute property value.
     *
     * @return The attribute property value.
     */
    T get_val() const
    {
        if(!is_value)
        {
            std::string err_msg = "Numeric representation of the property's value (" + str + ") has not been set";
            TANGO_THROW_EXCEPTION(API_AttrPropValueNotSet, err_msg);
        }
        return val;
    }

    /**
     * Get string representation of the attribute property value.
     *
     * @return The string representation of the attribute property value.
     */
    const std::string &get_str() const
    {
        return str;
    }

    /**
     * Set the attribute property value.
     *
     * The value is automatically converted to its string representation.
     *
     * @param value The value of the attribute property.
     */
    void set_val(const T &value)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        st << value;
        str = st.str();
        val = value;
        is_value = true;
    }

    /**
     * Set string representation of the attribute property value.
     *
     * @param value_str The the 'C string' representation of the attribute property value.
     */
    void set_str(const char *value_str)
    {
        str = std::string(value_str);
        is_value = false;
    }

    /**
     * Set string representation of the attribute property value.
     *
     * @param value_str The the string representation of the attribute property value.
     */
    void set_str(const std::string &value_str)
    {
        str = value_str;
        is_value = false;
    }

    //@}

    /**@name Check method
     * A method returning a boolean flag set to true if
     * the attribute property value has been assigned.
     */
    //@{
    /**
     * Check if the attribute property value has been assigned.
     *
     * This method returns a boolean set to true if the attribute property value has been assigned.
     *
     * @return A boolean set to true if the attribute property value has been assigned
     */
    bool is_val() const
    {
        return is_value;
    }

    //@}

    /// @privatesection

    operator std::string() const
    {
        return str;
    }

    operator const char *() const
    {
        return str.c_str();
    }

  private:
    T val;
    std::string str;
    bool is_value;

    //
    // The extension class
    //

    class AttrPropExt
    {
    };

    std::unique_ptr<AttrPropExt> ext; // Class extension
};

template <>
inline AttrProp<DevUChar>::AttrProp(const DevUChar &value) :
    val(value),
    is_value(true),
    ext(nullptr)
{
    TangoSys_MemStream st;
    st.precision(TANGO_FLOAT_PRECISION);
    st << (short) value; // to represent the numeric value
    str = st.str();
}

template <>
inline AttrProp<DevUChar> &AttrProp<DevUChar>::operator=(const DevUChar &value)
{
    TangoSys_MemStream st;
    st.precision(TANGO_FLOAT_PRECISION);
    st << (short) value; // to represent the numeric value
    str = st.str();
    val = value;
    is_value = true;
    return *this;
}

template <>
inline void AttrProp<DevUChar>::set_val(const DevUChar &value)
{
    TangoSys_MemStream st;
    st.precision(TANGO_FLOAT_PRECISION);
    st << (short) value; // to represent the numeric value
    str = st.str();
    val = value;
    is_value = true;
}

} // namespace Tango

#endif // TANGO_SERVER_ATTRPROP_H
