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

#include <tango/client/DeviceProxy.h>

#include <tango/client/ApiUtil.h>

namespace Tango
{
//-----------------------------------------------------------------------------
//
// method :         DeviceProxy::read_attributes_asynch()
//
// description :     Read Tango device attributes asynchrnously.
//            The client is not blocked until the attributes are read
//
// argin(s) :        attr_names : The attribute name(s)
//            cb : The callback object
//
//-----------------------------------------------------------------------------

void DeviceProxy::read_attributes_asynch(const std::vector<std::string> &attr_names, CallBack &cb)
{
    //
    // Reconnect to device in case it is needed
    //

    try
    {
        check_and_reconnect();
    }
    catch(Tango::ConnectionFailed &e)
    {
        TangoSys_OMemStream desc;
        desc << "Failed to execute read_attributes_asynch on device " << dev_name() << std::ends;
        TANGO_RETHROW_DETAILED_EXCEPTION(ApiConnExcept, e, API_CommandFailed, desc.str());
    }

    //
    // Check that the caller did not give two times the same attribute
    //

    same_att_name(attr_names, "DeviceProxy::read_attributes_asynch");

    //
    // Create the request object
    //

    CORBA::ORB::RequestSeq req_seq;
    req_seq.length(1);

    Tango::DevVarStringArray names;
    long nb_names = attr_names.size();
    names.length(nb_names);
    for(int i = 0; i < nb_names; i++)
    {
        names[i] = attr_names[i].c_str();
    }

    if(version >= 5)
    {
        req_seq[0] = Connection::device_5->_request("read_attributes_5");
        req_seq[0]->add_in_arg() <<= names;
        req_seq[0]->add_in_arg() <<= source;
        req_seq[0]->add_in_arg() <<= get_client_identification();
        req_seq[0]->set_return_type(Tango::_tc_AttributeValueList_5);
    }
    else if(version == 4)
    {
        req_seq[0] = Connection::device_4->_request("read_attributes_4");
        req_seq[0]->add_in_arg() <<= names;
        req_seq[0]->add_in_arg() <<= source;
        req_seq[0]->add_in_arg() <<= get_client_identification();
        req_seq[0]->set_return_type(Tango::_tc_AttributeValueList_4);
    }
    else if(version == 3)
    {
        req_seq[0] = Connection::device_3->_request("read_attributes_3");
        req_seq[0]->add_in_arg() <<= names;
        req_seq[0]->add_in_arg() <<= source;
        req_seq[0]->set_return_type(Tango::_tc_AttributeValueList_3);
    }
    else if(version == 2)
    {
        req_seq[0] = device_2->_request("read_attributes_2");
        req_seq[0]->add_in_arg() <<= names;
        req_seq[0]->add_in_arg() <<= source;
        req_seq[0]->set_return_type(Tango::_tc_AttributeValueList);
    }
    else
    {
        req_seq[0] = device->_request("read_attributes");
        req_seq[0]->add_in_arg() <<= names;
        req_seq[0]->set_return_type(Tango::_tc_AttributeValueList);
    }
    req_seq[0]->exceptions()->add(Tango::_tc_DevFailed);

    //
    // Send the request
    // and store it in the global asynchronous cb requests table
    //

    ApiUtil *au = ApiUtil::instance();
    add_asyn_cb_request(req_seq[0], &cb, this, TgRequest::READ_ATTR);
    CORBA::ORB_var orb = au->get_orb();
    orb->send_multiple_requests_deferred(req_seq);
    if(au->get_asynch_cb_sub_model() == PUSH_CALLBACK)
    {
        au->get_pasyn_table()->signal();
    }
}

void DeviceProxy::read_attribute_asynch(const std::string &attr_name, CallBack &cb)
{
    std::vector<std::string> tmp_att_names(1, attr_name);
    read_attributes_asynch(tmp_att_names, cb);
}

//-----------------------------------------------------------------------------
//
// method :         DeviceProxy::write_attributes_asynch()
//
// description :     Write Tango device attributes asynchronously.
//            The client is not blocked until the attributes are written
//
// argin(s) :        attr_list : The attribute name(s) and value(s)
//            cb : The callback object
//
//-----------------------------------------------------------------------------

void DeviceProxy::write_attributes_asynch(const std::vector<DeviceAttribute> &attr_list, CallBack &cb)
{
    //
    // Reconnect to device in case it is needed
    //

    try
    {
        check_and_reconnect();
    }
    catch(Tango::ConnectionFailed &e)
    {
        TangoSys_OMemStream desc;
        desc << "Failed to execute read_attributes_asynch on device " << dev_name() << std::ends;
        TANGO_RETHROW_DETAILED_EXCEPTION(ApiConnExcept, e, API_CommandFailed, desc.str());
    }

    //
    // Create the request object
    //

    CORBA::ORB::RequestSeq req_seq;
    req_seq.length(1);

    Tango::AttributeValueList att;
    Tango::AttributeValueList_4 att_4;

    long nb_attr = attr_list.size();
    if(version < 4)
    {
        att.length(nb_attr);
    }
    else
    {
        att_4.length(nb_attr);
    }

    for(int i = 0; i < nb_attr; i++)
    {
        if(version < 4)
        {
            ApiUtil::device_to_attr(attr_list[i], att[i], device_name);
        }
        else
        {
            ApiUtil::device_to_attr(attr_list[i], att_4[i]);
        }
    }

    if(version >= 4)
    {
        req_seq[0] = device_4->_request("write_attributes_4");
        req_seq[0]->add_in_arg() <<= att_4;
        req_seq[0]->add_in_arg() <<= get_client_identification();
        req_seq[0]->exceptions()->add(Tango::_tc_MultiDevFailed);
    }
    else if(version == 3)
    {
        req_seq[0] = device->_request("write_attributes_3");
        req_seq[0]->add_in_arg() <<= att;
        req_seq[0]->exceptions()->add(Tango::_tc_MultiDevFailed);
    }
    else
    {
        req_seq[0] = device->_request("write_attributes");
        req_seq[0]->add_in_arg() <<= att;
    }
    req_seq[0]->exceptions()->add(Tango::_tc_DevFailed);

    //
    // Send the request
    // and store it in the global asynchronous cb requests table
    //

    ApiUtil *au = ApiUtil::instance();
    add_asyn_cb_request(req_seq[0], &cb, this, TgRequest::WRITE_ATTR);
    CORBA::ORB_var orb = au->get_orb();
    orb->send_multiple_requests_deferred(req_seq);
    if(au->get_asynch_cb_sub_model() == PUSH_CALLBACK)
    {
        au->get_pasyn_table()->signal();
    }
}

//-----------------------------------------------------------------------------
//
// method :         DeviceProxy::write_attribute_asynch()
//
// description :     Write Tango device attribute asynchronously.
//            The client is not blocked until the attributes are written
//
//            WARNING : Obviously, this method could simply create
//            a vector of DeviceAttribute and call the previous
//            method (write_attributes_asynch (note the attributes))
//            using this vector. But the copy constructor of the
//            DeviceAttribute class, pass the memory to the newly
//            created DeviceAttribute object !!!!
//            Therefore, it is not possible to use this solution
//            in this case
//
// argin(s) :        attr_list : The attribute name(s) and value(s)
//            cb : The callback object
//
//-----------------------------------------------------------------------------

void DeviceProxy::write_attribute_asynch(const DeviceAttribute &attr, CallBack &cb)
{
    //
    // Reconnect to device in case it is needed
    //

    try
    {
        check_and_reconnect();
    }
    catch(Tango::ConnectionFailed &e)
    {
        TangoSys_OMemStream desc;
        desc << "Failed to execute read_attributes_asynch on device " << dev_name() << std::ends;
        TANGO_RETHROW_DETAILED_EXCEPTION(ApiConnExcept, e, API_CommandFailed, desc.str());
    }

    //
    // Create the request object
    //

    CORBA::ORB::RequestSeq req_seq;
    req_seq.length(1);

    Tango::AttributeValueList att;
    Tango::AttributeValueList_4 att_4;

    if(version < 4)
    {
        att.length(1);
        ApiUtil::device_to_attr(attr, att[0], device_name);
    }
    else
    {
        att_4.length(1);
        ApiUtil::device_to_attr(attr, att_4[0]);
    }

    if(version >= 4)
    {
        req_seq[0] = device_4->_request("write_attributes_4");
        req_seq[0]->add_in_arg() <<= att_4;
        req_seq[0]->add_in_arg() <<= get_client_identification();
        req_seq[0]->exceptions()->add(Tango::_tc_MultiDevFailed);
    }
    else if(version == 3)
    {
        req_seq[0] = device->_request("write_attributes_3");
        req_seq[0]->add_in_arg() <<= att;
        req_seq[0]->exceptions()->add(Tango::_tc_MultiDevFailed);
    }
    else
    {
        req_seq[0] = device->_request("write_attributes");
        req_seq[0]->add_in_arg() <<= att;
    }
    req_seq[0]->exceptions()->add(Tango::_tc_DevFailed);

    //
    // Send the request
    // and store it in the global asynchronous cb requests table
    //

    ApiUtil *au = ApiUtil::instance();
    add_asyn_cb_request(req_seq[0], &cb, this, TgRequest::WRITE_ATTR_SINGLE);
    CORBA::ORB_var orb = au->get_orb();
    orb->send_multiple_requests_deferred(req_seq);
    if(au->get_asynch_cb_sub_model() == PUSH_CALLBACK)
    {
        au->get_pasyn_table()->signal();
    }
}

} // namespace Tango
