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

#ifndef TANGO_SERVER_TEMPLCOMMANDOUT_H
#define TANGO_SERVER_TEMPLCOMMANDOUT_H

#include <tango/common/tango_const.h>
#include <tango/server/Command.h>
#include <tango/server/DeviceImpl.h>
#include <tango/server/TemplCommand.h>

#include <memory>
#include <string>

namespace Tango
{

//=============================================================================
//
//            The TemplCommandOut class
//
//
// description :    This class is a derived class of the Command class.
//            It is used to create a command from a pointer to a
//            object method which will execute the command.
//            This class is for command without inout nor output
//            parameters.
//            This class is also a base class for the template Command
//            class
//
//=============================================================================

/**
 * This class is a class representing a command in the template command model
 * with output parameter but without input parameter. The class template
 * parameter (called OUTARG) is the command output parameter type.
 *<p>Synopsis : template &lt;typename OUTARG&gt; class TemplCommandOut:public TemplCommand;</p>
 *<p>Usage : new TemplCommandOut&lt;Tango::DevLong&gt;(...);</p>
 *
 *
 * @headerfile tango.h
 * @ingroup Server
 */

template <typename OUTARG>
class TemplCommandOut : public TemplCommand
{
  public:
    /**@name Constructors
     * Miscellaneous constructors */
    //@{
    /**
     * Constructs a newly allocated TemplCommandOut object for a command with a
     * name and an execution method.
     * The input and output command data type are automatically determined.
     * The input and output parameter description are set to the default String
     * "Uninitialised".
     *
     * @param     cmd_name    The command name
     * @param    exe_method    Pointer to the command execution method
     * @param    level        The command display level
     *
     */
    TemplCommandOut(const std::string &cmd_name, OUTARG (DeviceImpl::*exe_method)(), Tango::DispLevel level);

    /**
     * Constructs a newly allocated TemplCommandOut object for a command with a
     * name, an execution method and a command allowed method.
     * The input and output command data type are automatically determined.
     * The input and output parameter description are set to the default String
     * "Uninitialised".
     *
     * @param     cmd_name    The command name
     * @param    exe_method    Pointer to the command execution method
     * @param    state_method     Pointer to the command allowed method
     * @param    level        The command display level
     *
     */
    TemplCommandOut(const std::string &cmd_name,
                    OUTARG (DeviceImpl::*exe_method)(),
                    bool (DeviceImpl::*state_method)(const CORBA::Any &),
                    Tango::DispLevel level);

    /**
     * Constructs a newly allocated TemplCommandOut object for a command with a
     * name, an execution method and a description for the
     * input and output command parameters.
     * The input and output command data type are automatically determined.
     *
     * @param     cmd_name    The command name
     * @param    exe_method    Pointer to the command execution method
     * @param    in_desc        The command input parameter description
     * @param    out_desc    The command output parameter description
     * @param    level        The command display level
     *
     */
    TemplCommandOut(const std::string &cmd_name,
                    OUTARG (DeviceImpl::*exe_method)(),
                    const std::string &in_desc,
                    const std::string &out_desc,
                    Tango::DispLevel level = Tango::OPERATOR);

    /**
     * Constructs a newly allocated TemplCommandIn object for a command with a
     * name, an execution method, a command allowed method and a description for the
     * input and output command parameters.
     * The input and output command data type are automatically determined.
     *
     * @param     cmd_name    The command name
     * @param    exe_method    Pointer to the command execution method
     * @param    state_method     Pointer to the command allowed method
     * @param    in_desc    The command input parameter description
     * @param    out_desc    The command output parameter description
     * @param    level        The command display level
     *
     */
    TemplCommandOut(const std::string &cmd_name,
                    OUTARG (DeviceImpl::*exe_method)(),
                    bool (DeviceImpl::*state_method)(const CORBA::Any &) = nullptr,
                    const std::string &in_desc = "",
                    const std::string &out_desc = "",
                    Tango::DispLevel level = Tango::OPERATOR);

    //@}

    ~TemplCommandOut() override { }

