/*
 * FreeRTOS Defender V3.0.1
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/**
 * @file aws_iot_defender.h
 * @brief User-facing functions and structs of AWS IoT Device Defender library.
 */

#ifndef AWS_IOT_DEFENDER_H_
#define AWS_IOT_DEFENDER_H_

/* The config header is always included first. */
#include "iot_config.h"

/* Standard includes. */
#include <stdint.h>
#include <stdlib.h>

/* MQTT include. */
#include "iot_mqtt.h"

/**
 * @page Defender_constants Constants
 * @brief Defined constants of the Defender library.
 * - @ref DefenderFormat "Serialization Format"
 * - @ref DefenderMetricsFlags "Metrics Flags"
 * - @ref DefenderInitializers "Initializers"
 */

/**
 * @anchor DefenderFormat
 * @name Serialization Format
 *
 * @brief Format constants: Cbor or Json.
 * @warning JSON format is not supported for now.
 */
/**@{ */
#define AWS_IOT_DEFENDER_FORMAT_CBOR    1                                      /**< CBOR format. */
#define AWS_IOT_DEFENDER_FORMAT_JSON    2                                      /**< JSON format (NOT supported). */
/**@} */

/**
 * @anchor DefenderMetricsFlags
 * @name Metrics Flags
 *
 * @brief Bit flags or metrics used by @ref defender_function_setmetrics function.
 *
 * These metrics are subset of metrics supported by AWS IoT Device Defender service. For details,
 * refer to developer document of [AWS IoT Device Defender](https://docs.aws.amazon.com/iot/latest/developerguide/device-defender-detect.html#DetectMetricsMessages).
 */
/**@{ */
#define AWS_IOT_DEFENDER_METRICS_ALL                                        0xffffffff /**< Flag to indicate including all metrics. */

#define AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_TOTAL          0x00000001 /**< Total count of established TCP connections. */
#define AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_REMOTE_ADDR    0x00000004 /**< Remote address (IP:port) of established TCP connections. For example, 192.168.0.1:8000. */

/**
 * Connections metrics including only remote address. Local port number is not supported.
 *
 */
#define AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_CONNECTIONS \
    ( AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_REMOTE_ADDR ) \


/**
 * Established connections metrics including connections metrics and total count.
 */
#define AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED                                                                          \
    ( AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_CONNECTIONS | AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_TOTAL ) \

/**@} end of DefenderMetricsFlags */

/**
 * @anchor DefenderInitializers
 * @name Initializers
 *
 * @brief Initializers of data handles.
 */
/**@{ */
#define AWS_IOT_DEFENDER_CALLBACK_INITIALIZER \
    {                                         \
        .pCallbackContext = NULL,             \
        .function = NULL                      \
    } /**< Initializer of #AwsIotDefenderCallback_t. */
#define AWS_IOT_DEFENDER_START_INFO_INITIALIZER          \
    { .pClientIdentifier = NULL,                         \
      .clientIdentifierLength = 0,                       \
      .mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER, \
      .callback = AWS_IOT_DEFENDER_CALLBACK_INITIALIZER  \
    } /**< Initializer of #AwsIotDefenderStartInfo_t. */
/**@} */

/**
 * @defgroup Defender_datatypes_enums Enumerated types
 * @brief Enumerated types of the Defender library.
 *
 * @defgroup Defender_datatypes_paramstructs Parameter structures
 * @brief Structures passed as parameters to [Defender functions](@ref defender_functions)
 *
 * These structures are passed as parameters to library functions.
 * Documentation for these structures will state the functions associated with each parameter structure and the purpose of each member.
 */

/**
 * @ingroup Defender_datatypes_enums
 * @brief Metrics group options for AwsIotDefender_SetMetrics() function.
 *
 * Metrics group is defined based on the "metrics blocks" listed in [AWS IoT Defender document]
 * (https://docs.aws.amazon.com/iot/latest/developerguide/device-defender-detect.html#DetectMetricsMessages)
 */
typedef enum
{
    AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS, /**< TCP connection metrics group. */
} AwsIotDefenderMetricsGroup_t;

/**
 * @ingroup Defender_datatypes_enums
 *  @brief Return codes of defender functions.
 */
