/**
* Copyright (c) NVIDIA CORPORATION & AFFILIATES, 2001-2013. ALL RIGHTS RESERVED.
* Copyright (C) Huawei Technologies Co., Ltd. 2021.  ALL RIGHTS RESERVED.
*
* See file LICENSE for terms.
*/

#ifndef UCS_LIBSTATS_H_
#define UCS_LIBSTATS_H_

#include <ucs/stats/stats_fwd.h>
#include <ucs/datastruct/list.h>
#include <ucs/type/status.h>
#include <ucs/sys/math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>


/*
 * Serialization options
 */
enum {
    UCS_STATS_SERIALIZE_INACTVIVE = UCS_BIT(0),  /* Use "inactive" tree */
    UCS_STATS_SERIALIZE_BINARY    = UCS_BIT(1),  /* Binary mode */
    UCS_STATS_SERIALIZE_COMPRESS  = UCS_BIT(2)   /* Compress */
};

#define UCS_STATS_DEFAULT_UDP_PORT 37873


#define UCS_STAT_NAME_MAX          39

#define UCS_STATS_NODE_FMT \
    "%s%s"
#define UCS_STATS_NODE_ARG(_node) \
    (_node)->cls->name, (_node)->name

#define UCS_STATS_INDENT(_is_sum, _indent)  _is_sum ? 0 : (_indent) * 2, ""
#define UCS_STATS_IS_LAST_COUNTER(_counters_bits, _current) \
    (_counters_bits > ((2ull<<_current) - 1))

typedef struct ucs_stats_server    *ucs_stats_server_h; /* Handle to server */
typedef struct ucs_stats_client    *ucs_stats_client_h; /* Handle to client */


typedef enum ucs_stats_children_sel {
    UCS_STATS_INACTIVE_CHILDREN,
    UCS_STATS_ACTIVE_CHILDREN,
    UCS_STATS_CHILDREN_LAST
} ucs_stats_children_sel_t;


/* Statistics class */
struct ucs_stats_class {
    const char           *name;
    unsigned             num_counters;
    unsigned             class_id;
    const char*          counter_names[];
};

/*
 * ucs_stats_node is used to hold the counters, their classes and the
 * relationship between them.
 * ucs_stats_filter_node is a data structure used to filter the counters
 * on the report.
 * There are 3 types of filtering: Full, Aggregate, and summary
 * Following is an example of the data structures in aggregate mode:
 *
 *      ucs_stats_node                        ucs_stats_filter_node
 *      --------------                        ---------------------
 *
 *             +-----+                              +-----+
 *             | A-1 |............................> | A-1 |
 *             +-----+                              +-----+
 *               A A A                                A
 *               | | |                                |
 *               | | +----------+                     |
 *               | |            |                     |
 *            +--+ +--+      +--+--+................. |
 *            |       |      | B-1 |                : |
 *            |       |      +-----+                : |
 *            |    +-----+....|..A................. : |
 *            |    | B-2 |    |  |                : : |
 *            |    +-----+ <--+  |                V V |
 *       +-----+......|..........|.............> +-----+
 *       | B-3 |      ||         +---------------| B*  |
 *       +-----+ <----+                          +-----+
 *       cntr1                                   cntr1*
 *       cntr2                                   cntr2*
 *       cntr3                                   cntr3*
 *
 * unfiltered statistics report:
 *
 *  A-1:
 *     B-1:
 *        cntr1: 11111
 *        cntr2: 22222
 *        cntr3: 33333
 *     B-2:
 *        cntr1: 11111
 *        cntr2: 22222
 *        cntr3: 33333
 *     B-3:
 *        cntr1: 11111
 *        cntr2: 22222
 *        cntr3: 33333
 *
 * filtered statistics report:
 *
 *  A-1:
 *     B*:
 *        cntr1: 33333
 *        cntr2: 66666
 *        cntr3: 99999
 *
 */

/* In-memory statistics node */

