/**
 * @file amr_server.h
 * @brief This file defines the AmrOpcUAServer class for managing an OPC UA server.
 *
 * The AmrOpcUAServer class provides functionalities to initialize, start, stop,
 * and manage access control for an OPC UA server. It includes methods for
 * handling user authentication and managing server operations.
 *
 * This library is part of the amr_comunication package, which is designed to
 * facilitate the integration of ROS (Robot Operating System) with OPC UA
 * for robotic applications.
 *
 * @note Ensure that the Open62541 library is properly linked and configured
 *       to use this package effectively.
 *
 * @author HiepLM
 * @date 16/10/2024
 */

#ifndef __AMR_COMUNICATION_UA_SERVER_H_INCLUDED_
#define __AMR_COMUNICATION_UA_SERVER_H_INCLUDED_
#include <ros/ros.h>
#include <memory>
#include <string>
#include <iostream>
#include <algorithm>
#include <time.h>

#include <thread>
#include <chrono>

#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <open62541/plugin/log_stdout.h>
#include <open62541/plugin/accesscontrol_default.h>
#include <signal.h>

namespace amr_comunication
{
  class AmrOpcUAServer
  {
  public:
    /**
     * @brief Default constructor for AmrOpcUAServer
     */
    AmrOpcUAServer();

    /**
     * @brief Constructor for AmrOpcUAServer with ROS node handle
     * @param nh ROS NodeHandle for initialization
     */
    AmrOpcUAServer(const ros::NodeHandle &nh);

    /**
     * @brief Virtual destructor for AmrOpcUAServer
     */
    virtual ~AmrOpcUAServer();

    /**
     * @brief Initialize the AmrOpcUAServer with a ROS node handle
     * @param nh ROS NodeHandle for initialization
     */
    virtual void init(const ros::NodeHandle &nh);

    /**
     * @brief Stop the server operation.
     *
     * This method is responsible for safely shutting down the server,
     * releasing any resources and stopping any ongoing processes.
     */
    virtual void stop(void);

    /**
     * @brief Start the server operation.
     *
     * This method initializes and starts the server, allowing it to
     * begin processing requests from clients.
     */
    virtual void start(void);

    /**
     * @brief Get server object to redefine 
     */
    UA_Server *getServerObject();

    /**
     * @brief Get status code 
     */
    UA_StatusCode statusCode();

    /**
     * @brief Check server is running 
     */
    UA_Boolean isRunning() { return running_;}

  private:
    /**
     * @brief Allow adding a node to the server.
     *
     * This method is called to determine if a node can be added to the server.
     * It is part of the access control mechanism.
     *
     * @param server Pointer to the UA_Server instance.
     * @param ac Pointer to the UA_AccessControl instance.
     * @param sessionId Pointer to the session ID of the client.
     * @param sessionContext Pointer to the session context.
     * @param item Pointer to the item that contains information about the node to be added.
     * @return UA_Boolean Returns UA_TRUE if the addition is allowed, otherwise UA_FALSE.
     */
    static UA_Boolean allowAddNode(UA_Server *server, UA_AccessControl *ac,
                                   const UA_NodeId *sessionId, void *sessionContext,
                                   const UA_AddNodesItem *item);

    /**
     * @brief Allow adding a reference to a node.
     *
     * This method is called to determine if a reference can be added to a node in the server.
     * It is part of the access control mechanism.
     *
     * @param server Pointer to the UA_Server instance.
     * @param ac Pointer to the UA_AccessControl instance.
     * @param sessionId Pointer to the session ID of the client.
     * @param sessionContext Pointer to the session context.
     * @param item Pointer to the item that contains information about the reference to be added.
     * @return UA_Boolean Returns UA_TRUE if the addition is allowed, otherwise UA_FALSE.
     */
    static UA_Boolean allowAddReference(UA_Server *server, UA_AccessControl *ac,
                                        const UA_NodeId *sessionId, void *sessionContext,
                                        const UA_AddReferencesItem *item);

    /**
     * @brief Allow deleting a node from the server.
     *
     * This method is called to determine if a node can be deleted from the server.
     * It is part of the access control mechanism.
     *
     * @param server Pointer to the UA_Server instance.
     * @param ac Pointer to the UA_AccessControl instance.
     * @param sessionId Pointer to the session ID of the client.
     * @param sessionContext Pointer to the session context.
     * @param item Pointer to the item that contains information about the node to be deleted.
     * @return UA_Boolean Returns UA_TRUE if the deletion is allowed, otherwise UA_FALSE.
     */
    static UA_Boolean allowDeleteNode(UA_Server *server, UA_AccessControl *ac,
                                      const UA_NodeId *sessionId, void *sessionContext,
                                      const UA_DeleteNodesItem *item);

    /**
     * @brief Allow deleting a reference from a node.
     *
     * This method is called to determine if a reference can be deleted from a node in the server.
     * It is part of the access control mechanism.
     *
     * @param server Pointer to the UA_Server instance.
     * @param ac Pointer to the UA_AccessControl instance.
     * @param sessionId Pointer to the session ID of the client.
     * @param sessionContext Pointer to the session context.
     * @param item Pointer to the item that contains information about the reference to be deleted.
     * @return UA_Boolean Returns UA_TRUE if the deletion is allowed, otherwise UA_FALSE.
     */
    static UA_Boolean allowDeleteReference(UA_Server *server, UA_AccessControl *ac,
                                           const UA_NodeId *sessionId, void *sessionContext,
                                           const UA_DeleteReferencesItem *item);

    /**
     * @brief Handle the stop signal.
     *
     * This method is called when a stop signal is received (e.g., SIGINT).
     * It performs necessary cleanup and stops the server operation.
     *
     * @param sign The signal number received.
     */
    void stopHandler(int sign);

    /**
     * @brief Static signal handler for stopping the server.
     *
     * This static method is called when a stop signal is received (e.g., SIGINT).
     * It invokes the instance method stopHandler to perform the cleanup.
     *
     * @param sign The signal number received.
     */
    static void staticSignalHandler(int sign);

    bool initalized_;                               ///< Indicates whether the AmrOpcUAServer has been initialized.
    static volatile UA_Boolean running_;            ///< Tracks the running state of the server; true if running, false if stopped.
    UA_Server *server_ptr_;                         ///< Pointer to the UA_Server instance representing the OPC UA server.
    UA_ServerConfig *config_;                       ///< Pointer to the server configuration (UA_ServerConfig).
    UA_String url_;                                 ///< URL.
    UA_StatusCode retval_;                          ///< Stores the return status code from server operations.
    static std::function<void(int)> signalHandler_; ///< Static function to handle signals for stopping the server.
    std::vector<UA_UsernamePasswordLogin> logins_;  ///< Vector containing user login credentials for the server.

  }; // class AMRController
} // namespace amr_comunication

#endif // __AMR_COMUNICATION_UA_SERVER_H_INCLUDED_