    /**@name Miscellaneous methods */
    //@{
    /**
     * Initialise command input and output types.
     *
     * Set the command output type to Tango::DEV_VOID. The command input type is
     * automatically determined from the class template specialisation
     *
     */
    void init_types() override;

    /**
     * Invoke the command execution method given at object creation time.
     *
     * This method is automtically called by the TANGO core classes when the
     * associated command is requested by a client. It invokes the user supplied
     * command execution method and packs the returned data into the outgoing
     * CORBA Any object
     *
     * @param dev The device on which the command must be executed
     * @param in_any The incoming data still packed in a CORBA Any object. For
     * command created with this TemplCommandOut class, this Any object does not
     * contain usefull data
     * @return The CORBA Any object returned to the client.
     * @exception DevFailed If the execution method failed
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    CORBA::Any *execute(DeviceImpl *dev, const CORBA::Any &in_any) override;
    //@}

  private:
    class TemplCommandOutExt
    {
    };

    OUTARG (DeviceImpl::*exe_ptr_out)();
    std::unique_ptr<TemplCommandOutExt> ext; // Class extension
};

//+-------------------------------------------------------------------------
//
// method :         TempCommandOut class constructors
//
// description :     instance constructor
//
//--------------------------------------------------------------------------

template <typename OUTARG>
TemplCommandOut<OUTARG>::TemplCommandOut(const std::string &s, OUTARG (DeviceImpl::*f)(), Tango::DispLevel level) :
    TemplCommand(s, level),
    exe_ptr_out(f),
    ext(nullptr)
{
    allowed_ptr = nullptr;
    init_types();
}

template <typename OUTARG>
TemplCommandOut<OUTARG>::TemplCommandOut(const std::string &s,
                                         OUTARG (DeviceImpl::*f)(),
                                         bool (DeviceImpl::*a)(const CORBA::Any &),
                                         Tango::DispLevel level) :
    TemplCommand(s, level),
    exe_ptr_out(f),
    ext(nullptr)
{
    allowed_ptr = a;
    init_types();
}

template <typename OUTARG>
TemplCommandOut<OUTARG>::TemplCommandOut(const std::string &s,
                                         OUTARG (DeviceImpl::*f)(),
                                         const std::string &in_desc,
                                         const std::string &out_desc,
                                         Tango::DispLevel level) :
    TemplCommand(s, in_desc, out_desc, level),
    exe_ptr_out(f),
    ext(nullptr)
{
    allowed_ptr = nullptr;
    init_types();
}

template <typename OUTARG>
TemplCommandOut<OUTARG>::TemplCommandOut(const std::string &s,
                                         OUTARG (DeviceImpl::*f)(),
                                         bool (DeviceImpl::*a)(const CORBA::Any &),
                                         const std::string &in_desc,
                                         const std::string &out_desc,
                                         Tango::DispLevel level) :
    TemplCommand(s, in_desc, out_desc, level),
    exe_ptr_out(f),
    ext(nullptr)
{
    allowed_ptr = a;
    init_types();
}

//+-------------------------------------------------------------------------
//
// method :         init_types
//
// description :     Initialise input and output type from the template
//            class specialisation parameters.
//
//--------------------------------------------------------------------------

template <typename OUTARG>
void TemplCommandOut<OUTARG>::init_types()
{
    in_type = Tango::DEV_VOID;
    //
    // Set output type
    //

    set_type(typeid(OUTARG), out_type);
}

//+-------------------------------------------------------------------------
//
// method :         execute
//
// description :     Execute the method associated with the command
//            (stored in the exe_ptr data)
//
// input : - dev_ptr : pointer to the device on which the command must be
//               executed
//       - in_any : Incoming command data
//
// This method returns a pointer to an Any object with the command outing
// data.
//
//--------------------------------------------------------------------------

template <typename OUTARG>
CORBA::Any *TemplCommandOut<OUTARG>::execute(DeviceImpl *dev_ptr, TANGO_UNUSED(const CORBA::Any &in_any))
{
    //
    // Execute the command associated method
    //

    return insert((dev_ptr->*exe_ptr_out)());
}
} // namespace Tango

#endif // TANGO_SERVER_TEMPLCOMMANDOUT_H
