/**
 * @file common_types.h
 *
 * Common types that don't fit anywhere else.
 *
 * Copyright © 2014 - 2023 DiffusionData Ltd., All Rights Reserved.
 *
 * Use is subject to licence terms.
 *
 * NOTICE: All information contained herein is, and remains the
 * property of DiffusionData. The intellectual and technical
 * concepts contained herein are proprietary to DiffusionData and
 * may be covered by U.S. and Foreign Patents, patents in process, and
 * are protected by trade secret or copyright law.
 */

#ifndef _diffusion_common_types_h_
#define _diffusion_common_types_h_ 1

#include <stddef.h>

#include "apr.h"
#include "misc/deprecate.h"
#include "types/error_types.h"

/// Constant used in the API to indicate "false".
#define DIFFUSION_FALSE 0

/// Constant used in the API to indicate "true"
#define DIFFUSION_TRUE 1

/**
 * Constant used in the API to indicate that a message or service handler
 * has completed successfully.
 */
#define HANDLER_SUCCESS 0

/**
 * Constant used in the API to indicate that a message or service handler
 * has failed with an error.
 */
#define HANDLER_FAILURE -1

/**
 * Constant defining the V5 service/control topic name.
 */
#define COMMAND_TOPIC_NAME "@"


/**
 * Priority for messages being sent to Diffusion. The default is MEDIUM for
 * most messages.
 *
 * IMMEDIATE priority messages are for system use only, and should not
 * by used by the user.
 */
typedef enum
{
    MESSAGE_PRIORITY_IMMEDIATE = 0,
    MESSAGE_PRIORITY_HIGH = 1,
    MESSAGE_PRIORITY_MEDIUM = 2,
    MESSAGE_PRIORITY_LOW = 3
} MESSAGE_PRIORITY_T;


/**
 * Priority of messages that Diffusion sends to control clients via
 * the send_client() call.
 */
typedef enum
{
    CLIENT_SEND_PRIORITY_NORMAL = 0,
    CLIENT_SEND_PRIORITY_HIGH = 1,
    CLIENT_SEND_PRIORITY_LOW = 2
} CLIENT_SEND_PRIORITY_T;


/**
 * Reasons for session closure.
 */
