/*
 * SPDX-FileCopyrightText: 2025 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
#ifndef TANGO_CLIENT_HELPERS_COMMANDINOUTHELPER_H
#define TANGO_CLIENT_HELPERS_COMMANDINOUTHELPER_H

#include <tango/client/helpers/HelperBase.h>

namespace Tango
{
//=============================================================================
// CLASS: CommandInOutHelper
//=============================================================================
class CommandInOutHelper : public virtual HelperBase
{
  public:
    //---------------------------------------------------------------------------
    //  CommandInOutHelper::CommandInOutHelper  (ctor)
    //  device_name : The name of the target device.
    //  client_device : Reference to the client device (for logging purpose).
    //---------------------------------------------------------------------------
    CommandInOutHelper(const std::string &device_name, Tango::DeviceImpl *client_device = 0) :
        HelperBase(device_name, client_device)
    {
        dd_out_.exceptions(Tango::DeviceData::isempty_flag | Tango::DeviceData::wrongtype_flag);
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::~CommandInOutHelper  (dtor)
    //---------------------------------------------------------------------------
    virtual ~CommandInOutHelper()
    {
        //- noop dtor
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command
    //  exec a DEV_VOID/DEV_VOID TANGO command on the underlying device
    //  cmd_name : The name of the TANGO command
    //---------------------------------------------------------------------------
    void internal_command(const std::string &cmd_name, std::string file, int line)
    {
        if(device_proxy_)
        {
            _DEV_TRY_FILE_LINE(
                //- try

                (device_proxy_->command_inout)(const_cast<std::string &>(cmd_name)),

                //- what do we tried
                std::string("command_inout on ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command",
                file,
                line);
        }
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_inout
    //  exec a ARG_OUT/ARG_IN TANGO command on the underlying device
    //  cmd_name : the name of the TANGO command
    //  argin : input argument
    //  argout : output argument

    //---------------------------------------------------------------------------
    //  template arg _IN must be supported by DeviceData::operator<<
    //  template arg _OUT must be supported by DeviceData::operator>>
    //---------------------------------------------------------------------------

    template <class _IN, class _OUT>
    void internal_command_inout(
        const std::string &cmd_name, const _IN &argin, _OUT &argout, std::string file = __FILE__, int line = __LINE__)
    {
        if(device_proxy_)
        {
            Tango::DeviceData dd_in;
            dd_in << const_cast<_IN &>(argin);
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_ = (device_proxy_->command_inout)(const_cast<std::string &>(cmd_name), dd_in),
                //- what do we tried
                std::string("command_inout on ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_inout",
                file,
                line);
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_ >> argout,
                //- what do we tried
                std::string("DeviceData::operator>> on data returned by ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_inout",
                file,
                line);
        }
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_inout
    //  exec a DEVVAR<X>STRINGARRAY/DEVVAR<X>STRINGARRAY command on the underlying device
    //  cmd_name : the name of the TANGO command
    //  _nv_in : numerical part of the input DEVVAR<X>STRINGARRAY
    //  _sv_in : string part of the input DEVVAR<X>STRINGARRAY
    //  _nv_out : numerical part of the ouput DEVVAR<X>STRINGARRAY
    //  _sv_out : string part of the ouput DEVVAR<X>STRINGARRAY
    //---------------------------------------------------------------------------
    //  template arg _IN must be supported by DeviceData::.insert
    //  template arg _OUT must be supported by DeviceData::.insert
    //---------------------------------------------------------------------------
    template <class _IN, class _OUT>
    void internal_command_inout(const std::string &cmd_name,
                                const std::vector<_IN> &_nv_in,
                                const std::vector<std::string> &_sv_in,
                                std::vector<_OUT> &_nv_out,
                                std::vector<std::string> &_sv_out,
                                std::string file = __FILE__,
                                int line = __LINE__)
    {
        if(device_proxy_)
        {
            Tango::DeviceData dd_in;
            dd_in.insert(const_cast<std::vector<_IN> &>(_nv_in), const_cast<std::vector<std::string> &>(_sv_in));
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_ = (device_proxy_->command_inout)(const_cast<std::string &>(cmd_name), dd_in),
                //- what do we tried
                std::string("command_inout on ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_inout",
                file,
                line);
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_.extract(_nv_out, _sv_out),
                //- what do we tried
                std::string("DeviceData::extract on data returned by ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_inout",
                file,
                line);
        }
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_inout
    //  Overloaded commands to  avoid usage of DevVarXX ARRAY for argout
    //---------------------------------------------------------------------------
    template <class _IN>
    void internal_command_inout(const std::string &TANGO_UNUSED(cmd_name),
                                const _IN &TANGO_UNUSED(argin),
                                DevVarDoubleStringArray *TANGO_UNUSED(argout),
                                std::string file = __FILE__,
                                int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;
        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_inout
    //  Overloaded commands to  avoid usage of DevVarXX ARRAY for argout
    //---------------------------------------------------------------------------
    template <class _IN>
    void internal_command_inout(const std::string &TANGO_UNUSED(cmd_name),
                                const _IN &TANGO_UNUSED(argin),
                                DevVarLongStringArray *TANGO_UNUSED(argout),
                                std::string file = __FILE__,
                                int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;
        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_inout
    //  Overloaded commands to  avoid usage of DevVarXX ARRAY for argout
    //---------------------------------------------------------------------------
    template <class _IN>
    void internal_command_inout(const std::string &TANGO_UNUSED(cmd_name),
                                const _IN &TANGO_UNUSED(argin),
                                DevVarDoubleStringArray &TANGO_UNUSED(argout),
                                std::string file = __FILE__,
                                int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;
        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_inout
    //  Overloaded commands to  avoid usage of DevVarXX ARRAY for argout
    //---------------------------------------------------------------------------
    template <class _IN>
    void internal_command_inout(const std::string &TANGO_UNUSED(cmd_name),
                                const _IN &TANGO_UNUSED(argin),
                                DevVarLongStringArray &TANGO_UNUSED(argout),
                                std::string file = __FILE__,
                                int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;
        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_in
    //  exec a DEV_VOID/ARG_IN TANGO command on the underlying device
    //  cmd_name : the name of the TANGO command
    //  argin : input argument
    // dummy : used to have same number of parameter for the 2 command_in methods
    //---------------------------------------------------------------------------
    //  template arg _IN must be supported by DeviceData::operator<<
    //---------------------------------------------------------------------------
    template <class _IN>
    void internal_command_in(const std::string &cmd_name,
                             const _IN &argin,
                             std::string file = __FILE__,
                             int line = __LINE__)
    {
        if(device_proxy_)
        {
            Tango::DeviceData dd_in;
            dd_in << const_cast<_IN &>(argin);
            _DEV_TRY_FILE_LINE(
                //- try
                (device_proxy_->command_inout)(const_cast<std::string &>(cmd_name), dd_in),
                //- what do we tried
                std::string("command_inout on ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_in",
                file,
                line);
        }
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_in
    //  exec a DEV_VOID/DEVVAR<X>STRINGARRAY command on the underlying device
    //  cmd_name : the name of the TANGO command
    //  _nv_in   : numerical part of the input DEVVAR<X>STRINGARRAY
    //  _sv_in   : string part of the input DEVVAR<X>STRINGARRAY
    //---------------------------------------------------------------------------
    //  template arg _IN must be supported by DeviceData::.insert
    //---------------------------------------------------------------------------
    template <class _IN>
    void internal_command_in(const std::string &cmd_name,
                             const std::vector<_IN> &_nv_in,
                             const std::vector<std::string> &_sv_in,
                             std::string file = __FILE__,
                             int line = __LINE__)
    {
        if(device_proxy_)
        {
            Tango::DeviceData dd_in;
            dd_in.insert(const_cast<std::vector<_IN> &>(_nv_in), const_cast<std::vector<std::string> &>(_sv_in));
            _DEV_TRY_FILE_LINE(
                //- try
                (device_proxy_->command_inout)(const_cast<std::string &>(cmd_name), dd_in),
                //- what do we tried
                std::string("command_inout on ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_in",
                file,
                line);
        }
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_out
    //  exec a ARG_OUT/DEV_VOID TANGO command on the underlying device
    //  cmd_name : the name of the TANGO command
    //  argout : output argument
    //---------------------------------------------------------------------------
    //  template arg _OUT must be supported by DeviceData::operator>>
    //---------------------------------------------------------------------------
    template <class _OUT>
    void internal_command_out(const std::string &cmd_name,
                              _OUT &argout,
                              std::string file = __FILE__,
                              int line = __LINE__)
    {
        if(device_proxy_)
        {
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_ = (device_proxy_->command_inout)(const_cast<std::string &>(cmd_name)),
                //- what do we tried
                std::string("command_inout on ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_out",
                file,
                line);
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_ >> argout,
                //- what do we tried
                std::string("DeviceData::operator>> on data returned by ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_out",
                file,
                line);
        }
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_out
    //  Overloaded commands to  avoid usage of DevVarDoubleStringArray ARRAY
    //---------------------------------------------------------------------------
    template <class _OUT>
    void internal_command_out(_OUT TANGO_UNUSED(dummy),
                              DevVarDoubleStringArray *TANGO_UNUSED(argout),
                              std::string file = __FILE__,
                              int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;

        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_out
    //  Overloaded commands to  avoid usage of DevVarLongStringArray ARRAY
    //---------------------------------------------------------------------------
    template <class _OUT>
    void internal_command_out(_OUT TANGO_UNUSED(dummy),
                              DevVarLongStringArray *TANGO_UNUSED(argout),
                              std::string file = __FILE__,
                              int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;
        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_out
    //  Overloaded commands to  avoid usage of DevVarDoubleStringArray ARRAY
    //---------------------------------------------------------------------------
    template <class _OUT>
    void internal_command_out(_OUT TANGO_UNUSED(dummy),
                              DevVarDoubleStringArray &TANGO_UNUSED(argout),
                              std::string file = __FILE__,
                              int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;
        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_out
    //  Overloaded commands to  avoid usage of DevVarLongStringArray ARRAY
    //---------------------------------------------------------------------------
    template <class _OUT>
    void internal_command_out(_OUT TANGO_UNUSED(dummy),
                              DevVarLongStringArray &TANGO_UNUSED(argout),
                              std::string file = __FILE__,
                              int line = __LINE__)
    {
        TangoSys_OMemStream o;
        o << " [" << file << "::" << line << "]" << std::ends;
        Tango::Except::throw_exception(static_cast<const char *>("TANGO_WRONG_DATA_ERROR"),
                                       static_cast<const char *>("Use only STL vector instead of DevVarXXStringArray"),
                                       static_cast<const char *>(o.str().c_str()));
    }

    //---------------------------------------------------------------------------
    //  CommandInOutHelper::command_in
    //  exec a DEV_VOID/DEVVAR<X>STRINGARRAY command on the underlying device
    //  cmd_name : the name of the TANGO command
    //  _nv_out : numerical part of the output DEVVAR<X>STRINGARRAY
    //  _sv_out : string part of the output DEVVAR<X>STRINGARRAY
    //---------------------------------------------------------------------------
    //  template arg _OUT must be supported by DeviceData::.extract
    //---------------------------------------------------------------------------
    template <class _OUT>
    void internal_command_out(const std::string &cmd_name,
                              std::vector<_OUT> &_nv_out,
                              std::vector<std::string> &_sv_out,
                              std::string file = __FILE__,
                              int line = __LINE__)
    {
        if(device_proxy_)
        {
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_ = (device_proxy_->command_inout)(const_cast<std::string &>(cmd_name)),
                //- what do we tried
                std::string("command_inout on ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_out",
                file,
                line);
            _DEV_TRY_FILE_LINE(
                //- try
                dd_out_.extract(_nv_out, _sv_out),
                //- what do we tried
                std::string("DeviceData::extract on data returned by ") + FULL_CMD_NAME(cmd_name),
                //- origin
                "CommandInOutHelper::command_out",
                file,
                line);
        }
    }

  private:
    //- placed here as a workaround due to CORBA::any_var limitations
    Tango::DeviceData dd_out_;
};
} // namespace Tango
#endif
