//===================================================================================================================
//
// devapi_pipe.cpp     - C++ source code file for TANGO devapi class DevicePipe
//
// programmer(s)     - E. Taurel
//
// original         - March 2001
//
// Copyright (C) :      2014,2015
//                        European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with Tango.
// If not, see <http://www.gnu.org/licenses/>.
//
//
//====================================================================================================================

#include <tango/tango.h>
#include <iomanip>
#include <memory>

using namespace CORBA;

namespace Tango
{

//------------------------------------------------------------------------------------------------------------------
//
// method:
//         DevicePipe::DevicePipe()
//
// description:
//        Constructor for DevicePipe class
//
//------------------------------------------------------------------------------------------------------------------

DevicePipe::DevicePipe() :
    ext(nullptr)
{
}

DevicePipe::DevicePipe(const std::string &pipe_name) :
    name(pipe_name),
    ext(nullptr)
{
}

DevicePipe::DevicePipe(const std::string &pipe_name, const std::string &root_blob_name) :
    name(pipe_name),
    ext(nullptr)
{
    the_root_blob.set_name(root_blob_name);
}

//-----------------------------------------------------------------------------
//
// DevicePipe::DevicePipe() - copy constructor to create DevicePipe
//
//-----------------------------------------------------------------------------

DevicePipe::DevicePipe(const DevicePipe &source) :
    ext(nullptr)
{
    name = source.name;
    time = source.time;
    the_root_blob = source.the_root_blob;

    if(source.ext != nullptr)
    {
        ext = std::make_unique<DevicePipeExt>();
        *(ext) = *(source.ext);
    }
}

//-----------------------------------------------------------------------------
//
// DevicePipe::operator= - assignment operator for DevicePipe class
//
//-----------------------------------------------------------------------------

DevicePipe &DevicePipe::operator=(const DevicePipe &rhs)
{
    if(this != &rhs)
    {
        name = rhs.name;
        time = rhs.time;
        the_root_blob = rhs.the_root_blob;

        if(rhs.ext != nullptr)
        {
            ext = std::make_unique<DevicePipeExt>();
            *(ext) = *(rhs.ext);
        }
    }
    return *this;
}

//-----------------------------------------------------------------------------
//
// DevicePipe::DevicePipe() - Move copy constructor to create DevicePipe
//
//-----------------------------------------------------------------------------

DevicePipe::DevicePipe(DevicePipe &&source) :
    ext(nullptr)
{
    name = std::move(source.name);
    time = source.time;
    the_root_blob = std::move(source.the_root_blob);

    if(source.ext != nullptr)
    {
        ext = std::move(source.ext);
    }
}

//-----------------------------------------------------------------------------
//
// DevicePipe::operator= - Move assignment operator for DevicePipe class
//
//-----------------------------------------------------------------------------

DevicePipe &DevicePipe::operator=(DevicePipe &&rhs)
{
    name = std::move(rhs.name);
    time = rhs.time;
    the_root_blob = std::move(rhs.the_root_blob);

    if(rhs.ext != nullptr)
    {
        ext = std::move(rhs.ext);
    }
    else
    {
        ext.reset();
    }

    return *this;
}

//----------------------------------------------------------------------------------------------------------------
//
// DevicePipe::~DevicePipe() - destructor to destroy DevicePipe
//
//----------------------------------------------------------------------------------------------------------------

DevicePipe::~DevicePipe() { }

DevicePipe &DevicePipe::operator[](const std::string &_na)
{
    the_root_blob.operator[](_na);
    return *this;
}

//*************************************************************************************************************
//
//                        DevicePipeBlob class
//
//*************************************************************************************************************

//------------------------------------------------------------------------------------------------------------------
//
// method:
//         DevicePipeBlob::DevicePipeBlob()
//
// description:
//        Constructor for DevicePipeBlob class
//
//------------------------------------------------------------------------------------------------------------------

DevicePipeBlob::DevicePipeBlob() :
    failed(false),
    insert_elt_array(nullptr),
    insert_ctr(0),
    extract_elt_array(nullptr),
    extract_ctr(0),
    extract_delete(false),
    ext(nullptr)
{
    exceptions_flags.set();
    ext_state.reset();
    insert_ind = -1;
    extract_ind = -1;
}

DevicePipeBlob::DevicePipeBlob(const std::string &blob_name) :
    name(blob_name),
    failed(false),
    insert_elt_array(nullptr),
    insert_ctr(0),
    extract_elt_array(nullptr),
    extract_ctr(0),
    extract_delete(false),
    ext(nullptr)
{
    exceptions_flags.set();
    ext_state.reset();
    insert_ind = -1;
    extract_ind = -1;
}

//----------------------------------------------------------------------------------------------------------------
//
// DevicePipeBlob::~DevicePipeBlob() - destructor to destroy DevicePipeBlob instance
//
//----------------------------------------------------------------------------------------------------------------

DevicePipeBlob::~DevicePipeBlob()
{
    if(extract_delete)
    {
        delete extract_elt_array;
    }
}

//-----------------------------------------------------------------------------
//
// DevicePipeBlob::DevicePipeBlob() - copy constructor to create DevicePipeBlob
//
//-----------------------------------------------------------------------------

DevicePipeBlob::DevicePipeBlob(const DevicePipeBlob &source) :
    ext(nullptr)
{
    name = source.name;
    exceptions_flags = source.exceptions_flags;
    ext_state = source.ext_state;
    failed = source.failed;
    insert_ind = source.insert_ind;
    extract_ind = source.extract_ind;

    if(source.insert_elt_array != nullptr)
    {
        insert_elt_array = new DevVarPipeDataEltArray();
        (*insert_elt_array) = (*source.insert_elt_array);
    }
    else
    {
        insert_elt_array = nullptr;
    }
    insert_ctr = source.insert_ctr;

    if(source.extract_elt_array != nullptr)
    {
        auto *tmp = new DevVarPipeDataEltArray();
        *tmp = (*source.extract_elt_array);
        set_extract_data(tmp);
        extract_delete = true;
    }
    else
    {
        extract_delete = source.extract_delete;
    }
    extract_ctr = source.extract_ctr;

    if(source.ext != nullptr)
    {
        ext = std::make_unique<DevicePipeBlobExt>();
        *(ext) = *(source.ext);
    }
}

//-----------------------------------------------------------------------------
//
// DevicePipeBlob::operator= - assignment operator for DevicePipeBlob class
//
//-----------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator=(const DevicePipeBlob &rhs)
{
    if(this != &rhs)
    {
        name = rhs.name;
        exceptions_flags = rhs.exceptions_flags;
        ext_state = rhs.ext_state;
        failed = rhs.failed;
        insert_ind = rhs.insert_ind;
        extract_ind = rhs.extract_ind;

        if(rhs.insert_elt_array != nullptr)
        {
            insert_elt_array = new DevVarPipeDataEltArray();
            (*insert_elt_array) = (*rhs.insert_elt_array);
        }
        else
        {
            insert_elt_array = nullptr;
        }
        insert_ctr = rhs.insert_ctr;

        if(rhs.extract_elt_array != nullptr)
        {
            auto *tmp = new DevVarPipeDataEltArray();
            *tmp = (*rhs.extract_elt_array);
            set_extract_data(tmp);
            extract_delete = true;
        }
        else
        {
            extract_delete = rhs.extract_delete;
        }
        extract_ctr = rhs.extract_ctr;

        if(rhs.ext != nullptr)
        {
            ext = std::make_unique<DevicePipeBlobExt>();
            *(ext) = *(rhs.ext);
        }
        else
        {
            ext.reset();
        }
    }
    return *this;
}

//-----------------------------------------------------------------------------
//
// DevicePipeBlob::DevicePipeBlob() - Move copy constructor to create DevicePipeBlob
//
//-----------------------------------------------------------------------------

DevicePipeBlob::DevicePipeBlob(DevicePipeBlob &&source) :
    ext(nullptr)
{
    name = std::move(source.name);
    exceptions_flags = source.exceptions_flags;
    ext_state = source.ext_state;
    failed = source.failed;
    insert_ind = source.insert_ind;
    extract_ind = source.extract_ind;

    if(source.insert_elt_array != nullptr)
    {
        insert_elt_array = new DevVarPipeDataEltArray();
        (*insert_elt_array) = (*source.insert_elt_array);
    }
    else
    {
        insert_elt_array = nullptr;
    }
    insert_ctr = source.insert_ctr;

    if(source.extract_elt_array != nullptr)
    {
        auto *tmp = new DevVarPipeDataEltArray();
        *tmp = (*source.extract_elt_array);
        set_extract_data(tmp);
        extract_delete = true;
    }
    else
    {
        extract_delete = source.extract_delete;
    }
    extract_ctr = source.extract_ctr;

    if(source.ext != nullptr)
    {
        ext = std::move(source.ext);
    }
}

//-----------------------------------------------------------------------------
//
// DevicePipeBlob::operator= - Move assignment operator for DevicePipeBlob class
//
//-----------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator=(DevicePipeBlob &&rhs)
{
    name = std::move(rhs.name);
    exceptions_flags = rhs.exceptions_flags;
    ext_state = rhs.ext_state;
    failed = rhs.failed;
    insert_ind = rhs.insert_ind;
    extract_ind = rhs.extract_ind;

    if(rhs.insert_elt_array != nullptr)
    {
        insert_elt_array = rhs.insert_elt_array;
    }
    rhs.insert_elt_array = nullptr;
    insert_ctr = rhs.insert_ctr;

    if(extract_delete)
    {
        delete extract_elt_array;
    }

    if(rhs.extract_elt_array != nullptr)
    {
        extract_elt_array = rhs.extract_elt_array;
    }
    rhs.extract_elt_array = nullptr;
    extract_delete = rhs.extract_delete;
    extract_ctr = rhs.extract_ctr;

    if(rhs.ext != nullptr)
    {
        ext = std::move(rhs.ext);
    }
    else
    {
        ext.reset();
    }

    return *this;
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::get_data_elt_name
//
// description :
//        Get all data element name
//
// return:
//        vector with the data elt name
//
//-------------------------------------------------------------------------------------------------------------------

std::vector<std::string> DevicePipeBlob::get_data_elt_names()
{
    if(extract_elt_array == nullptr)
    {
        std::stringstream ss;

        ss << "You try to get data element name(s) for a blob which has never been received from a device";
        TANGO_THROW_EXCEPTION(API_PipeWrongArg, ss.str());
    }

    std::vector<std::string> v_str;
    size_t nb_elt = extract_elt_array->length();

    for(size_t loop = 0; loop < nb_elt; loop++)
    {
        v_str.emplace_back((*extract_elt_array)[loop].name.in());
    }

    return v_str;
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::get_data_elt_name
//
// description :
//        Get data element name for a single data element
//
// argument :
//        in:
//            - _ind : Data element index in the blob
//
// return:
//        The data elt name
//
//-------------------------------------------------------------------------------------------------------------------

std::string DevicePipeBlob::get_data_elt_name(size_t _ind)
{
    if(extract_elt_array == nullptr)
    {
        std::stringstream ss;

        ss << "You try to get data element name(s) for a blob which has never been received from a device";
        TANGO_THROW_EXCEPTION(API_PipeWrongArg, ss.str());
    }

    if(_ind > extract_elt_array->length())
    {
        std::stringstream ss;

        ss << "Given index (" << _ind << ") is above the number of data element in the data blob (" << get_data_elt_nb()
           << ")";
        TANGO_THROW_EXCEPTION(API_PipeWrongArg, ss.str());
    }
    std::string tmp((*extract_elt_array)[_ind].name.in());
    return tmp;
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::get_data_elt_type
//
// description :
//        Get data element name for a single data element
//
// argument :
//        in:
//            - _ind : Data element index in the blob
//
// return:
//        The data elt name
//
//-------------------------------------------------------------------------------------------------------------------

int DevicePipeBlob::get_data_elt_type(size_t _ind)
{
    int ret = 0;

    if(extract_elt_array == nullptr)
    {
        std::stringstream ss;

        ss << "You try to get data element name(s) for a blob which has never been received from a device";
        TANGO_THROW_EXCEPTION(API_PipeWrongArg, ss.str());
    }

    if(_ind > extract_elt_array->length())
    {
        std::stringstream ss;

        ss << "Given index (" << _ind << ") is above the number of data element in the data blob (" << get_data_elt_nb()
           << ")";
        TANGO_THROW_EXCEPTION(API_PipeWrongArg, ss.str());
    }

    //
    // Do we have a Blob at this index?
    //

    if((*extract_elt_array)[_ind].inner_blob.length() != 0)
    {
        ret = DEV_PIPE_BLOB;
    }
    else
    {
        std::string d_type = (*extract_elt_array)[_ind].inner_blob_name.in();
        switch((*extract_elt_array)[_ind].value._d())
        {
        case ATT_BOOL:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_BOOLEAN;
            }
            else
            {
                ret = DEVVAR_BOOLEANARRAY;
            }
        }
        break;

        case ATT_SHORT:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_SHORT;
            }
            else
            {
                ret = DEVVAR_SHORTARRAY;
            }
        }
        break;

        case ATT_LONG:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_LONG;
            }
            else
            {
                ret = DEVVAR_LONGARRAY;
            }
        }
        break;

