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

#ifndef TANGO_CLIENT_DEVICEPIPE_H
#define TANGO_CLIENT_DEVICEPIPE_H

#include <tango/client/DataElement.h>
#include <tango/client/DevicePipeBlob.h>
#include <tango/common/tango_const.h>

#include <bitset>
#include <memory>
#include <string>
#include <vector>

namespace Tango
{

/****************************************************************************************
 *                                                                                         *
 *                     The DevicePipe class                                                   *
 *                     --------------------                                                *
 *                                                                                         *
 ***************************************************************************************/

/**
 * Fundamental type for sending/receiving data from device pipes
 *
 * This is the fundamental type for sending/receiving data to/from device pipe.
 *
 *
 * @headerfile tango.h
 * @ingroup Client
 */

class DevicePipe
{
  public:
    ///@publicsection
    /**@name Constructors */
    //@{
    /**
     * Create a DevicePipe object.
     *
     * Default constructor. The instance is empty
     *
     */
    DevicePipe();
    /**
     * Create a DevicePipe object with name
     *
     * Create one instance of the DevicePipe class and set its name
     *
     * @param [in] pipe_name The pipe name
     */
    DevicePipe(const std::string &pipe_name);
    /**
     * Create a DevicePipe object with name and root blob name.
     *
     * Create one instance of the DevicePipe class and set its name and its root blob name
     *
     * @param [in] pipe_name The pipe name
     * @param [in] root_blob_name The root blob name
     */
    DevicePipe(const std::string &pipe_name, const std::string &root_blob_name);

    //@}

    /**@name Get/Set methods */
    //@{
    /**
     * Set pipe name
     *
     * Set the device pipe name
     *
     * @param [in] pipe_name The pipe name
     */
    void set_name(const std::string &pipe_name)
    {
        name = pipe_name;
    }

    /**
     * Get pipe name
     *
     * Set the device pipe name
     *
     * @return The pipe name
     */
    const std::string &get_name()
    {
        return name;
    }

    /**
     * Set root blob name
     *
     * Set the root blob name
     *
     * @param [in] root_blob_name The root blob name
     */
    void set_root_blob_name(const std::string &root_blob_name)
    {
        the_root_blob.set_name(root_blob_name);
    }

    /**
     * Get root blob name
     *
     * Get the root blob name
     *
     * @return The root blob name
     */
    const std::string &get_root_blob_name()
    {
        return the_root_blob.get_name();
    }

//@}

/**@name Inserting data into a DevicePipe
 */
//@{
#ifdef GEN_DOC
    /**
     * Insert data into a device pipe
     *
     * Inserting data into a DevicePipe instance is simlar to inserting data into a DevicePipeBlob class instance.
     * See doc of DevicePipeBlob class insertion methods (DevicePipeBlob::operator<<) to get a complete documentation on
     * how to insert data into a DevicePipe
     *
     * @param [in] datum The data to be inserted into the DevicePipe
     * @exception WrongData if requested
     */
    DevicePipe &operator<<(short &datum);
#endif
    /**
     * Set blob data element number
     *
     * Set the blob data element number
     *
     * @param [in] nb The blob data element number
     */
    void set_data_elt_nb(size_t nb)
    {
        the_root_blob.set_data_elt_nb(nb);
    }

    /**
     * Set blob data element number and names
     *
     * Set the blob data element number and names. The data element number is the number of names in the input
     * parameter.
     *
     * @param [in] names The blob data element names
     */
    void set_data_elt_names(const std::vector<std::string> &names)
    {
        the_root_blob.set_data_elt_names(names);
    }

//@}

/**@name Extracting data from a DevicePipe
 */
//@{
#ifdef GEN_DOC
    /**
     * Extract data from a device pipe
     *
     * Extracting data from a DevicePipe instance is simlar to extracting data from a DevicePipeBlob class instance.
     * See doc of DevicePipeBlob class extraction methods (DevicePipeBlob::operator>>) to get a complete documentation
     * on how to extract data from a DevicePipe
     *
     * @param [in] datum The pipe data
     * @exception WrongData if requested
     */
    DevicePipe &operator>>(short &datum);
#endif
    /**
     * Get root blob data element number
     *
     * Get the root blob data element number
     *
     * @return The root blob data element number
     */
    size_t get_data_elt_nb()
    {
        return the_root_blob.get_data_elt_nb();
    }