typedef enum
{
    /**
     * The connection to the client was lost - possibly dropped by the client.
     * Recoverable.
     *
     * A client may be closed for many reasons that are presented as CONNECTION_LOST.
     *
     * During connection the connection can be lost. The server might have received a connection
     * or reconnection request from a client already connected.
     * The server might have received a reconnection request without a session Id.
     * The connection may not have been authorised because the credentials are wrong.
     * The maximum number of clients might already be connected.
     *
     * Once connected the connection can be lost for different reasons.
     * If the client closes its connection while the server is writing a message to the client.
     * With the chunked encoding based connection the HTTP response is completed by the server.
     * If the client does not open a new request within a timeout the client will be closed.
     * If a poll request times out and the server finds that the connection has already been
     * closed by the client.
     */
    SESSION_CLOSE_REASON_CONNECTION_LOST = 0,

    /**
     * An unexpected IO Exception occurred.
     * Recoverable.
     *
     * While trying to perform an I/O operation an exception was generated.
     * This often means that a read was attempted from a closed TCP connection.
     *
     * When handling SSL connections if there is a problem encrypting or decrypting
     * a message the client will be closed for this reason.
     */
    SESSION_CLOSE_REASON_IO_EXCEPTION = 1,

    /**
     *  The client had become unresponsive.
     *
     * The client has either failed to respond to a ping message in a timely
     * manner or the client has failed to open an HTTP poll for messages.
     * The client does not appear to be receiving messages.
 	 */
    SESSION_CLOSE_REASON_CLIENT_UNRESPONSIVE = 2,

    /**
     * The maximum outbound queue size was reached for the client.
     * Not recoverable.
     *
     * Messages sent to the client are placed in a queue.
     * This queue has a maximum allowed size.
     * If the queue limit is reached the client is closed and the queue discarded.
     * The queue is intended to protect against slow consumers, reaching the queue
     * limit is taken to mean that the client cannot keep up with the number of
     * messages sent to it.
     */
    SESSION_CLOSE_REASON_MESSAGE_QUEUE_LIMIT_REACHED = 3,

    /**
     * The client requested close.
     * Not recoverable.
     */
    SESSION_CLOSE_REASON_CLOSED_BY_CLIENT = 4,

    /**
     * The client sent a message that exceeded the maximum message size that
     * can be processed by the server.
     *
     * The server has a maximum message size that it can process on any single connector.
     * If a client sends a message larger than this the server is unable to process it.
     * When this happens the message is discarded and the client is closed.
     */
    SESSION_CLOSE_REASON_MESSAGE_TOO_LARGE = 5,

     /**
     * An internal error occurred.
     *
     * An unexpected internal error has occurred.
     * The server logs will contain more detail.
     */
    SESSION_CLOSE_REASON_INTERNAL_ERROR = 6,

     /**
     * An inbound message with an invalid format was received.
     *
     * A message received by the server is not a valid Diffusion message.
     * The server is unable to process this and closes the client that sent it.
     */
    SESSION_CLOSE_REASON_INVALID_INBOUND_MESSAGE = 7,

    /**
     * The client connection was aborted by the server, possibly because the
     * connection was disallowed.
     *
     * This is may be because the connection was disallowed.
     * Abort messages are also sent to clients that have unrecognised client IDs.
     * This may be because the server closed the client previously but the client is
     * unaware of this and tried to continue interacting with the server.
     */
    SESSION_CLOSE_REASON_ABORTED = 8,

    /**
     * Loss of messages from the client has been detected.
     * For example, whilst waiting for the arrival of missing messages in a sequence of
     * messages a timeout has occurred.
     *
     * HTTP based transports use multiple TCP connections.
     * This can cause the messages to be received out of order.
     * To reorder the messages those sent to the server may contain a sequence number
     * indicating the correct order.
     *
     * If a message is received out of order there is a short time for the earlier messages
     * to be received.
     * If the messages are not received in this time the client is closed.
     *
     * Missing, invalid or duplicate sequence numbers will also close the client for this reason.
     *
     * This cannot be recovered from as the client and the server are in inconsistent states.
     */
    SESSION_CLOSE_REASON_LOST_MESSAGES = 9,

    /**
     * Closed by a client session.
     *
     * A control session initiated the client close.
     */
    SESSION_CLOSE_REASON_CLOSED_BY_CONTROLLER = 11,

     /**
     * The session has failed over to a different Diffusion server.
     *
     * The session is still open but is now connected to a different Diffusion server.
     * This server has evicted its view of the session from its set of local sessions.
     *
     * @since 5.8
     */
    SESSION_CLOSE_REASON_FAILED_OVER = 12,

    /**
     * The session had an EXPIRY_TIME specified which expired before the session re-authenticated.
     *
     * @since 6.12
     */
    SESSION_CLOSE_REASON_EXPIRED = 13,

    /**
     * The session's authentication was revoked by a privileged user.
     *
     * @since 6.12
     */
    SESSION_CLOSE_REASON_REVOKED = 14,

    /**
     * The session has been closed to make way for a new session.
     *
     * This can only occur for an MQTT session.
     *
     * @since 6.12
     */
    SESSION_CLOSE_REASON_SESSION_TAKEN_OVER = 15
} SESSION_CLOSE_REASON_T;


typedef enum
{
    SUCCESS = 0,
    RETRIABLE = 1,
    FATAL = 2,
    FATAL_AUTHENTICATION = 3
} CONNECTION_RESPONSE_CODE_TYPE_T;


/**
 * Connection response codes.
 */