        case ATT_LONG64:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_LONG64;
            }
            else
            {
                ret = DEVVAR_LONG64ARRAY;
            }
        }
        break;

        case ATT_FLOAT:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_FLOAT;
            }
            else
            {
                ret = DEVVAR_FLOATARRAY;
            }
        }
        break;

        case ATT_DOUBLE:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_DOUBLE;
            }
            else
            {
                ret = DEVVAR_DOUBLEARRAY;
            }
        }
        break;

        case ATT_UCHAR:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_UCHAR;
            }
            else
            {
                ret = DEVVAR_CHARARRAY;
            }
        }
        break;

        case ATT_USHORT:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_USHORT;
            }
            else
            {
                ret = DEVVAR_USHORTARRAY;
            }
        }
        break;

        case ATT_ULONG:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_ULONG;
            }
            else
            {
                ret = DEVVAR_ULONGARRAY;
            }
        }
        break;

        case ATT_ULONG64:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_ULONG64;
            }
            else
            {
                ret = DEVVAR_ULONG64ARRAY;
            }
        }
        break;

        case ATT_STRING:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_STRING;
            }
            else
            {
                ret = DEVVAR_STRINGARRAY;
            }
        }
        break;

        case ATT_STATE:
        {
            if(d_type == SCALAR_PIPE)
            {
                ret = DEV_STATE;
            }
            else
            {
                ret = DEVVAR_STATEARRAY;
            }
        }
        break;

        case ATT_ENCODED:
        {
            ret = DEV_ENCODED;
        }
        break;

        case DEVICE_STATE:
            [[fallthrough]];
        case ATT_NO_DATA:
            TANGO_THROW_EXCEPTION(API_PipeWrongArg,
                                  "Unsupported data type in data element! (ATT_NO_DATA, DEVICE_STATE)");
            break;

        default:
            TANGO_ASSERT_ON_DEFAULT((*extract_elt_array)[_ind].value._d());
        }
    }

    return ret;
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::get_extract_ind_from_name
//
// description :
//        Get the index of a data element in the extract blob from its name. Throw exception if not found
//
// argument :
//        in:
//            - _na : Data element name
//
// return:
//        The data elt index.
//
//-------------------------------------------------------------------------------------------------------------------