    /**
     * Get root blob data elements name
     *
     * Get the root blob data elements name
     *
     * @return The root blob data elements name
     */
    std::vector<std::string> get_data_elt_names()
    {
        return the_root_blob.get_data_elt_names();
    }

    /**
     * Get root blob data element name
     *
     * Get root blob data element name for a single data element
     *
     * @param [in] ind The data element index within the root blob
     * @return The root blob data element name
     */
    std::string get_data_elt_name(size_t ind)
    {
        return the_root_blob.get_data_elt_name(ind);
    }

    /**
     * Get root blob data element value type
     *
     * Get root blob data element value type for a single data element
     *
     * @param [in] ind The data element index within the root blob
     * @return The root blob data element value type
     */
    int get_data_elt_type(size_t ind)
    {
        return the_root_blob.get_data_elt_type(ind);
    }

    //@}

    /**@name Exception and error related methods methods
     */
    //@{
    /**
     * Set exception flag
     *
     * It's a method which allows the user to switch on/off exception throwing when trying to insert/extract data from a
     * DevicePipe object. The following flags are supported :
     * @li @b isempty_flag - throw a WrongData exception (reason = API_EmptyDataElement) if user
     *       tries to extract data from one empty pipe data element. By default, this flag
     *       is set
     * @li @b wrongtype_flag - throw a WrongData exception (reason = API_IncompatibleArgumentType) if user
     *       tries to extract data with a type different than the type used for insertion. By default, this flag
     *       is set
     * @li @b notenoughde_flag - throw a WrongData exception (reason = API_PipeWrongArg) if user
     *       tries to extract data from a DevicePipe for a data element which does not exist. By default, this flag
     *       is set
     * @li @b blobdenamenotset_flag - Throw a WrongData exception (reason = API_PipeNoDataElement) if user tries to
     *       insert data into the blob while the name or number of data element has not been set with methods
     *       set_data_elt_nb() or set_data_elt_names()
     * @li @b mixing_flag - Throw a WrongData exception (reason = API_NotSupportedFeature) if user tries to mix
     *          insertion/extraction method (<< or >>) with operator[]
     *
     * @param [in] fl The exception flag
     */
    void exceptions(std::bitset<DevicePipeBlob::numFlags> fl)
    {
        the_root_blob.exceptions(fl);
    }

    /**
     * Get exception flag
     *
     * Returns the whole exception flags.
     * The following is an example of how to use these exceptions related methods
     * @code
     * DevicePipe dp;
     *
     * std::bitset<DevicePipeBlob::numFlags> bs = dp.exceptions();
     * std::cout << "bs = " << bs << std::endl;
     *
     * dp.set_exceptions(DevicePipeBlob::wrongtype_flag);
     * bs = dp.exceptions();
     *
     * std::cout << "bs = " << bs << std::endl;
     * @endcode
     *
     * @return The exception flag
     */
    std::bitset<DevicePipeBlob::numFlags> exceptions()
    {
        return the_root_blob.exceptions();
    }

    /**
     * Reset one exception flag
     *
     * Resets one exception flag
     *
     * @param [in] fl The exception flag
     */
    void reset_exceptions(DevicePipeBlob::except_flags fl)
    {
        the_root_blob.reset_exceptions(fl);
    }

    /**
     * Set one exception flag
     *
     * Sets one exception flag. See DevicePipe::exceptions() for a usage example.
     *
     * @param [in] fl The exception flag
     */
    void set_exceptions(DevicePipeBlob::except_flags fl)
    {
        the_root_blob.set_exceptions(fl);
    }

    /**
     * Check insertion/extraction success
     *
     * Allow the user to check if insertion/extraction into/from DevicePipe instance was successfull. This
     * method has to be used when exceptions are disabled.
     *
     * @return True if insertion/extraction has failed
     */
    bool has_failed()
    {
        return the_root_blob.has_failed();
    }

    /**
     * Get instance insertion/extraction state
     *
     * Allow the user to find out what was the reason of insertion/extraction into/from DevicePipe failure. This
     * method has to be used when exceptions are disabled.
     * Here is an example of how methods has_failed() and state() could be used
     * @code
     * DevicePipe dpb = ....
     *
     * std::bitset<DevicePipeBlob::numFlags> bs;
     * bs.reset();
     * dpb.exceptions(bs);
     *
     * DevLong dl;
     * dpb >> dl;
     *
     * if (dpb.has_failed() == true)
     * {
     *    std::bitset<DevicePipeBlob::numFlags> bs_err = dpb.state();
     *    if (dpb.test(DevicePipeBlob::isempty_flag) == true)
     *        .....
     * }
     * @endcode
     *
     * @return The error bit set.
     */
    std::bitset<DevicePipeBlob::numFlags> state()
    {
        return the_root_blob.state();
    }