typedef enum
{
    CONNECTION_RESPONSE_CODE_OK = 100,
    CONNECTION_RESPONSE_CODE_DOWNGRADE = 102,
    CONNECTION_RESPONSE_CODE_RECONNECTED = 105,
    CONNECTION_RESPONSE_CODE_RECONNECTED_WITH_MESSAGE_LOSS = 106,
    CONNECTION_RESPONSE_CODE_LICENSE_EXCEEDED = 113,
    CONNECTION_RESPONSE_CODE_RECONNECTION_UNSUPPORTED = 114,
    CONNECTION_RESPONSE_CODE_CONNECTION_PROTOCOL_ERROR = 115,
    CONNECTION_RESPONSE_CODE_AUTHENTICATION_FAILED = 116,
    CONNECTION_RESPONSE_CODE_UNKNOWN_SESSION = 117,
    CONNECTION_RESPONSE_CODE_RECONNECTION_FAILED_MESSAGE_LOSS = 118,
    CONNECTION_RESPONSE_CODE_REJECTED_SERVER_INITIALISING = 119,
} CONNECTION_RESPONSE_CODE_T;


typedef struct
{
    const CONNECTION_RESPONSE_CODE_T code;
    const char *msg;
    const CONNECTION_RESPONSE_CODE_TYPE_T type;
    const ERROR_CODE_T diffusion_error_code;
} CONNECTION_RESPONSE_T;


static const CONNECTION_RESPONSE_T connectionResponseList[] = {
    {CONNECTION_RESPONSE_CODE_OK, "Connected successfully", SUCCESS, DIFF_ERR_SUCCESS},
    {CONNECTION_RESPONSE_CODE_DOWNGRADE,
     "Client version rejected : Client should be downgraded to use server compatible version or server upgraded to client version in use",
     FATAL,
     DIFF_ERR_DOWNGRADE},
    {CONNECTION_RESPONSE_CODE_RECONNECTED, "Reconnected successfully", SUCCESS, DIFF_ERR_SUCCESS},
    {
        CONNECTION_RESPONSE_CODE_RECONNECTED_WITH_MESSAGE_LOSS,
        "Reconnected with message loss",
        SUCCESS,
        DIFF_ERR_SUCCESS,
    },
    {CONNECTION_RESPONSE_CODE_LICENSE_EXCEEDED, "Connection rejected due to license limit", FATAL, DIFF_ERR_LICENCE_EXCEEDED},
    {CONNECTION_RESPONSE_CODE_RECONNECTION_UNSUPPORTED,
     "Reconnection not supported by connector",
     FATAL,
     DIFF_ERR_RECONNECTION_UNSUPPORTED},
    {CONNECTION_RESPONSE_CODE_CONNECTION_PROTOCOL_ERROR, "Connection failed - protocol error", FATAL, DIFF_ERR_CONNECTION_PROTOCOL_ERROR},
    {CONNECTION_RESPONSE_CODE_AUTHENTICATION_FAILED, "Authentication failed", FATAL_AUTHENTICATION, DIFF_ERR_AUTHENTICATION_FAILED},
    {CONNECTION_RESPONSE_CODE_UNKNOWN_SESSION, "Reconnection failed - the session is unknown", FATAL, DIFF_ERR_UNKNOWN_SESSION},
    {CONNECTION_RESPONSE_CODE_RECONNECTION_FAILED_MESSAGE_LOSS, "Reconnection failed due to message loss", FATAL, DIFF_ERR_MESSAGE_LOSS},
    {CONNECTION_RESPONSE_CODE_REJECTED_SERVER_INITIALISING,
     "Connection rejected because the server is not ready",
     RETRIABLE,
     DIFF_ERR_REJECTED_SERVER_INITIALIZING},
    // Terminator - NULL description - other values ignored.
    {CONNECTION_RESPONSE_CODE_OK, NULL, SUCCESS, DIFF_ERR_SUCCESS}};

#endif
