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

#ifndef TANGO_SERVER_DOUBLEATTRPROP_H
#define TANGO_SERVER_DOUBLEATTRPROP_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 DoubleAttrProp class
//
// description :    This is a template class which holds values of a compound attribute property (like rel_change,
//                    abs_change, archive_rel_change, archive_abs_change) which consists of two values, and its string
//                    representation.
//
//==================================================================================================================

/**
 * This class represents a Tango compound attribute property which consists of
 * two values.
 *
 *
 *
 *
 * @headerfile tango.h
 * @ingroup Server
 */

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

    /**
     * Create a new DoubleAttrProp object.
     *
     * @param values A vector containing two values of
     * the compound attribute property.
     */
    DoubleAttrProp(const std::vector<T> &values) :
        val(values),
        is_value(true)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        for(size_t i = 0; i < values.size(); i++)
        {
            if(i > 0)
            {
                st << ",";
            }
            st << values[i];
        }
        str = st.str();
    }

    /**
     * Create a new DoubleAttrProp object.
     *
     * @param value The figure assigned to both values of the compound attribute property.
     */
    DoubleAttrProp(const T &value) :
        is_value(true)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        st << value;
        str = st.str();
        val.push_back(value);
    }

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

    /**
     * Create a new DoubleAttrProp object.
     *
     * @param value_str The string representation of values of the compound attribute property.
     */
    DoubleAttrProp(const std::string &value_str) :
        str(value_str),
        is_value(false)
    {
    }

    //@}

    /**@name Assignment operators
     * These operators allow to assign the values of the compound attribute property
     * by providing the values or their string representations.
     */
    //@{
    /**
     * Assign the values of the compound attribute property.
     *
     * @param values A vector containing compound attribute property values.
     *
     * @return DoubleAttrProp object with both values of the compound attribute property
     * and their string representation set.
     */
    DoubleAttrProp &operator=(const std::vector<T> &values)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        for(size_t i = 0; i < values.size(); i++)
        {
            if(i > 0)
            {
                st << ",";
            }
            st << values[i];
        }
        str = st.str();
        val = values;
        is_value = true;
        return *this;
    }

    /**
     * Assign the values of the compound attribute property.
     *
     * @param value A figure representing both values of the compound attribute property.
     *
     * @return DoubleAttrProp object with both values of the compound attribute property
     * and their string representation set.
     */
    DoubleAttrProp &operator=(const T &value)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        if(Tango::tango_type_traits<T>::type_value() == Tango::DEV_UCHAR)
        {
            st << (short) value; // to represent the numeric value
        }
        else
        {
            st << value;
        }
        str = st.str();
        val.push_back(value);
        is_value = true;
        return *this;
    }

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

    /**
     * Assign the values of the compound attribute property.
     *
     * @param value_str A string representation of values of the compound attribute property.
     *
     * @return DoubleAttrProp object with string representation of values of
     * the compound attribute property set.
     */
    DoubleAttrProp &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 DoubleAttrProp instance
     * data members
     */
    //@{
    /**
     * Get the vector containing the compound attribute property values.
     *
     * @return The vector containing the compound attribute property values.
     */
    const std::vector<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 compound attribute property values.
     *
     * @return The string representation of the compound attribute property values.
     */
    const std::string &get_str() const
    {
        return str;
    }

    /**
     * Set the compound attribute property values.
     *
     * The values are automatically converted to their string representation.
     *
     * @param values The vector containing the compound attribute property values.
     */
    void set_val(const std::vector<T> &values)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        for(size_t i = 0; i < values.size(); i++)
        {
            if(i > 0)
            {
                st << ",";
            }
            st << values[i];
        }
        str = st.str();
        val = values;
        is_value = true;
    }

    /**
     * Set the compound attribute property values.
     *
     * The figure provided is set for both values of the compound attribute property
     * and is automatically converted to its string representation.
     *
     * @param value The figure representing both values of the compound attribute property.
     */
    void set_val(const T &value)
    {
        TangoSys_MemStream st;
        st.precision(TANGO_FLOAT_PRECISION);
        st << value;
        str = st.str();
        val.push_back(value);
        is_value = true;
    }

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

    /**
     * Set string representation of the compound attribute property values.
     *
     * @param value_str The string representation of the compound attribute property values.
     */
    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 compound attribute property values have been assigned.
     */
    //@{
    /**
     * Check if the compound attribute property values have been assigned.
     *
     * This method returns a boolean set to true if the compound attribute property values
     * have been assigned.
     *
     * @return A boolean set to true if the compound attribute property values have 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:
    std::vector<T> val;
    std::string str;
    bool is_value;

    //
    // The extension class
    //

    class DoubleAttrPropExt
    {
    };

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

//
// DoubleAttrProp template specialisation for DevUChar
//

template <>
inline DoubleAttrProp<DevUChar>::DoubleAttrProp(const std::vector<DevUChar> &values) :
    val(values),
    is_value(true)
{
    TangoSys_MemStream st;
    st.precision(TANGO_FLOAT_PRECISION);
    for(size_t i = 0; i < values.size(); i++)
    {
        if(i > 0)
        {
            st << ",";
        }
        st << (short) values[i]; // to represent the numeric value
    }
    str = st.str();
}

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

template <>
inline DoubleAttrProp<DevUChar> &DoubleAttrProp<DevUChar>::operator=(const std::vector<DevUChar> &values)
{
    TangoSys_MemStream st;
    st.precision(TANGO_FLOAT_PRECISION);
    for(size_t i = 0; i < values.size(); i++)
    {
        if(i > 0)
        {
            st << ",";
        }
        st << (short) values[i]; // to represent the numeric value
    }
    str = st.str();
    val = values;
    is_value = true;
    return *this;
}

template <>
inline DoubleAttrProp<DevUChar> &DoubleAttrProp<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.push_back(value);
    is_value = true;
    return *this;
}

template <>
inline void DoubleAttrProp<DevUChar>::set_val(const std::vector<DevUChar> &values)
{
    TangoSys_MemStream st;
    st.precision(TANGO_FLOAT_PRECISION);
    for(size_t i = 0; i < values.size(); i++)
    {
        if(i > 0)
        {
            st << ",";
        }
        st << (short) values[i]; // to represent the numeric value
    }
    str = st.str();
    val = values;
    is_value = true;
}

template <>
inline void DoubleAttrProp<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.push_back(value);
    is_value = true;
}

} // namespace Tango

#endif // TANGO_SERVER_DOUBLEATTRPROP_H