    //@}

    /**
     * Print a DevicePipe instance
     *
     * Is an utility function to easily print the contents of a DevicePipe object. This function knows all types
     * which could be inserted in a DevicePipe object and print them accordingly. A special string is printed if
     * the DevicePipe object is empty
     * @code
     * DeviceProxy *dev = new DeviceProxy(“...”);
     * DevicePipe out;
     *
     * out = dev->read_pipe(“MyPipe”);
     * std::cout << “Pipe content: ” << out << std::endl;
     * @endcode
     *
     * @param [in] str The printing stream
     * @param [in] dd The instance to be printed
     */
    friend std::ostream &operator<<(std::ostream &str, const DevicePipe &dd);

    ///@privatesection
    DevicePipe(const DevicePipe &);
    DevicePipe &operator=(const DevicePipe &);
    DevicePipe(DevicePipe &&);
    DevicePipe &operator=(DevicePipe &&);
    ~DevicePipe();

    void set_time(TimeVal &_ti)
    {
        time = _ti;
    }

    DevicePipeBlob &get_root_blob()
    {
        return the_root_blob;
    }

    const DevicePipeBlob &get_root_blob() const
    {
        return the_root_blob;
    }

    DevicePipe &operator[](const std::string &);

  private:
    DevicePipeBlob the_root_blob; // Root blob
    std::string name;             // Pipe name
    TimeVal time;                 // When pipe has been read

    class DevicePipeExt
    {
      public:
        DevicePipeExt() { }
    };