typedef enum
{
    AWS_IOT_DEFENDER_SUCCESS = 0,      /**< Defender operation completed successfully. */
    AWS_IOT_DEFENDER_INVALID_INPUT,    /**< At least one input parameter is invalid. */
    AWS_IOT_DEFENDER_ALREADY_STARTED,  /**< Defender has been already started. */
    AWS_IOT_DEFENDER_PERIOD_TOO_SHORT, /**< Given period is too short. */
    AWS_IOT_DEFENDER_ERROR_NO_MEMORY,  /**< Defender operation failed due to memory allocation failure. */
    AWS_IOT_DEFENDER_INTERNAL_FAILURE  /**< Defender operation failed due to internal unexpected cause. */
} AwsIotDefenderError_t;

/**
 * @ingroup Defender_datatypes_enums
 * @brief Event codes passed into AwsIotDefenderCallbackInfo_t
 */
typedef enum
{
    AWS_IOT_DEFENDER_METRICS_ACCEPTED = 0,  /**< Metrics report was accepted by defender service. */
    AWS_IOT_DEFENDER_METRICS_REJECTED,      /**< Metrics report was rejected by defender service. */
    AWS_IOT_DEFENDER_FAILURE_MQTT,          /**< Defender failed to perform MQTT operation. */
    AWS_IOT_DEFENDER_FAILURE_METRICS_REPORT /**< Defender failed to create metrics report. */
} AwsIotDefenderEventType_t;

/**
 * @defgroup Defender_datatypes_paramstructs Parameter structures
 * @brief Structures passed as parameters to [Defender functions](@ref defender_functions)
 *
 * These structures are passed as parameters to library functions.
 * Documentation for these structures will state the functions associated with each parameter structure and the purpose of each member.
 */

/**
 * @ingroup Defender_datatypes_paramstructs
 * @brief Callback parameters.
 */
typedef struct AwsIotDefenderCallbackInfo
{
    const uint8_t * pMetricsReport;      /**< The published metrics report(could be NULL). */
    size_t metricsReportLength;          /**< Length of the published metrics report. */
    const uint8_t * pPayload;            /**< The received message if there is one(could be NULL). */
    size_t payloadLength;                /**< Length of the message. */
    AwsIotDefenderEventType_t eventType; /**< Event code(always valid). */
} AwsIotDefenderCallbackInfo_t;

/**
 * @ingroup Defender_datatypes_paramstructs
 * @brief User provided callback handle.
 */
typedef struct AwsIotDefenderCallback
{
    void * pCallbackContext;                                     /**< The callback context for caller's use (optional). */
    void ( * function )( void *,
                         AwsIotDefenderCallbackInfo_t * const ); /**< Callback function signature(optional). */
} AwsIotDefenderCallback_t;

/**
 * @ingroup Defender_datatypes_paramstructs
 * @brief Parameters of AwsIotDefender_Start function.
 */
typedef struct AwsIotDefenderStartInfo
{
    const char * pClientIdentifier;     /**< @brief MQTT client identifier (required). */
    uint16_t clientIdentifierLength;    /**< @brief Length of #IotMqttConnectInfo_t.pClientIdentifier (required). */
    IotMqttConnection_t mqttConnection; /**< @brief MQTT connection used by defender (required). */
    AwsIotDefenderCallback_t callback;  /**< Callback function parameter (optional). */
} AwsIotDefenderStartInfo_t;

/**
 * @functions_page{defender,Device Defender library}
 * - @function_name{defender_function_setmetrics}
 * @function_brief{defender_function_setmetrics}
 * - @function_name{defender_function_start}
 * @function_brief{defender_function_start}
 * - @function_name{defender_function_stop}
 * @function_brief{defender_function_stop}
 * - @function_name{defender_function_setperiod}
 * @function_brief{defender_function_setperiod}
 * - @function_name{defender_function_getperiod}
 * @function_brief{defender_function_getperiod}
 * - @function_name{defender_function_strerror}
 * @function_brief{defender_function_strerror}
 * - @function_name{defender_function_EventType}
 * @function_brief{defender_function_EventType}
 */

