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

#ifndef TANGO_SERVER_TEMPLCOMMANDIN_H
#define TANGO_SERVER_TEMPLCOMMANDIN_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 TemplCommandIn 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
//            paremeters.
//            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 input parameter but without output parameter. The class template
 * parameter (called INARG) is the command input parameter type.
 *<p>Synopsis : template typename &lt;INARG&gt; class TemplCommandIn:public TemplCommand;</p>
 *<p>Usage : new TemplCommandIn&lt;Tango::DevLong&gt;(...);</p>
 *
 *
 * @headerfile tango.h
 * @ingroup Server
 */

template <typename INARG>
class TemplCommandIn : public TemplCommand
{
  public:
    /**@name Constructors
     * Miscellaneous constructors */
    //@{
    /**
     * Constructs a newly allocated TemplCommandIn 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
     *
     */
    TemplCommandIn(const std::string &cmd_name, void (DeviceImpl::*exe_method)(INARG), Tango::DispLevel level);

    /**
     * Constructs a newly allocated TemplCommandIn 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
     *
     */
    TemplCommandIn(const std::string &cmd_name,
                   void (DeviceImpl::*exe_method)(INARG),
                   bool (DeviceImpl::*state_method)(const CORBA::Any &),
                   Tango::DispLevel level);

    /**
     * Constructs a newly allocated TemplCommandIn 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
     *
     */
    TemplCommandIn(const std::string &cmd_name,
                   void (DeviceImpl::*exe_method)(INARG),
                   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
     *
     */
    TemplCommandIn(const std::string &cmd_name,
                   void (DeviceImpl::*exe_method)(INARG),
                   bool (DeviceImpl::*state_method)(const CORBA::Any &) = nullptr,
                   const std::string &in_desc = "",
                   const std::string &out_desc = "",
                   Tango::DispLevel level = Tango::OPERATOR);

    //@}

    ~TemplCommandIn() 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 unpacks the data stored in
     * the CORBA Any object and invoke the user supplied command execution
     * method
     *
     * @param dev The device on which the command must be executed
     * @param in_any The incoming data still packed in a CORBA Any object.
     * @return The CORBA Any object returned to the client. For command created with
     * this TemplCommandIn class, this any object does not contain data.
     * @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 TemplCommandInExt
    {
    };

    void (DeviceImpl::*exe_ptr_in)(INARG);
    std::unique_ptr<TemplCommandInExt> ext; // Class extension
};

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

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

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

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

template <typename INARG>
TemplCommandIn<INARG>::TemplCommandIn(const std::string &s,
                                      void (DeviceImpl::*f)(INARG),
                                      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_in(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 INARG>
void TemplCommandIn<INARG>::init_types()
{
    out_type = Tango::DEV_VOID;
    //
    // Set intput type
    //
    set_type(typeid(INARG), in_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 INARG>
CORBA::Any *TemplCommandIn<INARG>::execute(DeviceImpl *dev_ptr, const CORBA::Any &in_any)
{
    //
    // Execute the command associated method
    //

    INARG in_data;
    extract(in_any, in_data);
    (dev_ptr->*exe_ptr_in)(in_data);
    return insert();
}

} // namespace Tango

#endif // TANGO_SERVER_TEMPLCOMMANDIN_H