    std::unique_ptr<DevicePipeExt> ext;
};

/****************************************************************************************
 *                                                                                         *
 *             Some DevicePipe, DevicePipeBlob and DataElement helper functions            *
 *             ----------------------------------------------------------------            *
 *                                                                                         *
 ***************************************************************************************/

DevicePipe &operator>>(DevicePipe &_dp, char *&datum);

//
// For DevicePipe insertion
//

template <typename T>
DevicePipe &operator<<(DevicePipe &, T &);

template <typename T>
DevicePipe &operator<<(DevicePipe &, T *);

template <typename T>
DevicePipe &operator<<(DevicePipe &, DataElement<T> &);

//
// For DevicePipe extraction
//

template <typename T>
DevicePipe &operator>>(DevicePipe &, T &);

template <typename T>
DevicePipe &operator>>(DevicePipe &, T *);

template <typename T>
DevicePipe &operator>>(DevicePipe &, DataElement<T> &);

/****************************************************************************************
 *                                                                                         *
 *             Some macros (shame on me, but I am too lazy)                                *
 *             -------------------------------------------                                    *
 *                                                                                         *
 ***************************************************************************************/

//
// A is the required value for the IDL enum descriminator
// B is the IDL enum method to get data
// C is data type name
//

#define EXTRACT_BASIC_TYPE(A, B, C)                                                                 \
    failed = false;                                                                                 \
    ext_state.reset();                                                                              \
                                                                                                    \
    if(extract_elt_array == nullptr)                                                                \
        ext_state.set(isempty_flag);                                                                \
    else if(extract_ctr > (int) extract_elt_array->length() - 1)                                    \
        ext_state.set(notenoughde_flag);                                                            \
    else if(extract_ctr == -1 && extract_ind == -1)                                                 \
        ext_state.set(mixing_flag);                                                                 \
    else                                                                                            \
    {                                                                                               \
        int ind;                                                                                    \
        if(extract_ind != -1)                                                                       \
            ind = extract_ind;                                                                      \
        else                                                                                        \
            ind = extract_ctr;                                                                      \
        const AttrValUnion *uni_ptr = &((*extract_elt_array)[ind].value);                           \
        AttributeDataType adt = uni_ptr->_d();                                                      \
        if(adt != A)                                                                                \
        {                                                                                           \
            if(adt == ATT_NO_DATA)                                                                  \
            {                                                                                       \
                if((*extract_elt_array)[ind].inner_blob.length() == 0)                              \
                    ext_state.set(isempty_flag);                                                    \
                else                                                                                \
                    ext_state.set(wrongtype_flag);                                                  \
            }                                                                                       \
            else                                                                                    \
                ext_state.set(wrongtype_flag);                                                      \
        }                                                                                           \
        else                                                                                        \
        {                                                                                           \
            datum = (uni_ptr->B())[0];                                                              \
            if(extract_ind != -1)                                                                   \
                extract_ind = -1;                                                                   \
            else                                                                                    \
                extract_ctr++;                                                                      \
        }                                                                                           \
    }                                                                                               \
                                                                                                    \
    if(ext_state.any() == true)                                                                     \
        failed = true;                                                                              \
                                                                                                    \
    if(ext_state.test(isempty_flag) == true && exceptions_flags.test(isempty_flag) == true)         \
        throw_is_empty("operator>>");                                                               \
                                                                                                    \
    if(ext_state.test(notenoughde_flag) == true && exceptions_flags.test(notenoughde_flag) == true) \
        throw_too_many("operator>>", true);                                                         \
                                                                                                    \
    if(ext_state.test(mixing_flag) == true && exceptions_flags.test(mixing_flag) == true)           \
        throw_mixing("operator>>");                                                                 \
                                                                                                    \
    if(ext_state.test(wrongtype_flag) == true && exceptions_flags.test(wrongtype_flag) == true)     \
        throw_type_except(C, "operator>>");

//
// A is the required value for the IDL enum descriminator
// B is the IDL enum method to get data
// C is the CORBA sequence type name
// D is data type name
//

#define EXTRACT_VECTOR_TYPE(A, B, C, D)                                                             \
    failed = false;                                                                                 \
    ext_state.reset();                                                                              \
                                                                                                    \
    if(extract_elt_array == nullptr)                                                                \
        ext_state.set(isempty_flag);                                                                \
    else if(extract_ctr > (int) extract_elt_array->length() - 1)                                    \
        ext_state.set(notenoughde_flag);                                                            \
    else if(extract_ctr == -1 && extract_ind == -1)                                                 \
        ext_state.set(mixing_flag);                                                                 \
    else                                                                                            \
    {                                                                                               \
        int ind;                                                                                    \
        if(extract_ind != -1)                                                                       \
            ind = extract_ind;                                                                      \
        else                                                                                        \
            ind = extract_ctr;                                                                      \
        const AttrValUnion *uni_ptr = &((*extract_elt_array)[ind].value);                           \
        AttributeDataType adt = uni_ptr->_d();                                                      \
        if(adt != A)                                                                                \
        {                                                                                           \
            if(adt == ATT_NO_DATA)                                                                  \
            {                                                                                       \
                if((*extract_elt_array)[ind].inner_blob.length() == 0)                              \
                    ext_state.set(isempty_flag);                                                    \
                else                                                                                \
                    ext_state.set(wrongtype_flag);                                                  \
            }                                                                                       \
            else                                                                                    \
                ext_state.set(wrongtype_flag);                                                      \
        }                                                                                           \
        else                                                                                        \
        {                                                                                           \
            const C &dvsa = uni_ptr->B();                                                           \
            datum << dvsa;                                                                          \
            if(extract_ind != -1)                                                                   \
                extract_ind = -1;                                                                   \
            else                                                                                    \
                extract_ctr++;                                                                      \
        }                                                                                           \
    }                                                                                               \
                                                                                                    \
    if(ext_state.any() == true)                                                                     \
        failed = true;                                                                              \
                                                                                                    \
    if(ext_state.test(isempty_flag) == true && exceptions_flags.test(isempty_flag) == true)         \
        throw_is_empty("operator>>");                                                               \
                                                                                                    \
    if(ext_state.test(notenoughde_flag) == true && exceptions_flags.test(notenoughde_flag) == true) \
        throw_too_many("operator>>", true);                                                         \
                                                                                                    \
    if(ext_state.test(mixing_flag) == true && exceptions_flags.test(mixing_flag) == true)           \
        throw_mixing("operator>>");                                                                 \
                                                                                                    \
    if(ext_state.test(wrongtype_flag) == true && exceptions_flags.test(wrongtype_flag) == true)     \
        throw_type_except(D, "operator>>");

//
// A is the required value for the IDL enum descriminator
// B is the IDL enum method to get data
// C is the CORBA sequence type name
// D is data type name
//

#define EXTRACT_SEQ_PTR_TYPE(A, B, C, D)                                                            \
    failed = false;                                                                                 \
    ext_state.reset();                                                                              \
                                                                                                    \
    if(extract_elt_array == nullptr)                                                                \
        ext_state.set(isempty_flag);                                                                \
    else if(extract_ctr > (int) extract_elt_array->length() - 1)                                    \
        ext_state.set(notenoughde_flag);                                                            \
    else if(extract_ctr == -1 && extract_ind == -1)                                                 \
        ext_state.set(mixing_flag);                                                                 \
    else                                                                                            \
    {                                                                                               \
        int ind;                                                                                    \
        if(extract_ind != -1)                                                                       \
            ind = extract_ind;                                                                      \
        else                                                                                        \
            ind = extract_ctr;                                                                      \
        const AttrValUnion *uni_ptr = &((*extract_elt_array)[ind].value);                           \
        AttributeDataType adt = uni_ptr->_d();                                                      \
        if(adt != A)                                                                                \
        {                                                                                           \
            if(adt == ATT_NO_DATA)                                                                  \
            {                                                                                       \
                if((*extract_elt_array)[ind].inner_blob.length() == 0)                              \
                    ext_state.set(isempty_flag);                                                    \
                else                                                                                \
                    ext_state.set(wrongtype_flag);                                                  \
            }                                                                                       \
            else                                                                                    \
                ext_state.set(wrongtype_flag);                                                      \
        }                                                                                           \
        else                                                                                        \
        {                                                                                           \
            C &dvsa = const_cast<C &>(uni_ptr->B());                                                \
            CORBA::Long max, len;                                                                   \
            max = dvsa.maximum();                                                                   \
            len = dvsa.length();                                                                    \
            datum->replace(max, len, dvsa.get_buffer((CORBA::Boolean) true), true);                 \
            if(extract_ind != -1)                                                                   \
                extract_ind = -1;                                                                   \
            else                                                                                    \
                extract_ctr++;                                                                      \
        }                                                                                           \
    }                                                                                               \
                                                                                                    \
    if(ext_state.any() == true)                                                                     \
        failed = true;                                                                              \
                                                                                                    \
    if(ext_state.test(isempty_flag) == true && exceptions_flags.test(isempty_flag) == true)         \
        throw_is_empty("operator>>");                                                               \
                                                                                                    \
    if(ext_state.test(notenoughde_flag) == true && exceptions_flags.test(notenoughde_flag) == true) \
        throw_too_many("operator>>", true);                                                         \
                                                                                                    \
    if(ext_state.test(mixing_flag) == true && exceptions_flags.test(mixing_flag) == true)           \
        throw_mixing("operator>>");                                                                 \
                                                                                                    \
    if(ext_state.test(wrongtype_flag) == true && exceptions_flags.test(wrongtype_flag) == true)     \
        throw_type_except(D, "operator>>");

//
// A is the sequence CORBA name
// B is the IDL enum method to set data
//

#define INSERT_BASIC_TYPE(A, B)                                                                               \
    failed = false;                                                                                           \
    ext_state.reset();                                                                                        \
                                                                                                              \
    if(insert_elt_array == nullptr)                                                                           \
        ext_state.set(blobdenamenotset_flag);                                                                 \
    else if(insert_ctr == -1 && insert_ind == -1)                                                             \
        ext_state.set(mixing_flag);                                                                           \
    else                                                                                                      \
    {                                                                                                         \
        size_t nb_insert = insert_elt_array->length();                                                        \
        if(nb_insert == 0 || insert_ctr > (int) nb_insert - 1)                                                \
            ext_state.set(notenoughde_flag);                                                                  \
        else                                                                                                  \
        {                                                                                                     \
            A dvsa;                                                                                           \
            dvsa.length(1);                                                                                   \
            dvsa[0] = datum;                                                                                  \
                                                                                                              \
            if(insert_ind != -1)                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ind].value.B(dvsa);                                                \
                (*insert_elt_array)[insert_ind].inner_blob_name = Tango::string_dup(SCALAR_PIPE);             \
                insert_ind = -1;                                                                              \
            }                                                                                                 \
            else                                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ctr].value.B(dvsa);                                                \
                (*insert_elt_array)[insert_ctr].inner_blob_name = Tango::string_dup(SCALAR_PIPE);             \
                insert_ctr++;                                                                                 \
            }                                                                                                 \
        }                                                                                                     \
    }                                                                                                         \
                                                                                                              \
    if(ext_state.any() == true)                                                                               \
        failed = true;                                                                                        \
                                                                                                              \
    if(ext_state.test(blobdenamenotset_flag) == true && exceptions_flags.test(blobdenamenotset_flag) == true) \
        throw_name_not_set("operator<<");                                                                     \
                                                                                                              \
    if(ext_state.test(mixing_flag) == true && exceptions_flags.test(mixing_flag) == true)                     \
        throw_mixing("operator>>");                                                                           \
                                                                                                              \
    if(ext_state.test(notenoughde_flag) == true && exceptions_flags.test(notenoughde_flag) == true)           \
        throw_too_many("operator<<", false);