struct ucs_stats_node {
    ucs_stats_class_t        *cls;               /* Class info */
    ucs_stats_node_t         *parent;            /* Hierarchy structure */
    char                     name[UCS_STAT_NAME_MAX + 1];
                                                 /* instance name */
    ucs_list_link_t          list;               /* nodes sharing same parent */
    ucs_list_link_t          children[UCS_STATS_CHILDREN_LAST];
                                                 /* children list head */
    ucs_list_link_t          type_list;          /* nodes with same class/es
                                                    hierarchy */
    ucs_stats_filter_node_t  *filter_node;       /* ptr to type list head */
    ucs_stats_counter_t      counters[1];        /* instance counters */
};

struct ucs_stats_filter_node {
    ucs_stats_filter_node_t   *parent;
    ucs_list_link_t           list;               /* nodes sharing same parent.*/
    ucs_list_link_t           children;
    ucs_list_link_t           type_list_head;     /* nodes with same ancestors classes */
    int                       type_list_len;      /* length of list */
    int                       ref_count;          /* report node when non zero */
    uint64_t                  counters_bitmask;   /* which counters to print */
};

/**
 * Initialize statistics node.
 *
 * @param node  Node to initialize.
 * @param cls   Node class.
 * @param name  Node name format string.
 * @param ap    Name formatting arguments.
 */
ucs_status_t ucs_stats_node_initv(ucs_stats_node_t *node, ucs_stats_class_t *cls,
                                 const char *name, va_list ap);


/**
 * Serialize statistics.
 *
 * @param stream   Destination
 * @param root     Statistics node root.
 * @param options  Serialization options.
 */
ucs_status_t ucs_stats_serialize(FILE *stream, ucs_stats_node_t *root, int options);


/**
 * De-serialize statistics.
 *
 * @param stream   Source data.
 * @param p_roo    Filled with statistics node root.
 *
 * @return UCS_ERR_NO_ELEM if hit EOF.
 */
ucs_status_t ucs_stats_deserialize(FILE *stream, ucs_stats_node_t **p_root);


/**
 * Release stats returned by ucs_stats_deserialize().
 * @param root     Stats to release.
 */
void ucs_stats_free(ucs_stats_node_t *root);


/**
 * Initialize statistics client.
 *
 * @param server_addr  Address of server machine.
 * @param port         Port number on server.
 * @param p_client     Filled with handle to the client.
 */
ucs_status_t ucs_stats_client_init(const char *server_addr, int port,
                                   ucs_stats_client_h *p_client);


/**
 * Destroy statistics client.
 */
void ucs_stats_client_cleanup(ucs_stats_client_h client);


/**
 * Send statistics.
 *
 * @param client     Client handle.
 * @param root       Statistics tree root.
 * @param timestamp  Current statistics timestamp, identifies every "snapshot".
 */
ucs_status_t ucs_stats_client_send(ucs_stats_client_h client, ucs_stats_node_t *root,
                                  uint64_t timestamp);


/**
 * Start a thread running a server which receives statistics.
 *
 * @param port       Port number to listen on. 0 - random available port.
 * @param verbose    Verbose level.
 * @param p_server   Filled with handle to the server.
 */
ucs_status_t ucs_stats_server_start(int port, ucs_stats_server_h *p_server);


/**
 * Stop statistics server.
 * @param server   Handle to statistics server.
 */
void ucs_stats_server_destroy(ucs_stats_server_h server);


/**
 * Get port number used by the server, useful if we started it on a random port.
 *
 * @param server   Handle to statistics server.
 *
 * @return Port number.
 */
int ucs_stats_server_get_port(ucs_stats_server_h server);


/**
 * Get current statistics gathered by the server. The data is valid until the next
 * call to any of the following functions:
 *  - ucs_stats_server_purge_stats
 *  - ucs_stats_server_cleanup
 *  - ucs_stats_server_get_stats
 *
 * @param server   Handle to statistics server.
 * @return A list of stat trees for all entities gathered by the server.
 */
ucs_list_link_t *ucs_stats_server_get_stats(ucs_stats_server_h server);


/**
 * Clean up existing statistics.
 */
void ucs_stats_server_purge_stats(ucs_stats_server_h server);


/**
 * @return Number of packets received by the server.
 */
unsigned long ucs_stats_server_rcvd_packets(ucs_stats_server_h server);


#endif /* LIBSTATS_H_ */