/**
 * @function_page{AwsIotDefender_SetMetrics,defender,setmetrics}
 * @function_snippet{defender,setmetrics,this}
 * @copydoc AwsIotDefender_SetMetrics
 * @function_page{AwsIotDefender_Start,defender,start}
 * @function_snippet{defender,start,this}
 * @copydoc AwsIotDefender_Start
 * @function_page{AwsIotDefender_Stop,defender,stop}
 * @function_snippet{defender,stop,this}
 * @copydoc AwsIotDefender_Stop
 * @function_page{AwsIotDefender_SetPeriod,defender,setperiod}
 * @function_snippet{defender,setperiod,this}
 * @copydoc AwsIotDefender_SetPeriod
 * @function_page{AwsIotDefender_GetPeriod,defender,getperiod}
 * @function_snippet{defender,getperiod,this}
 * @copydoc AwsIotDefender_GetPeriod
 * @function_page{AwsIotDefender_strerror,defender,strerror}
 * @function_snippet{defender,strerror,this}
 * @copydoc AwsIotDefender_strerror
 * @function_page{AwsIotDefender_EventType,defender,EventType}
 * @function_snippet{defender,EventType,this}
 * @copydoc AwsIotDefender_EventType
 */

/**
 * @brief Set metrics that defender agent needs to collect for a metrics group.
 *
 * * If defender agent is not started, this function will provide the metrics to be collected.
 * * If defender agent is started, this function will update the metrics and take effect in defender agent's next iteration.
 *
 * @param[in] metricsGroup Metrics group defined in #AwsIotDefenderMetricsGroup_t
 * @param[in] metrics Bit-flags to specify what metrics to collect.
 * If all metrics in a group is needed, simply set metrics to #AWS_IOT_DEFENDER_METRICS_ALL.
 * See @ref DefenderMetricsFlags "Metrics flags" for details.
 *
 * @return
 * * On success, #AWS_IOT_DEFENDER_SUCCESS is returned.
 * * If metricsGroup is invalid, #AWS_IOT_DEFENDER_INVALID_INPUT is returned.
 * @note This function is thread safe.
 * @note @ref defender_function_stop will clear the metrics.
 */

/* @[declare_defender_setmetrics] */
AwsIotDefenderError_t AwsIotDefender_SetMetrics( AwsIotDefenderMetricsGroup_t metricsGroup,
                                                 uint32_t metrics );
/* @[declare_defender_setmetrics] */