//
// A is the sequence CORBA name
// B is the IDL enum method to set data
//

#define INSERT_VECTOR_TYPE(A, B)                                                                              \
    failed = false;                                                                                           \
    ext_state.reset();                                                                                        \
                                                                                                              \
    if(insert_elt_array == nullptr)                                                                           \
        ext_state.set(blobdenamenotset_flag);                                                                 \
    else if(insert_ctr == -1 && insert_ind == -1)                                                             \
        ext_state.set(mixing_flag);                                                                           \
    else                                                                                                      \
    {                                                                                                         \
        size_t nb_insert = insert_elt_array->length();                                                        \
        if(nb_insert == 0 || insert_ctr > (int) nb_insert - 1)                                                \
            ext_state.set(notenoughde_flag);                                                                  \
        else                                                                                                  \
        {                                                                                                     \
            A dvsa;                                                                                           \
            if(insert_ind != -1)                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ind].value.B(dvsa);                                                \
                A &dvsb = (*insert_elt_array)[insert_ind].value.B();                                          \
                dvsb.replace(datum.size(), datum.size(), &datum[0], false);                                   \
                (*insert_elt_array)[insert_ind].inner_blob_name = Tango::string_dup(ARRAY_PIPE);              \
                insert_ind = -1;                                                                              \
            }                                                                                                 \
            else                                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ctr].value.B(dvsa);                                                \
                A &dvsb = (*insert_elt_array)[insert_ctr].value.B();                                          \
                dvsb.replace(datum.size(), datum.size(), &datum[0], false);                                   \
                (*insert_elt_array)[insert_ctr].inner_blob_name = Tango::string_dup(ARRAY_PIPE);              \
                insert_ctr++;                                                                                 \
            }                                                                                                 \
        }                                                                                                     \
    }                                                                                                         \
                                                                                                              \
    if(ext_state.any() == true)                                                                               \
        failed = true;                                                                                        \
                                                                                                              \
    if(ext_state.test(blobdenamenotset_flag) == true && exceptions_flags.test(blobdenamenotset_flag) == true) \
        throw_name_not_set("operator<<");                                                                     \
                                                                                                              \
    if(ext_state.test(mixing_flag) == true && exceptions_flags.test(mixing_flag) == true)                     \
        throw_mixing("operator>>");                                                                           \
                                                                                                              \
    if(ext_state.test(notenoughde_flag) == true && exceptions_flags.test(notenoughde_flag) == true)           \
        throw_too_many("operator<<", false);

