#ifndef _ntp_h__
#define _ntp_h__

/** ntp **
 * 
 * Very simple Network Time Protocol (NTP) client.
 * 
 * This client uses version 4 of the protocol, but implements only a small subset. The
 * only goal is to compute the clock offset and round-trip delay in order to synchronize
 * the timing of Tenkinet device readings with the client's local system clock.
 * 
 * This module performs asynchronous I/O using the `select_helper` module.
 * 
 * Usage:
 * 
 *   1. Initialize the module with ntp_init().
 *   2. Add any number of clients with ntp_client_new().
 *   3. Send queries with ntp_query().
 *   4. Invoke select_helper() to wait for replies.
 *   5. After waking up, call ntp_process() to handle any replies. Callbacks are invoked.
 *   6. Repeat steps 2 to 5 as needed. (No need to re-add clients but you can add new ones.)
 *   7. Terminate the module with ntp_exit().
 * 
 * Each client is associated with a NTP_Client handle.
 * 
 * Query results are expressed in a struct NTP_Result.
 * 
 */

#include <stdint.h>

#include "select_helper.h"

#ifdef __cplusplus
extern "C" {
#endif


/**
 * Default NTP port.
 */
#define NTP_PORT 123

/**
 * Default NTP port as a string.
 */
extern const char NTP_PORT_STR[];

// Status codes
#define NTP_CLIENT_SUCCESS 0
#define NTP_CLIENT_TIMEOUT 1
#define NTP_CLIENT_ERROR 2

/**
 * Result of a NTP query.
 * Members are valid only when status == NTP_CLIENT_SUCCESS.
 */
typedef struct NTP_Result {

    int status;         // status code
    uint64_t delay_us;  // round-trip delay (ping) in microseconds
    int64_t offset_us;  // clock offset in microseconds

} NTP_Result;

/**
 * NTP client handle (opaque type)
 */
typedef void *NTP_Client;

/**
 * Result callback function indicating the success or failure of a query.
 * 
 * The result struct is valid only for the lifetime of the function.
 * Make sure to copy it elsewhere if persistence is required.
 */
typedef void (*ntp_callback_fn)(NTP_Client client, NTP_Result *result, void *user_data);

/**
 * Initialize the NTP module.
 * 
 * This module performs asynchronous I/O using the `select_helper` module.
 */
int ntp_init(select_helper_data *seldata);

/**
 * Perform clean-up and terminate the NTP module.
 * 
 * It is not necessary to delete clients with ntp_client_delete() beforehand.
 */
void ntp_exit();

/**
 * Handle results of each query, including errors and timeouts.
 * 
 * Must be called periodically, ideally after select_helper() has returned.
 * 
 * This function may trigger callbacks, up to one per previous call to ntp_query().
 */
void ntp_process();

/**
 * Create a NTP client for a single server identified by host and port strings.
 * 
 * It is mandatory to provide a callback function.
 * 
 * Arguments:
 * 
 *   host: host name or IP address of the NTP server
 *   port: port of the NTP server, expressed as a string
 *   cb: callback function (mandatory)
 *   user_data: arbitrary data to be passed back to the callback function
 * 
 * Return: client handle, or NULL if the host could not be resolved
 * 
 */
NTP_Client ntp_client_new(const char *host, const char *port, ntp_callback_fn cb, void *user_data);

/**
 * Delete a NTP client from the module.
 * 
 * After calling this function, the handle becomes invalid.
 */
void ntp_client_delete(NTP_Client client);

/**
 * Query using a NTP client.
 * 
 * Arguments:
 * 
 *   client: the NTP client handle
 *   interval_ms: how long to wait (milliseconds) between queries
 *   count: how many packets to send in total
 * 
 * The results will be passed later to your callback function triggered by ntp_process().
 * 
 * On success, the result status is set to NTP_CLIENT_SUCCESS and the computed offset
 * and round-trip delay are stored in the result struct.
 * 
 * The client is considered to have timed out if no reply is received after
 * (count * interval_ms) milliseconds. The result status is set to NTP_CLIENT_TIMEOUT.
 * 
 * If an invalid reply is received, the result status is set to NTP_CLIENT_ERROR.
 */
void ntp_query(NTP_Client client, uint32_t interval_ms, uint32_t count);


#ifdef __cplusplus
}
#endif

#endif
