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

#ifndef TANGO_CLIENT_GROUPCMDREPLY_H
#define TANGO_CLIENT_GROUPCMDREPLY_H

#include <tango/client/DeviceData.h>
#include <tango/client/GroupReply.h>
#include <tango/server/Except.h>

#include <string>
#include <vector>

namespace Tango
{

/** @defgroup Grp Group Client classes
 * @ingroup Client
 */

//=============================================================================
// class GroupCmdReply : reply to command executed on a group
//=============================================================================
/**
 * Single element group reply for a command execution
 *
 *
 * @headerfile tango.h
 * @ingroup Grp
 */
class GroupCmdReply : public GroupReply
{
  public:
    ///@privatesection
    //- default ctor
    GroupCmdReply();
    //-
    GroupCmdReply(const std::string &dev_name, const std::string &obj_name, const DeviceData &data);
    //- ctor
    GroupCmdReply(const std::string &dev_name, const std::string &obj_name, const DevFailed &exception);
    //- ctor
    GroupCmdReply(const std::string &dev_name, const std::string &obj_name, bool group_element_enabled);
    //- dtor
    ~GroupCmdReply() override;

    GroupCmdReply(const GroupCmdReply &);
    GroupCmdReply &operator=(const GroupCmdReply &);

    GroupCmdReply(GroupCmdReply &&);
    GroupCmdReply &operator=(GroupCmdReply &&);

    ///@publicsection
    //- data accessor (may throw Tango::DevFailed)
    /**
     * Get command data
     *
     * Get command result for a device member of a group hierarchy
     *
     * @return The command data
     */
    DeviceData &get_data();
    //- template data extractor method
    /**
     * Get command data and extract them
     *
     * Get command result for a device member of a group hierarchy and extract
     * them in the provided variable
     *
     * @param [out] dest The variable in which the data should be extracted
     * @return Flag set to true if the extraction succeeds
     */
    template <typename T>
    bool operator>>(T &dest);
    //- data extractor method for DevVarLongStringArray
    /**
     * Get command data and extract them (DevVarLongStringArray)
     *
     * Get command result for a device member of a group hierarchy and extract
     * them in the provided variable in case the command returns a
     * data of the DevVarLongStringArray type
     *
     * @param [out] vl The array of DevLong part of the DevVarLongStringArray
     * @param [out] vs The array of string part of the DevVarLongStringArray data
     * @return Flag set to true if the extraction succeeds
     */
    bool extract(std::vector<DevLong> &vl, std::vector<std::string> &vs);
    //- data extractor method for DevVarDoubleStringArray
    /**
     * Get command data and extract them (DevVarDoubleStringArray)
     *
     * Get command result for a device member of a group hierarchy and extract
     * them in the provided variable in case the command returns a
     * data of the DevVarDoubleStringArray type
     *
     * @param [out] vd The array of DevDouble part of the DevVarDoubleStringArray
     * @param [out] vs The array of string part of the DevVarDoubleStringArray data
     * @return Flag set to true if the extraction succeeds
     */
    bool extract(std::vector<double> &vd, std::vector<std::string> &vs);

  private:
    //- data: valid if GroupReply::has_failed_m set to false and
    //- GroupReply::enabled_m set to true
    DeviceData data_m;
};

//=============================================================================
// GroupCmdReply::operator>> template impl.
//=============================================================================
template <typename T>
bool GroupCmdReply::operator>>(T &dest)
{
    bool result = true;
    if(!GroupReply::group_element_enabled_m)
    {
        if(exception_enabled)
        {
            Tango::DevErrorList errors(1);
            errors.length(1);
            errors[0].severity = Tango::ERR;
            errors[0].desc = Tango::string_dup("no available data");
            errors[0].reason = Tango::string_dup("no data - group member is disabled");
            errors[0].origin = Tango::string_dup(TANGO_EXCEPTION_ORIGIN);
            DevFailed df(errors);
            throw df;
        }
        result = false;
    }
    else if(GroupReply::has_failed_m)
    {
        if(exception_enabled)
        {
            throw GroupReply::exception_m;
        }
        result = false;
    }
    else
    {
        std::bitset<DeviceData::numFlags> bs;
        data_m.exceptions(exception_enabled ? bs.set() : bs.reset());
        try
        {
            result = data_m >> dest;
        }
        catch(const DevFailed &df)
        {
            GroupReply::exception_m = df;
            if(exception_enabled)
            {
                throw GroupReply::exception_m;
            }
            result = false;
        }
        catch(...)
        {
            if(exception_enabled)
            {
                Tango::DevErrorList errors(1);
                errors.length(1);
                errors[0].severity = Tango::ERR;
                errors[0].desc = Tango::string_dup("unknown exception caught");
                errors[0].reason = Tango::string_dup("an error occurred while trying to extract data");
                errors[0].origin = Tango::string_dup(TANGO_EXCEPTION_ORIGIN);
                DevFailed df(errors);
                GroupReply::exception_m = df;
                throw GroupReply::exception_m;
            }
            result = false;
        }
    }
    return result;
}

} // namespace Tango

#endif /* TANGO_CLIENT_GROUPCMDREPLY_H */