//
// A is the sequence CORBA name
// B is the IDL enum method to set data
//

#define INSERT_SEQ_TYPE(A, B)                                                                                 \
    failed = false;                                                                                           \
    ext_state.reset();                                                                                        \
                                                                                                              \
    if(insert_elt_array == nullptr)                                                                           \
        ext_state.set(blobdenamenotset_flag);                                                                 \
    else if(insert_ctr == -1 && insert_ind == -1)                                                             \
        ext_state.set(mixing_flag);                                                                           \
    else                                                                                                      \
    {                                                                                                         \
        size_t nb_insert = insert_elt_array->length();                                                        \
        if(nb_insert == 0 || insert_ctr > (int) nb_insert - 1)                                                \
            ext_state.set(notenoughde_flag);                                                                  \
        else                                                                                                  \
        {                                                                                                     \
            CORBA::Long max, len;                                                                             \
            max = datum.maximum();                                                                            \
            len = datum.length();                                                                             \
            A dvsa;                                                                                           \
            if(insert_ind != -1)                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ind].value.B(dvsa);                                                \
                A &dvsb = (*insert_elt_array)[insert_ind].value.B();                                          \
                dvsb.replace(max, len, datum.get_buffer(), false);                                            \
                (*insert_elt_array)[insert_ind].inner_blob_name = Tango::string_dup(ARRAY_PIPE);              \
                insert_ind = -1;                                                                              \
            }                                                                                                 \
            else                                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ctr].value.B(dvsa);                                                \
                A &dvsb = (*insert_elt_array)[insert_ctr].value.B();                                          \
                dvsb.replace(max, len, datum.get_buffer(), false);                                            \
                (*insert_elt_array)[insert_ctr].inner_blob_name = Tango::string_dup(ARRAY_PIPE);              \
                insert_ctr++;                                                                                 \
            }                                                                                                 \
        }                                                                                                     \
    }                                                                                                         \
                                                                                                              \
    if(ext_state.any() == true)                                                                               \
        failed = true;                                                                                        \
                                                                                                              \
    if(ext_state.test(blobdenamenotset_flag) == true && exceptions_flags.test(blobdenamenotset_flag) == true) \
        throw_name_not_set("operator<<");                                                                     \
                                                                                                              \
    if(ext_state.test(mixing_flag) == true && exceptions_flags.test(mixing_flag) == true)                     \
        throw_mixing("operator>>");                                                                           \
                                                                                                              \
    if(ext_state.test(notenoughde_flag) == true && exceptions_flags.test(notenoughde_flag) == true)           \
        throw_too_many("operator<<", false);