size_t DevicePipeBlob::get_extract_ind_from_name(const std::string &_na)
{
    std::string lower_name(_na);
    std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower);

    bool found = false;
    size_t loop;

    if(extract_elt_array == nullptr)
    {
        TANGO_THROW_EXCEPTION(API_PipeNoDataElement, "No data element available for extraction");
    }

    if(extract_ctr > 0)
    {
        TANGO_THROW_EXCEPTION(API_NotSupportedFeature,
                              "Not supported to mix extraction type (operator >> and operator [])");
    }

    for(loop = 0; loop < extract_elt_array->length(); loop++)
    {
        std::string tmp((*extract_elt_array)[loop].name.in());
        std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);

        if(tmp == lower_name)
        {
            found = true;
            break;
        }
    }

    if(!found)
    {
        std::stringstream ss;

        ss << "Can't get data element with name " << _na;
        TANGO_THROW_EXCEPTION(API_PipeWrongArg, ss.str());
    }

    extract_ctr = -1;
    return loop;
}

size_t DevicePipeBlob::get_insert_ind_from_name(const std::string &_na)
{
    std::string lower_name(_na);
    std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower);

    bool found = false;
    size_t loop;

    if(insert_elt_array == nullptr)
    {
        TANGO_THROW_EXCEPTION(API_PipeNoDataElement, "No data element available for insertion");
    }

    if(insert_ctr > 0)
    {
        TANGO_THROW_EXCEPTION(API_NotSupportedFeature,
                              "Not supported to mix insertion type (operator << and operator [])");
    }

    for(loop = 0; loop < insert_elt_array->length(); loop++)
    {
        std::string tmp((*insert_elt_array)[loop].name.in());
        std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);

        if(tmp == lower_name)
        {
            found = true;
            break;
        }
    }

    if(!found)
    {
        std::stringstream ss;

        ss << "Can't get data element with name " << _na;
        TANGO_THROW_EXCEPTION(API_PipeWrongArg, ss.str());
    }

    insert_ctr = -1;
    return loop;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::operator[]