/**
 * @brief Start the defender agent.
 *
 * @param[in] pStartInfo Pointer of parameters of start function
 *
 * Periodically, defender agent collects metrics and publish to specifc AWS reserved MQTT topic.
 *
 * @return
 * * On success, #AWS_IOT_DEFENDER_SUCCESS is returned.
 * * If pStartInfo is invalid, #AWS_IOT_DEFENDER_INVALID_INPUT is returned.
 * * If memory allocation fails, #AWS_IOT_DEFENDER_ERROR_NO_MEMORY is returned.
 * * If defender is already started, #AWS_IOT_DEFENDER_ALREADY_STARTED is returned.
 *
 * @warning This function is not thread safe.
 *
 * @note No need to manage the memory allocated for #AwsIotDefenderCallbackInfo_t. This function save the information internally.
 *
 * Example:
 *
 * @code{c}
 *
 * // assume valid IotMqttConnection_t is created and available.
 * const IotMqttConnection_t _mqttConnection;
 * // use AWS thing name as client identifier
 * const char * pClientIdentifier = "AwsThingName";
 *
 * void logDefenderCallback( void * param1, AwsIotDefenderCallbackInfo_t * const pCallbackInfo )
 * {
 *     if ( pCallbackInfo->eventType == AWS_IOT_DEFENDER_METRICS_ACCEPTED )
 *     {
 *         // log info: metrics report accepted by defender service is a happy case
 *     }
 *     else
 *     {
 *         // log error: pCallbackInfo->eventType
 *     }
 *
 *     if ( pCallbackInfo->pPayload != NULL )
 *     {
 *         // log info: pCallbackInfo->pPayload with length pCallbackInfo->payloadLength
 *     }
 *
 *     if ( pCallbackInfo->pMetricsReport != NULL )
 *     {
 *         // log info: pCallbackInfo->pMetricsReport with length pCallbackInfo->metricsReportLength
 *     }
 * }
 *
 * void startDefender()
 * {
 *     // define a simple callback function which simply logs
 *     const AwsIotDefenderCallback_t callback = { .function = logDefenderCallback, .pCallbackContext = NULL };
 *
 *     // define parameters of AwsIotDefender_Start function
 *     // Note: This example assumes MQTT connection is already established and metrics library is initialized.
 *     const AwsIotDefenderStartInfo_t startInfo =
 *     {
 *         .pClientIdentifier = pClientIdentifier,
 *         .clientIdentifierLength = strlen( pClientIdentifier ),
 *         .mqttConnection = _mqttConnection,
 *         .callback = callback
 *     };
 *
 *     // specify two TCP connections metrics: total count and local port
 *     AwsIotDefenderError_t error = AwsIotDefender_SetMetrics(AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS,
 *                                                             AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_TOTAL |
 *                                                             AWS_IOT_DEFENDER_METRICS_TCP_CONNECTIONS_ESTABLISHED_REMOTE_ADDR );
 *
 *     if ( error == AWS_IOT_DEFENDER_SUCCESS )
 *     {
 *         // set metrics report period to 10 minutes (600 seconds)
 *         error = AwsIotDefender_SetPeriod( 600 );
 *     }
 *
 *     if (error == AWS_IOT_DEFENDER_SUCCESS)
 *     {
 *         // start the defender
 *         error = AwsIotDefender_Start( &startInfo );
 *     }
 *
 *     if ( error != AWS_IOT_DEFENDER_SUCCESS )
 *     {
 *         const char * pError = AwsIotDefender_strerror( error );
 *         // log error: pError
 *     }
 * }
 *
 * void stopDefender()
 * {
 *     //stop the defender
 *     AwsIotDefender_Stop();
 * }
 *
 * @endcode
 */
/* @[declare_defender_start] */
AwsIotDefenderError_t AwsIotDefender_Start( AwsIotDefenderStartInfo_t * pStartInfo );
/* @[declare_defender_start] */

/**
 * @brief Stop the defender agent.
 *
 * It waits for the current metrics-publishing iteration to finish before freeing the resource allocated.
 * It also clears the metrics set previously so that user is expected to SetMetrics again before restarting defender agent.
 *
 * @warning This function must be called after successfully calling @ref defender_function_start.
 * @warning This function is not thread safe.
 */
/* @[declare_defender_stop] */
void AwsIotDefender_Stop( void );
/* @[declare_defender_stop] */

/**
 * @brief Set period in seconds.
 *
 *
 * @param[in] periodSeconds Period is specified in seconds. Mininum is 300 (5 minutes)
 *
 * @return
 * * On success, #AWS_IOT_DEFENDER_SUCCESS is returned.
 * * If defender is not started yet, AWS_IOT_DEFENDER_NOT_STARTED is returned.
 *
 * @warning This function is not thread safe.
 *
 * @note If this function is called when defender agent is started, the period is re-calculated and updated in next iteration.
 */
/* @[declare_defender_setperiod] */
AwsIotDefenderError_t AwsIotDefender_SetPeriod( uint32_t periodSeconds );
/* @[declare_defender_setperiod] */

/**
 * @brief Get period in seconds.
 *
 * @return Current period in seconds
 */
/* @[declare_defender_getperiod] */
uint32_t AwsIotDefender_GetPeriod( void );
/* @[declare_defender_getperiod] */

/**
 * @brief Return a string that describes #AwsIotDefenderError_t
 *
 * @return A string that describes given #AwsIotDefenderError_t
 */
/* @[declare_defender_strerror] */
const char * AwsIotDefender_strerror( AwsIotDefenderError_t error );
/* @[declare_defender_strerror] */

/**
 * @brief Return a string that describes #AwsIotDefenderEventType_t
 *
 * @return A string that describes given #AwsIotDefenderEventType_t
 */
/* @[declare_defender_EventType] */
const char * AwsIotDefender_EventType( AwsIotDefenderEventType_t eventType );
/* @[declare_defender_EventType] */
#endif /* AWS_IOT_DEFENDER_H_ */