//
// A is the sequence CORBA name
// B is the IDL enum method to set data
//

#define INSERT_SEQ_PTR_TYPE(A, B)                                                                             \
    failed = false;                                                                                           \
    ext_state.reset();                                                                                        \
                                                                                                              \
    if(insert_elt_array == nullptr)                                                                           \
        ext_state.set(blobdenamenotset_flag);                                                                 \
    else if(insert_ctr == -1 && insert_ind == -1)                                                             \
        ext_state.set(mixing_flag);                                                                           \
    else                                                                                                      \
    {                                                                                                         \
        size_t nb_insert = insert_elt_array->length();                                                        \
        if(nb_insert == 0 || insert_ctr > (int) nb_insert - 1)                                                \
            ext_state.set(notenoughde_flag);                                                                  \
        else                                                                                                  \
        {                                                                                                     \
            A dvsa;                                                                                           \
            CORBA::Long max, len;                                                                             \
            max = datum->maximum();                                                                           \
            len = datum->length();                                                                            \
            bool rel = datum->release();                                                                      \
            if(rel == false)                                                                                  \
            {                                                                                                 \
                datum->replace(max, len, datum->get_buffer(), true);                                          \
            }                                                                                                 \
            if(insert_ind != -1)                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ind].value.B(dvsa);                                                \
                A &dvsb = (*insert_elt_array)[insert_ind].value.B();                                          \
                dvsb.replace(max, len, datum->get_buffer((CORBA::Boolean) true), true);                       \
                (*insert_elt_array)[insert_ind].inner_blob_name = Tango::string_dup(ARRAY_PIPE);              \
                insert_ind = -1;                                                                              \
            }                                                                                                 \
            else                                                                                              \
            {                                                                                                 \
                (*insert_elt_array)[insert_ctr].value.B(dvsa);                                                \
                A &dvsb = (*insert_elt_array)[insert_ctr].value.B();                                          \
                dvsb.replace(max, len, datum->get_buffer((CORBA::Boolean) true), true);                       \
                (*insert_elt_array)[insert_ctr].inner_blob_name = Tango::string_dup(ARRAY_PIPE);              \
                insert_ctr++;                                                                                 \
            }                                                                                                 \
                                                                                                              \
            delete datum;                                                                                     \
        }                                                                                                     \
    }                                                                                                         \
                                                                                                              \
    if(ext_state.any() == true)                                                                               \
        failed = true;                                                                                        \
                                                                                                              \
    if(ext_state.test(blobdenamenotset_flag) == true && exceptions_flags.test(blobdenamenotset_flag) == true) \
        throw_name_not_set("operator<<");                                                                     \
                                                                                                              \
    if(ext_state.test(mixing_flag) == true && exceptions_flags.test(mixing_flag) == true)                     \
        throw_mixing("operator>>");                                                                           \
                                                                                                              \
    if(ext_state.test(notenoughde_flag) == true && exceptions_flags.test(notenoughde_flag) == true)           \
        throw_too_many("operator<<", false);

} // namespace Tango

#endif /* TANGO_CLIENT_DEVICEPIPE_H */