//
// description :
//        Return the object itself with the extract counter set to the ind of the data element with name given as
//        parameter. This allows user to write code like
//            pipe_blob["MyDataElt"] >> sh;
//
// argument :
//        in:
//            - _na : Data element name
//
// return:
//        The DevicePipeBlob object itself
//
//-------------------------------------------------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator[](const std::string &_na)
{
    int ind;
    try
    {
        ind = get_extract_ind_from_name(_na);
        extract_ind = ind;
    }
    catch(Tango::DevFailed &e)
    {
        std::string reason(e.errors[0].reason.in());
        if(reason != API_PipeNoDataElement)
        {
            throw;
        }
    }

    try
    {
        ind = get_insert_ind_from_name(_na);
        insert_ind = ind;
    }
    catch(Tango::DevFailed &e)
    {
        std::string reason(e.errors[0].reason.in());
        if(reason != API_PipeNoDataElement)
        {
            throw;
        }
    }

    return *this;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::set_data_elt_names
//
// description :
//        Set the blob data element name(s)
//
// argument :
//        in:
//            - elt_names : Vector with data elements name
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::set_data_elt_names(const std::vector<std::string> &elt_names)
{
    //
    // Check that we do not have two times the same DE name (case independant)
    //

    if(elt_names.size() > 1)
    {
        unsigned int i;
        std::vector<std::string> same_de = elt_names;

        for(i = 0; i < same_de.size(); ++i)
        {
            std::transform(same_de[i].begin(), same_de[i].end(), same_de[i].begin(), ::tolower);
        }
        sort(same_de.begin(), same_de.end());
        std::vector<std::string> same_de_lower = same_de;

        auto pos = unique(same_de.begin(), same_de.end());

        int duplicate_de;
        duplicate_de = distance(elt_names.begin(), elt_names.end()) - distance(same_de.begin(), pos);

        if(duplicate_de != 0)
        {
            std::stringstream desc;
            desc << "Several times the same data element name in provided vector: ";
            int ctr = 0;
            for(i = 0; i < same_de_lower.size() - 1; i++)
            {
                if(same_de_lower[i] == same_de_lower[i + 1])
                {
                    ctr++;
                    desc << same_de_lower[i];
                    if(ctr < duplicate_de)
                    {
                        desc << ", ";
                    }
                }
            }
            TANGO_THROW_DETAILED_EXCEPTION(ApiConnExcept, API_PipeDuplicateDEName, desc.str());
        }
    }

    //
    // Init insert elt array and set names
    //

    insert_elt_array = new DevVarPipeDataEltArray();
    insert_elt_array->length(elt_names.size());

    for(size_t loop = 0; loop < elt_names.size(); loop++)
    {
        (*insert_elt_array)[loop].name = Tango::string_dup(elt_names[loop].c_str());
        (*insert_elt_array)[loop].value.union_no_data(true);
        (*insert_elt_array)[loop].inner_blob.length(0);
    }

    insert_ctr = 0;
    extract_ctr = 0;
    insert_ind = -1;
    extract_ind = -1;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::set_data_elt_nb
//
// description :
//        Set the blob data element number
//
// argument :
//        in:
//            - _nb : The data elements number
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::set_data_elt_nb(size_t _nb)
{
    insert_elt_array = new DevVarPipeDataEltArray();
    insert_elt_array->length(_nb);

    for(size_t loop = 0; loop < _nb; loop++)
    {
        (*insert_elt_array)[loop].value.union_no_data(true);
        (*insert_elt_array)[loop].inner_blob.length(0);
    }

    insert_ctr = 0;
    extract_ctr = 0;
    insert_ind = -1;
    extract_ind = -1;
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::set_current_delt_name
//
// description :
//        Set the data element name. This method create the pipe data elt array (insert) if not already done
//
// argument :
//        in:
//            - _na : The data elements name
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::set_current_delt_name(const std::string &_na)
{
    if(insert_elt_array == nullptr)
    {
        insert_elt_array = new DevVarPipeDataEltArray(10);
        insert_elt_array->length(1);

        (*insert_elt_array)[0].value.union_no_data(true);
        (*insert_elt_array)[0].inner_blob.length(0);

        insert_ctr = 0;
        extract_ctr = 0;
        insert_ind = -1;
        extract_ind = -1;
    }
    else if(insert_ctr == (int) insert_elt_array->length())
    {
        insert_elt_array->length(insert_elt_array->length() + 1);

        (*insert_elt_array)[insert_ctr].value.union_no_data(true);
        (*insert_elt_array)[insert_ctr].inner_blob.length(0);
    }

    (*insert_elt_array)[insert_ctr].name = Tango::string_dup(_na.c_str());
}

//------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::get_data_elt_nb()
//
// description :
//        Get the data element number in the blob
//
// return :
//        The data element number
//
//-------------------------------------------------------------------------------------------------------------------

size_t DevicePipeBlob::get_data_elt_nb()
{
    size_t ret;

    if(extract_elt_array == nullptr)
    {
        if(insert_elt_array == nullptr)
        {
            ret = 0;
        }
        else
        {
            ret = insert_elt_array->length();
        }
    }
    else
    {
        ret = extract_elt_array->length();
    }

    return ret;
}

//******************************************************************************************************************

DevicePipeBlob &DevicePipeBlob::operator<<(const DevBoolean &datum)
{
    INSERT_BASIC_TYPE(DevVarBooleanArray, bool_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const short &datum)
{
    INSERT_BASIC_TYPE(DevVarShortArray, short_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevLong &datum)
{
    INSERT_BASIC_TYPE(DevVarLongArray, long_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevLong64 &datum)
{
    INSERT_BASIC_TYPE(DevVarLong64Array, long64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const float &datum)
{
    INSERT_BASIC_TYPE(DevVarFloatArray, float_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const double &datum)
{
    INSERT_BASIC_TYPE(DevVarDoubleArray, double_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevUChar &datum)
{
    INSERT_BASIC_TYPE(DevVarUCharArray, uchar_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevUShort &datum)
{
    INSERT_BASIC_TYPE(DevVarUShortArray, ushort_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevULong &datum)
{
    INSERT_BASIC_TYPE(DevVarULongArray, ulong_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevULong64 &datum)
{
    INSERT_BASIC_TYPE(DevVarULong64Array, ulong64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevString &datum)
{
    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
        {
            DevVarStringArray dvsa;
            dvsa.length(1);
            dvsa[0] = Tango::string_dup(datum);

            auto update_idx = [this]()
            {
                int ret{insert_ctr};
                if(insert_ind != -1)
                {
                    ret = insert_ind;
                    insert_ind = -1;
                }
                else
                {
                    insert_ctr++;
                }
                return ret;
            };

            auto idx = update_idx();
            (*insert_elt_array)[idx].value.string_att_value(dvsa);
            (*insert_elt_array)[idx].inner_blob_name = Tango::string_dup("Scalar");
        }
    }

    if(ext_state.any())
    {
        failed = true;
    }

    if(ext_state.test(blobdenamenotset_flag) && exceptions_flags.test(blobdenamenotset_flag))
    {
        throw_name_not_set("operator<<");
    }

    if(ext_state.test(notenoughde_flag) && exceptions_flags.test(notenoughde_flag))
    {
        throw_too_many("operator<<", false);
    }

    if(ext_state.test(mixing_flag) && exceptions_flags.test(mixing_flag))
    {
        throw_mixing("operator>>");
    }

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevState &datum)
{
    INSERT_BASIC_TYPE(DevVarStateArray, state_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevEncoded &datum)
{
    INSERT_BASIC_TYPE(DevVarEncodedArray, encoded_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const std::string &datum)
{
    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
        {
            DevVarStringArray dvsa;
            dvsa.length(1);
            dvsa[0] = Tango::string_dup(datum.c_str());

            if(insert_ind != -1)
            {
                (*insert_elt_array)[insert_ind].value.string_att_value(dvsa);
                (*insert_elt_array)[insert_ind].inner_blob_name = Tango::string_dup("Scalar");
                insert_ind = -1;
            }
            else
            {
                (*insert_elt_array)[insert_ctr].value.string_att_value(dvsa);
                (*insert_elt_array)[insert_ctr].inner_blob_name = Tango::string_dup("Scalar");
                insert_ctr++;
            }
        }
    }

    if(ext_state.any())
    {
        failed = true;
    }

    if(ext_state.test(blobdenamenotset_flag) && exceptions_flags.test(blobdenamenotset_flag))
    {
        throw_name_not_set("operator<<");
    }

    if(ext_state.test(notenoughde_flag) && exceptions_flags.test(notenoughde_flag))
    {
        throw_too_many("operator<<", false);
    }

    if(ext_state.test(mixing_flag) && exceptions_flags.test(mixing_flag))
    {
        throw_mixing("operator>>");
    }

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const DevicePipeBlob &datum)
{
    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
        {
            DevVarPipeDataEltArray *tmp_ptr = datum.get_insert_data();
            if(tmp_ptr != nullptr)
            {
                CORBA::ULong max, len;
                max = tmp_ptr->maximum();
                len = tmp_ptr->length();

                if(insert_ind != -1)
                {
                    (*insert_elt_array)[insert_ind].inner_blob.replace(
                        max, len, tmp_ptr->get_buffer((CORBA::Boolean) true), true);
                    (*insert_elt_array)[insert_ind].inner_blob_name = Tango::string_dup(datum.get_name().c_str());
                    insert_ind = -1;
                }
                else
                {
                    (*insert_elt_array)[insert_ctr].inner_blob.replace(
                        max, len, tmp_ptr->get_buffer((CORBA::Boolean) true), true);
                    (*insert_elt_array)[insert_ctr].inner_blob_name = Tango::string_dup(datum.get_name().c_str());
                    insert_ctr++;
                }

                delete tmp_ptr;
            }
        }
    }

    if(ext_state.any())
    {
        failed = true;
    }

    if(ext_state.test(blobdenamenotset_flag) && exceptions_flags.test(blobdenamenotset_flag))
    {
        throw_name_not_set("operator<<");
    }

    if(ext_state.test(notenoughde_flag) && exceptions_flags.test(notenoughde_flag))
    {
        throw_too_many("operator<<", false);
    }

    if(ext_state.test(mixing_flag) && exceptions_flags.test(mixing_flag))
    {
        throw_mixing("operator>>");
    }

    return *this;
}

//---------------------------------------------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator<<(const std::vector<DevBoolean> &datum)
{
    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
        {
            DevVarBooleanArray dvsa;
            if(insert_ind != -1)
            {
                (*insert_elt_array)[insert_ind].value.bool_att_value(dvsa);
                DevVarBooleanArray &dvsb = (*insert_elt_array)[insert_ind].value.bool_att_value();
                dvsb << datum;
                insert_ind = -1;
            }
            else
            {
                (*insert_elt_array)[insert_ctr].value.bool_att_value(dvsa);
                DevVarBooleanArray &dvsb = (*insert_elt_array)[insert_ctr].value.bool_att_value();
                dvsb << datum;
                insert_ctr++;
            }
        }
    }

    if(ext_state.any())
    {
        failed = true;
    }

    if(ext_state.test(blobdenamenotset_flag) && exceptions_flags.test(blobdenamenotset_flag))
    {
        throw_name_not_set("operator<<");
    }

    if(ext_state.test(mixing_flag) && exceptions_flags.test(mixing_flag))
    {
        throw_mixing("operator>>");
    }

    if(ext_state.test(notenoughde_flag) && exceptions_flags.test(notenoughde_flag))
    {
        throw_too_many("operator<<", false);
    }

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevShort> &datum)
{
    INSERT_VECTOR_TYPE(DevVarShortArray, short_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevLong> &datum)
{
    INSERT_VECTOR_TYPE(DevVarLongArray, long_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevLong64> &datum)
{
    INSERT_VECTOR_TYPE(DevVarLong64Array, long64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<float> &datum)
{
    INSERT_VECTOR_TYPE(DevVarFloatArray, float_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<double> &datum)
{
    INSERT_VECTOR_TYPE(DevVarDoubleArray, double_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevUChar> &datum)
{
    INSERT_VECTOR_TYPE(DevVarUCharArray, uchar_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevUShort> &datum)
{
    INSERT_VECTOR_TYPE(DevVarUShortArray, ushort_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevULong> &datum)
{
    INSERT_VECTOR_TYPE(DevVarULongArray, ulong_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevULong64> &datum)
{
    INSERT_VECTOR_TYPE(DevVarULong64Array, ulong64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevString> &datum)
{
    INSERT_VECTOR_TYPE(DevVarStringArray, string_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevState> &datum)
{
    INSERT_VECTOR_TYPE(DevVarStateArray, state_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(std::vector<DevEncoded> &datum)
{
    INSERT_VECTOR_TYPE(DevVarEncodedArray, encoded_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(const std::vector<std::string> &datum)
{
    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
        {
            size_t nb = datum.size();
            char **strvec = DevVarStringArray::allocbuf(nb);
            for(size_t i = 0; i < nb; i++)
            {
                strvec[i] = Tango::string_dup(datum[i].c_str());
            }
            DevVarStringArray dvsa;

            if(insert_ind != -1)
            {
                (*insert_elt_array)[insert_ind].value.string_att_value(dvsa);
                DevVarStringArray &dvsb = (*insert_elt_array)[insert_ind].value.string_att_value();
                dvsb.replace(datum.size(), datum.size(), strvec, true);
                insert_ind = -1;
            }
            else
            {
                (*insert_elt_array)[insert_ctr].value.string_att_value(dvsa);
                DevVarStringArray &dvsb = (*insert_elt_array)[insert_ctr].value.string_att_value();
                dvsb.replace(datum.size(), datum.size(), strvec, true);
                insert_ctr++;
            }
        }
    }

    if(ext_state.any())
    {
        failed = true;
    }

    if(ext_state.test(blobdenamenotset_flag) && exceptions_flags.test(blobdenamenotset_flag))
    {
        throw_name_not_set("operator<<");
    }

    if(ext_state.test(notenoughde_flag) && exceptions_flags.test(notenoughde_flag))
    {
        throw_too_many("operator<<", false);
    }

    if(ext_state.test(mixing_flag) && exceptions_flags.test(mixing_flag))
    {
        throw_mixing("operator>>");
    }

    return *this;
}

//---------------------------------------------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarBooleanArray &datum)
{
    INSERT_SEQ_TYPE(DevVarBooleanArray, bool_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarShortArray &datum)
{
    INSERT_SEQ_TYPE(DevVarShortArray, short_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarLongArray &datum)
{
    INSERT_SEQ_TYPE(DevVarLongArray, long_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarLong64Array &datum)
{
    INSERT_SEQ_TYPE(DevVarLong64Array, long64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarFloatArray &datum)
{
    INSERT_SEQ_TYPE(DevVarFloatArray, float_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarDoubleArray &datum)
{
    INSERT_SEQ_TYPE(DevVarDoubleArray, double_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarUCharArray &datum)
{
    INSERT_SEQ_TYPE(DevVarUCharArray, uchar_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarUShortArray &datum)
{
    INSERT_SEQ_TYPE(DevVarUShortArray, ushort_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarULongArray &datum)
{
    INSERT_SEQ_TYPE(DevVarULongArray, ulong_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarULong64Array &datum)
{
    INSERT_SEQ_TYPE(DevVarULong64Array, ulong64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarStringArray &datum)
{
    INSERT_SEQ_TYPE(DevVarStringArray, string_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarStateArray &datum)
{
    INSERT_SEQ_TYPE(DevVarStateArray, state_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarEncodedArray &datum)
{
    INSERT_SEQ_TYPE(DevVarEncodedArray, encoded_att_value)

    return *this;
}

//---------------------------------------------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarBooleanArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarBooleanArray, bool_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarShortArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarShortArray, short_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarLongArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarLongArray, long_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarLong64Array *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarLong64Array, long64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarFloatArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarFloatArray, float_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarDoubleArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarDoubleArray, double_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarUCharArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarUCharArray, uchar_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarUShortArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarUShortArray, ushort_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarULongArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarULongArray, ulong_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarULong64Array *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarULong64Array, ulong64_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarStringArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarStringArray, string_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarStateArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarStateArray, state_att_value)

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator<<(DevVarEncodedArray *datum)
{
    INSERT_SEQ_PTR_TYPE(DevVarEncodedArray, encoded_att_value)

    return *this;
}

//******************************************************************************************************************

DevicePipeBlob &DevicePipeBlob::operator>>(DevBoolean &datum)
{
    EXTRACT_BASIC_TYPE(ATT_BOOL, bool_att_value, "DevBoolean")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(short &datum)
{
    EXTRACT_BASIC_TYPE(ATT_SHORT, short_att_value, "DevShort")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevLong &datum)
{
    EXTRACT_BASIC_TYPE(ATT_LONG, long_att_value, "DevLong")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevLong64 &datum)
{
    EXTRACT_BASIC_TYPE(ATT_LONG64, long64_att_value, "DevLong64")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(float &datum)
{
    EXTRACT_BASIC_TYPE(ATT_FLOAT, float_att_value, "DevFloat")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(double &datum)
{
    EXTRACT_BASIC_TYPE(ATT_DOUBLE, double_att_value, "DevDouble")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevUChar &datum)
{
    EXTRACT_BASIC_TYPE(ATT_UCHAR, uchar_att_value, "DevUChar")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevUShort &datum)
{
    EXTRACT_BASIC_TYPE(ATT_USHORT, ushort_att_value, "DevUShort")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevULong &datum)
{
    EXTRACT_BASIC_TYPE(ATT_ULONG, ulong_att_value, "DevULong")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevULong64 &datum)
{
    EXTRACT_BASIC_TYPE(ATT_ULONG64, ulong64_att_value, "DevULong64")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevString &datum)
{
    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 != ATT_STRING)
        {
            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
        {
            datum = Tango::string_dup((uni_ptr->string_att_value())[0].in());
            if(extract_ind != -1)
            {
                extract_ind = -1;
            }
            else
            {
                extract_ctr++;
            }
        }
    }

    if(ext_state.any())
    {
        failed = true;
    }

    if(ext_state.test(isempty_flag) && exceptions_flags.test(isempty_flag))
    {
        throw_is_empty("operator>>");
    }

    if(ext_state.test(notenoughde_flag) && exceptions_flags.test(notenoughde_flag))
    {
        throw_too_many("operator>>", true);
    }

    if(ext_state.test(mixing_flag) && exceptions_flags.test(mixing_flag))
    {
        throw_mixing("operator>>");
    }

    if(ext_state.test(wrongtype_flag) && exceptions_flags.test(wrongtype_flag))
    {
        throw_type_except("DevString", "operator>>");
    }

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevState &datum)
{
    EXTRACT_BASIC_TYPE(ATT_STATE, state_att_value, "DevState")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevEncoded &datum)
{
    EXTRACT_BASIC_TYPE(ATT_ENCODED, encoded_att_value, "DevEncoded")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::string &datum)
{
    EXTRACT_BASIC_TYPE(ATT_STRING, string_att_value, "DevString")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevicePipeBlob &datum)
{
    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;
        }

        if((*extract_elt_array)[ind].inner_blob.length() == 0)
        {
            if((*extract_elt_array)[ind].value._d() == ATT_NO_DATA)
            {
                ext_state.set(isempty_flag);
            }
            else
            {
                ext_state.set(wrongtype_flag);
            }
        }
        else
        {
            datum.set_extract_data(&((*extract_elt_array)[ind].inner_blob));
            std::string tmp((*extract_elt_array)[ind].inner_blob_name.in());
            datum.set_name(tmp);
            datum.reset_extract_ctr();

            if(extract_ind != -1)
            {
                extract_ind = -1;
            }
            else
            {
                extract_ctr++;
            }
        }
    }

    if(ext_state.any())
    {
        failed = true;
    }

    if(ext_state.test(isempty_flag) && exceptions_flags.test(isempty_flag))
    {
        throw_is_empty("operator>>");
    }

    if(ext_state.test(notenoughde_flag) && exceptions_flags.test(notenoughde_flag))
    {
        throw_too_many("operator>>", true);
    }

    if(ext_state.test(mixing_flag) && exceptions_flags.test(mixing_flag))
    {
        throw_mixing("operator>>");
    }

    if(ext_state.test(wrongtype_flag) && exceptions_flags.test(wrongtype_flag))
    {
        throw_type_except("DevicePipeBlob", "operator>>");
    }

    return *this;
}

//----------------------------------------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevBoolean> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_BOOL, bool_att_value, DevVarBooleanArray, "DevBoolean")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<short> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_SHORT, short_att_value, DevVarShortArray, "DevShort")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevLong> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_LONG, long_att_value, DevVarLongArray, "DevLong")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevLong64> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_LONG64, long64_att_value, DevVarLong64Array, "DevLong64")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<float> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_FLOAT, float_att_value, DevVarFloatArray, "DevFloat")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<double> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_DOUBLE, double_att_value, DevVarDoubleArray, "DevDouble")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevUChar> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_UCHAR, uchar_att_value, DevVarUCharArray, "DevUChar")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevUShort> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_USHORT, ushort_att_value, DevVarUShortArray, "DevUShort")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevULong> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_ULONG, ulong_att_value, DevVarULongArray, "DevULong")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevULong64> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_ULONG64, ulong64_att_value, DevVarULong64Array, "DevULong64")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<std::string> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_STRING, string_att_value, DevVarStringArray, "DevString")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(std::vector<DevState> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_STATE, state_att_value, DevVarStateArray, "DevState")

    return *this;
}

/*DevicePipeBlob &DevicePipeBlob::operator >> (std::vector<DevEncoded> &datum)
{
    EXTRACT_VECTOR_TYPE(ATT_ENCODED,encoded_att_value,DevVarEncodedArray,"DevEncoded")

    return *this;
}*/

//----------------------------------------------------------------------------------------------------------

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarBooleanArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_BOOL, bool_att_value, DevVarBooleanArray, "DevBoolean")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarShortArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_SHORT, short_att_value, DevVarShortArray, "DevShort")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarLongArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_LONG, long_att_value, DevVarLongArray, "DevLong")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarLong64Array *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_LONG64, long64_att_value, DevVarLong64Array, "DevLong64")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarFloatArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_FLOAT, float_att_value, DevVarFloatArray, "DevFloat")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarDoubleArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_DOUBLE, double_att_value, DevVarDoubleArray, "DevDouble")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarUCharArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_UCHAR, uchar_att_value, DevVarUCharArray, "DevUChar")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarUShortArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_USHORT, ushort_att_value, DevVarUShortArray, "DevUShort")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarULongArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_ULONG, ulong_att_value, DevVarULongArray, "DevULong")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarULong64Array *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_ULONG64, ulong64_att_value, DevVarULong64Array, "DevULong64")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarStringArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_STRING, string_att_value, DevVarStringArray, "DevString")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarStateArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_STATE, state_att_value, DevVarStateArray, "DevState")

    return *this;
}

DevicePipeBlob &DevicePipeBlob::operator>>(DevVarEncodedArray *datum)
{
    EXTRACT_SEQ_PTR_TYPE(ATT_ENCODED, encoded_att_value, DevVarEncodedArray, "DevEncoded")

    return *this;
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::has_failed
//
// description :
//        Throw exception when wrong data type is used
//
// return:
//        True if previous insertion/extraction has failed for one reason or another
//
//-------------------------------------------------------------------------------------------------------------------

bool DevicePipeBlob::has_failed()
{
    if(failed)
    {
        delete insert_elt_array;
        if(extract_delete)
        {
            delete extract_elt_array;
            extract_delete = false;
        }
    }
    return failed;
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::throw_type_except
//
// description :
//        Throw exception when wrong data type is used
//
// argument:
//        in :
//            - _ty: data type name
//            - _meth : Caller method name
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::throw_type_except(const std::string &_ty, const std::string &_meth)
{
    delete insert_elt_array;
    if(extract_delete)
    {
        delete extract_elt_array;
        extract_delete = false;
    }

    std::stringstream ss;

    ss << "Can't get data element " << extract_ctr << " (numbering starting from 0) into a " << _ty << " data type";
    std::string m_name("DevicePipeBlob::");
    m_name = m_name + _meth;
    TANGO_THROW_DETAILED_EXCEPTION(ApiDataExcept, API_IncompatibleArgumentType, ss.str());
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::throw_too_many
//
// description :
//        Throw exception when user try to insert/extract too many data from the blob
//
// argument:
//        in :
//            - _meth : Caller method name
//            - _extract : Flag set to true if this method is called during extraction. Otherwise, false
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::throw_too_many(const std::string &_meth, bool _extract)
{
    std::stringstream ss;
    ss << "Not enough data element in blob (";
    if(_extract)
    {
        ss << get_data_elt_nb();
    }
    else
    {
        ss << insert_elt_array->length();
    }
    ss << " data elt in created/received blob)";
    std::string m_name("DevicePipeBlob::");
    m_name = m_name + _meth;

    delete insert_elt_array;
    insert_elt_array = nullptr;
    if(extract_delete)
    {
        delete extract_elt_array;
        extract_delete = false;
    }

    TANGO_THROW_DETAILED_EXCEPTION(ApiDataExcept, API_PipeWrongArg, ss.str());
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::throw_is_empty
//
// description :
//        Throw exception when user try to insert/extract data from one empty data element
//
// argument:
//        in :
//            - _meth : Caller method name
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::throw_is_empty(const std::string &_meth)
{
    delete insert_elt_array;
    if(extract_delete)
    {
        delete extract_elt_array;
        extract_delete = false;
    }

    std::string m_name("DevicePipeBlob::");
    m_name = m_name + _meth;
    TANGO_THROW_DETAILED_EXCEPTION(ApiDataExcept, API_EmptyDataElement, "The data element is empty");
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::throw_name_not_set
//
// description :
//        Throw exception when user try to insert data into a device blob while the DE name/nb not set
//
// argument:
//        in :
//            - _meth : Caller method name
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::throw_name_not_set(const std::string &_meth)
{
    delete insert_elt_array;
    if(extract_delete)
    {
        delete extract_elt_array;
        extract_delete = false;
    }

    std::string m_name("DevicePipeBlob::");
    m_name = m_name + _meth;
    TANGO_THROW_DETAILED_EXCEPTION(
        ApiDataExcept, API_PipeNoDataElement, "The blob data element number (or name) not set");
}

//+------------------------------------------------------------------------------------------------------------------
//
// method :
//        DevicePipeBlob::throw_mixing
//
// description :
//        Throw exception when user try to mix [] and >> (<<) insertion/extraction method
//
// argument:
//        in :
//            - _meth : Caller method name
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::throw_mixing(const std::string &_meth)
{
    delete insert_elt_array;
    if(extract_delete)
    {
        delete extract_elt_array;
        extract_delete = false;
    }

    std::string m_name("DevicePipeBlob::");
    m_name = m_name + _meth;
    TANGO_THROW_EXCEPTION(API_NotSupportedFeature,
                          "Not supported to mix extraction type (operator >> (or <<) and operator [])");
}

//+------------------------------------------------------------------------------------------------------------------
//
// method
//         DevicePipeBlob::print
//
// description :
//        Print blob content. Due to recusrsive type, it was not possible to write this feature as a classical
//        << operator.
//
// argument:
//        in :
//            - o_str : The stream used for printing
//            - indent : Indentation level for printing in case of blob inside blob
//            - insert_extract: Flag set to true is insert data should be printed, else extract data are printed
//
//-------------------------------------------------------------------------------------------------------------------

void DevicePipeBlob::print(std::ostream &o_str, int indent, bool insert_extract) const
{
    //
    // Print blob name (if any)
    //

    if(name.size() != 0 && insert_extract)
    {
        int max = indent;
        if(indent > 1)
        {
            max--;
        }
        for(int loop = 0; loop < max; loop++)
        {
            o_str << "\t";
        }
        o_str << "Blob name = " << name << std::endl;
    }

    //
    // Data element name/value
    //

    const DevVarPipeDataEltArray *dvpdea;
    if(!insert_extract)
    {
        dvpdea = get_extract_data();
    }
    else
    {
        dvpdea = get_insert_data();
    }

    if(dvpdea != nullptr)
    {
        for(size_t ctr = 0; ctr < dvpdea->length(); ctr++)
        {
            for(int loop = 0; loop < indent; loop++)
            {
                o_str << "\t";
            }
            o_str << "Data element name = " << (*dvpdea)[ctr].name.in() << std::endl;
            for(int loop = 0; loop < indent; loop++)
            {
                o_str << "\t";
            }
            o_str << "Data element value:" << std::endl;

            //
            // Print indent level
            //

            for(int loop = 0; loop < indent; loop++)
            {
                o_str << "\t";
            }

            //
            // Print data element value
            //

            switch((*dvpdea)[ctr].value._d())
            {
            case ATT_BOOL:
            {
                const DevVarBooleanArray &dvba = (*dvpdea)[ctr].value.bool_att_value();
                o_str << dvba;
            }
            break;

            case ATT_SHORT:
            {
                const DevVarShortArray &dvsa = (*dvpdea)[ctr].value.short_att_value();
                o_str << dvsa;
            }
            break;

            case ATT_LONG:
            {
                const DevVarLongArray &dvla = (*dvpdea)[ctr].value.long_att_value();
                o_str << dvla;
            }
            break;

            case ATT_LONG64:
            {
                const DevVarLong64Array &dvlo64 = (*dvpdea)[ctr].value.long64_att_value();
                o_str << dvlo64;
            }
            break;

            case ATT_FLOAT:
            {
                const DevVarFloatArray &dvfa = (*dvpdea)[ctr].value.float_att_value();
                o_str << dvfa;
            }
            break;

            case ATT_DOUBLE:
            {
                const DevVarDoubleArray &dvda = (*dvpdea)[ctr].value.double_att_value();
                o_str << dvda;
            }
            break;

            case ATT_UCHAR:
            {
                const DevVarCharArray &dvda = (*dvpdea)[ctr].value.uchar_att_value();
                o_str << dvda;
            }
            break;

            case ATT_USHORT:
            {
                const DevVarUShortArray &dvda = (*dvpdea)[ctr].value.ushort_att_value();
                o_str << dvda;
            }
            break;

            case ATT_ULONG:
            {
                const DevVarULongArray &dvda = (*dvpdea)[ctr].value.ulong_att_value();
                o_str << dvda;
            }
            break;

            case ATT_ULONG64:
            {
                const DevVarULong64Array &dvda = (*dvpdea)[ctr].value.ulong64_att_value();
                o_str << dvda;
            }
            break;

            case ATT_STRING:
            {
                const DevVarStringArray &dvda = (*dvpdea)[ctr].value.string_att_value();
                o_str << dvda;
            }
            break;

            case ATT_STATE:
            {
                const DevVarStateArray &dvda = (*dvpdea)[ctr].value.state_att_value();
                o_str << dvda;
            }
            break;

            case ATT_ENCODED:
            {
                const DevVarEncodedArray &dvda = (*dvpdea)[ctr].value.encoded_att_value();
                o_str << dvda;
            }
            break;

            case DEVICE_STATE:
                o_str << "Unsupported data type in data element (ATT_NO_DATA, DEVICE_STATE)!" << std::endl;
                break;

            default:
                TANGO_ASSERT_ON_DEFAULT((*dvpdea)[ctr].value._d());
            }

            //
            // Print inner blob if any
            //

            if((*dvpdea)[ctr].inner_blob.length() != 0)
            {
                DevicePipeBlob dpb((*dvpdea)[ctr].inner_blob_name.in());
                dpb.set_extract_data(&(*dvpdea)[ctr].inner_blob);
                dpb.print(o_str, indent + 1, true);
                dpb.print(o_str, indent + 1, false);
            }

            if(ctr < dvpdea->length() - 1)
            {
                o_str << std::endl;
            }
        }
    }
}

//+------------------------------------------------------------------------------------------------------------------
//
// Function
//         operator overloading :     <<
//
// description :
//        Friend function to ease printing instance of the DevicePipe class
//
//-------------------------------------------------------------------------------------------------------------------

std::ostream &operator<<(std::ostream &o_str, const DevicePipe &dd)
{
    //
    // Pipe date
    //

    if(dd.time.tv_sec != 0)
    {
        std::tm tm = Tango_localtime(dd.time.tv_sec);
        o_str << std::put_time(&tm, "%c") << " (" << dd.time.tv_sec << "," << dd.time.tv_usec << " sec) : ";
    }

    //
    // Print pipe name
    //

    o_str << dd.name << std::endl;

    //
    // Print blob
    //

    const DevicePipeBlob &dpb = dd.get_root_blob();
    if(dpb.get_insert_data() == nullptr && dpb.get_extract_data() == nullptr)
    {
        o_str << "DevicePipe is empty";
    }
    else
    {
        dpb.print(o_str, 0, true);
        dpb.print(o_str, 0, false);
    }

    return o_str;
}

DevicePipe &operator>>(DevicePipe &_dp, char *&datum)
{
    _dp.get_root_blob().operator>>(datum);
    return _dp;
}

} // namespace Tango
