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

#ifndef TANGO_SERVER_TEMPLCOMMAND_H
#define TANGO_SERVER_TEMPLCOMMAND_H

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

#include <memory>
#include <string>

namespace Tango
{

//=============================================================================
//
//            The TemplCommand 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
 * without input or output parameter
 *
 *
 * @headerfile tango.h
 * @ingroup Server
 */

class TemplCommand : public Command
{
  public:
    /**@name Constructors
     * Miscellaneous constructors */
    //@{
    /**
     * Constructs a newly allocated Command object.
     *
     * The default constructor
     */
    TemplCommand() :
        ext(nullptr)
    {
    }

    /**
     * Constructs a newly allocated TemplCommand object for a command with a
     * name and an execution method.
     * This constructor set the command input and output type to Tango::DEV_VOID.
     * 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
     *
     */
    TemplCommand(const std::string &cmd_name, void (DeviceImpl::*exe_method)(), Tango::DispLevel level);

    /**
     * Constructs a newly allocated TemplCommand object for a command with a
     * name, an execution method and a command allowed method.
     * This constructor set the command input and output type to Tango::DEV_VOID
     * 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
     *
     */
    TemplCommand(const std::string &cmd_name,
                 void (DeviceImpl::*exe_method)(),
                 bool (DeviceImpl::*state_method)(const CORBA::Any &),
                 Tango::DispLevel level);

    /**
     * Constructs a newly allocated TemplCommand object for a command with a
     * name, an execution method and a description for the
     * input and output command parameters.
     *
     * @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
     *
     */
    TemplCommand(const std::string &cmd_name,
                 void (DeviceImpl::*exe_method)(),
                 const std::string &in_desc,
                 const std::string &out_desc,
                 Tango::DispLevel level = Tango::OPERATOR);

    /**
     * Constructs a newly allocated TemplCommand object for a command with a
     * name, an execution method, a command allowed method and a description for the
     * input and output command parameters.
     * This constructor set the command input and output type to Tango::DEV_VOID.
     *
     * @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
     *
     */
    TemplCommand(const std::string &cmd_name,
                 void (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);
    //@}

    TemplCommand(const std::string &, Tango::DispLevel);
    TemplCommand(const std::string &, const std::string & = "", const std::string & = "", DispLevel = Tango::OPERATOR);

    /**@name Miscellaneous methods */
    //@{
    /**
     * Choose the correct TANGO data type constant according to data type
     *
     * The TANGO constant is determined using the type_info object passed
     * as first argument of the method. This data is compared to each defined
     * Tango type.
     *
     * @param data_type The type to be analysed
     * @param type A reference where Tango data type constant must be stored
     * @exception DevFailed If the type is not a Tango data type
     * Click <a href="https://tango-controls.readthedocs.io/en/latest/development/advanced/IDL.html#exceptions">here</a>
     * to read <b>DevFailed</b> exception specification
     */
    void set_type(const std::type_info &data_type, Tango::CmdArgType &type);

    /**
     * Invoke the command execution method given at object creation time.
     *
     * This method is automatically called by the TANGO core classes when the
     * associated command is requested by a client.
     *
     * @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 TemplCommand class, this Any object does not
     * contain usefull data
     * @return The CORBA Any object returned to the client. For command created with
     * this TemplCommand 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;

    /**
     * Invoke the command allowed 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 to check if the command is allowed
     * in the actual device state. If the user give a command allowed method
     * at object creation time, this method will be invoked.
     *
     * @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 TemplCommand class, this Any object does not
     * contain data
     * @return A boolean set to true is the command is allowed. Otherwise, the
     * return value is false. This return value is always set to true if the user
     * does not supply a method to be excuted. If a method has been supplied, the
     * return value is the value returned by the user supplied mehod.
     */
    bool is_allowed(DeviceImpl *dev, const CORBA::Any &in_any) override;
    //@}

  private:
    class TemplCommandExt
    {
    };

    void (DeviceImpl::*exe_ptr)();
    std::unique_ptr<TemplCommandExt> ext; // Class extension

  protected:
    /**@name Class data members */
    //@{
    /**
     * The command allowed method object reference
     */
    bool (DeviceImpl::*allowed_ptr)(const CORBA::Any &);
    //@}
};

} // namespace Tango

#endif // TANGO_SERVER_TEMPLCOMMAND_H
