/*
 * This file is part of the Owl Positioning System (OwlPS) project.
 * It is subject to the copyright notice and license terms in the
 * COPYRIGHT.t2t file found in the top-level directory of this
 * distribution and at
 *   https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t
 * No part of the OwlPS Project, including this file, may be copied,
 * modified, propagated, or distributed except according to the terms
 * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be
 * distributed along with this file, either separately or by replacing
 * this notice by the COPYRIGHT.t2t file's contents.
 */

/**
 * @file owlps.h
 * @brief OwlPS library
 *
 * libowlps is the library containing the code shared by most of the
 * OwlPS programs.
 */


#ifndef _LIBOWLPS_H_
#define _LIBOWLPS_H_

#ifdef __cplusplus
# include <cstdint>
#else // __cplusplus
# include <stdint.h>
#endif // __cplusplus

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus


#include <stdbool.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>


/** @name Network ports */
//@{
/// Port on which the positioning and calibration requests are sent by
/// the mobiles (or the listeners for autocalibration requests)
#define OWL_DEFAULT_REQUEST_PORT 9900
/// Port on which listeners and aggregator communicate
#define OWL_DEFAULT_LISTENER_PORT 9901
/// Port on which Aggregator and Positioner communicate
#define OWL_DEFAULT_AGGREGATION_PORT 9902
/// Port on which the aggregator listens for hello messages from the
/// listeners
#define OWL_DEFAULT_AUTOCALIBRATION_HELLO_PORT 9903
/// Port on which the listeners listen for orders from the aggregator
#define OWL_DEFAULT_AUTOCALIBRATION_ORDER_PORT 9904
/// Port on which the mobile listens for its position
#define OWL_DEFAULT_RESULT_PORT 9910
//@}


/** @name Miscellaneous informations */
//@{
#define OWL_LATEST_AGGREGATION_CSV_FORMAT 1
//@}


/// Directions
enum owl_directions
{
  owl_north = 1, ///< North direction
  owl_east, ///< East direction
  owl_south, ///< South direction
  owl_west ///< West direction
} ;
/// First [direction](@ref owl_direction)
#define OWL_DIRECTION_MIN 1
/// Last [direction](@ref owl_direction)
#define OWL_DIRECTION_MAX 4
/// Direction type
typedef uint8_t owl_direction ;


/// Timestamp structure
/**
 * This timestamp structure is a clone of `struct timespec` (from
 * `time.h`), but with fixed-size fields.
 */
struct _owl_timestamp
{
  uint32_t tv_sec ; ///< Seconds
  uint32_t tv_nsec ; ///< Nanoseconds
} ;
/// Timestamp type
typedef struct _owl_timestamp owl_timestamp ;

/** @name String lengths */
//@{
/// Length of a owl_timestamp when converted to string
/**
 * 22 = 10 digits, '.', 10 digits, '\0'
 */
#define OWL_TIMESTAMP_STRLEN 22
//@}


/// Message sent by the Listener to the Aggregator
/**
 * Message sent by the Listener to the Aggregator for each captured
 * request.
 *
 * TODO: This structure's size could be reduced from 60 to 52 byte by reordering
 * the fields by size.
 */
struct _owl_captured_request
{
  uint8_t type ; ///< Type of the captured request
  unsigned char __pad0; // 1 byte alignment
  uint16_t nb_packets ; ///< Number of packets for this request
  uint16_t packet_id ; ///< Number of the current packet
  uint8_t cp_mac_addr_bytes[ETHER_ADDR_LEN] ; ///< MAC of the listener
  uint8_t mobile_mac_addr_bytes[ETHER_ADDR_LEN] ; ///< MAC of the mobile
  uint8_t mobile_ip_addr_bytes[4] ; ///< IP of the mobile
  unsigned char __pad1[2]; // 2 bytes alignment
  owl_timestamp request_time ; ///< Timestamp on the mobile
  owl_timestamp capture_time ; ///< Timestamp of arrival on the listener
  int8_t ss_dbm ; ///< Signal strength measured by the listener (dBm)
  unsigned char __pad2[3]; // 3 bytes alignment
  /* Calibration data */
  float x_position ; ///< X coordinate
  float y_position ; ///< Y coordinate
  float z_position ; ///< Z coordinate
  owl_direction direction ; ///< Orientation of the mobile
  unsigned char __pad3[3]; // 3 bytes alignment
} ;
/// Captured request type
typedef struct _owl_captured_request owl_captured_request ;


/// Message sent by the Aggregator to the Positioner (request's data)
/**
 * Message sent by the Aggregator to the Positioner containing
 * the main data of a request.
 *
 * TODO: This structure's size could be reduced from 44 to 36 bytes by
 * reordering the fields by size.
 */
struct _owl_request
{
  uint8_t type ; ///< Type of the request
  unsigned char __pad0; // 1 byte alignment
  uint16_t nb_packets ; ///< Number of packets sent for this request
  uint8_t mobile_mac_addr_bytes[ETHER_ADDR_LEN] ; ///< MAC of the mobile
  uint8_t mobile_ip_addr_bytes[4] ; ///< IP of the mobile
  unsigned char __pad1[2]; // 2 bytes alignment
  owl_timestamp request_time ; ///< Timestamp on the mobile
  uint16_t nb_info ; ///< Number of owl_request_info
  unsigned char __pad2[2]; // 2 bytes alignment
  /* Calibration data */
  float x_position ; ///< X coordinate
  float y_position ; ///< Y coordinate
  float z_position ; ///< Z coordinate
  owl_direction direction ; ///< Orientation of the mobile
  unsigned char __pad3[3]; // 3 bytes alignment
} ;
/// Aggregated request type
typedef struct _owl_request owl_request ;


/// Message sent by the Aggregator to the Positioner (signal strength)
/**
 * Message sent by the Aggregator to the Positioner refering to
 * a request, indicating that a CP received the request with a given
 * signal strength.
 */
struct _owl_request_info
{
  uint16_t packet_id ; ///< Number of the current packet
  uint8_t cp_mac_addr_bytes[ETHER_ADDR_LEN] ; ///< MAC of the listener
  owl_timestamp capture_time ; ///< Timestamp of arrival on the listener
  int8_t ss_dbm ; ///< Signal strength measured by the listener (dBm)
  unsigned char __pad0[3]; // 3 bytes alignment
} ;
/// Aggregated request information type
typedef struct _owl_request_info owl_request_info ;


/// *Hello* message sent by the Listener to the Aggregator
/**
 * This structure represents a *hello* message sent to the Aggregator by
 * each Listener to signal its presence.
 */
struct _owl_autocalibration_hello
{
  /// MAC address of the Listener
  uint8_t cp_mac_addr_bytes[ETHER_ADDR_LEN] ;
} ;
/// *Hello* message type
typedef struct _owl_autocalibration_hello owl_autocalibration_hello ;


/// Message sent to the Listener to order an emission
/**
 * This structure represents a message sent by the Aggregator to a
 * Listener to order it to send an autocalibration request.
 */
struct _owl_autocalibration_order
{
  uint8_t order ; ///< Code of the order
} ;
/// Autocalibration order type
typedef struct _owl_autocalibration_order owl_autocalibration_order ;
/** @name Autocalibration order type identifiers */
//@{
#define AUTOCALIBRATION_ORDER_SEND 1
//@}


/** @name Request type identifiers */
//@{
/// "Normal" positioning request
#define OWL_REQUEST_NORMAL 0
/// Calibration request
#define OWL_REQUEST_CALIBRATION 1
/// Autocalibration request
#define OWL_REQUEST_AUTOCALIBRATION 2
/// Generated request (in Positioner)
#define OWL_REQUEST_GENERATED 3
/// Implicit positioning request
#define OWL_REQUEST_IMPLICIT 10
/// Undefined or unknown request type
#define OWL_REQUEST_UNDEFINED 255
//@}
/* Don't forget to update the OWL_IS_REQUEST_TYPE macro below when you
 * change something here! */


/** @name Wi-Fi channels' frequencies in MHz */
//@{
#define OWL_80211_MHZ_CHANNEL_1  2412
#define OWL_80211_MHZ_CHANNEL_2  2417
#define OWL_80211_MHZ_CHANNEL_3  2422
#define OWL_80211_MHZ_CHANNEL_4  2427
#define OWL_80211_MHZ_CHANNEL_5  2432
#define OWL_80211_MHZ_CHANNEL_6  2437
#define OWL_80211_MHZ_CHANNEL_7  2442
#define OWL_80211_MHZ_CHANNEL_8  2447
#define OWL_80211_MHZ_CHANNEL_9  2452
#define OWL_80211_MHZ_CHANNEL_10 2457
#define OWL_80211_MHZ_CHANNEL_11 2462
#define OWL_80211_MHZ_CHANNEL_12 2467
#define OWL_80211_MHZ_CHANNEL_13 2472
#define OWL_80211_MHZ_CHANNEL_14 2477
//@}

/** @name Wi-Fi channels' frequencies in Hz */
//@{
#define OWL_80211_HZ_CHANNEL_1  2412000000ul
#define OWL_80211_HZ_CHANNEL_2  2417000000ul
#define OWL_80211_HZ_CHANNEL_3  2422000000ul
#define OWL_80211_HZ_CHANNEL_4  2427000000ul
#define OWL_80211_HZ_CHANNEL_5  2432000000ul
#define OWL_80211_HZ_CHANNEL_6  2437000000ul
#define OWL_80211_HZ_CHANNEL_7  2442000000ul
#define OWL_80211_HZ_CHANNEL_8  2447000000ul
#define OWL_80211_HZ_CHANNEL_9  2452000000ul
#define OWL_80211_HZ_CHANNEL_10 2457000000ul
#define OWL_80211_HZ_CHANNEL_11 2462000000ul
#define OWL_80211_HZ_CHANNEL_12 2467000000ul
#define OWL_80211_HZ_CHANNEL_13 2472000000ul
#define OWL_80211_HZ_CHANNEL_14 2477000000ul
//@}


/** @name String lengths */
//@{
/// Length of a MAC address in string format (including '\0')
#define OWL_ETHER_ADDR_STRLEN 18
/// Maximum length of an algorithm name (including '\0')
#define OWL_ALGORITHM_STRLEN 31
/// Maximum length of an area name (including '\0')
#define OWL_AREA_STRLEN 31
/// Maximum length of a coordinate X, Y or Z (including '\0')
#define OWL_COORDINATE_STRLEN 16
//@}


/* Global variables */
/// Global variable used to handle end of loops
extern bool owl_run ;


/** @name Error codes */
//@{
/// Wrong program invokation (command-line arguments)
#define OWL_ERR_BAD_USAGE 100
/// Error when reading/parsing the configuration file
#define OWL_ERR_CONFIG_FILE 101
/* System */
/// Error when creating a thread
#define OWL_ERR_THREAD_CREATE 110
/// Wrong signal received
#define OWL_ERR_BAD_SIGNAL 111
/* Network communication */
/// Error when creating a socket
#define OWL_ERR_SOCKET_CREATE 120
/// Error when sending a message on a socket
#define OWL_ERR_SOCKET_SEND 121
/// Error when reading from a socket
#define OWL_ERR_SOCKET_RECV 122
/* Network interface / capture */
/// Error when opening the capture interface
#define OWL_ERR_IFACE_PCAP_OPEN 130
/// Error when reading the interface Wi-Fi mode
#define OWL_ERR_IFACE_MODE_GET 131
/// Error when setting the interface Wi-Fi mode
#define OWL_ERR_IFACE_MODE_SET 132
//@}


/* Macros */

/// Converts a bool to the corresponding string
#define OWL_BOOL_TO_STRING(B) ((B) ? "true" : "false")

/// Make sure the given integer is a valid request type
/**
 * True if `I` is a valid request type, false otherwise.
 * Do *not* pass an incremented number to this macro (e.g. type++ or
 * ++type). This shouldn't be a problem with a request type, though.
 */
#define OWL_IS_REQUEST_TYPE(I)                                          \
  (   (I) == OWL_REQUEST_NORMAL                                         \
   || (I) == OWL_REQUEST_CALIBRATION                                    \
   || (I) == OWL_REQUEST_AUTOCALIBRATION                                \
   || (I) == OWL_REQUEST_GENERATED                                      \
   || (I) == OWL_REQUEST_IMPLICIT                                       \
   || (I) == OWL_REQUEST_UNDEFINED)


/* Function headers */

/** @name Miscellaneous */
//@{
/// Converts a MAC address from bytes to string
const char* owl_mac_bytes_to_string(const uint8_t *const mac_binary) ;
/// Converts a MAC address from bytes to string
void owl_mac_bytes_to_string_r(const uint8_t *const mac_binary,
                               char mac_str[OWL_ETHER_ADDR_STRLEN]) ;
/// Compares two MAC addresses
bool owl_mac_equals(const uint8_t *const mac1,
                    const uint8_t *const mac2) ;
/// Converts a IEEE 802.11 frequency into a channel number
uint_fast8_t owl_frequency_to_channel(const uint_fast16_t frequency) ;
//@}

/** @name Time */
//@{
/// Sleeps for a given amount of milliseconds
int owl_msleep(uint32_t time_ms) ;
/// Sets the `owl_timestamp` `now` to the current time
int owl_timestamp_now(owl_timestamp *const now) ;
/// Converts a `struct timespec` to an `owl_timestamp`
void owl_timespec_to_timestamp(const struct timespec *const src,
                               owl_timestamp *const dst) ;
/// Converts a `struct timeval` to an `owl_timestamp`
void owl_timeval_to_timestamp(const struct timeval *const src,
                              owl_timestamp *const dst) ;
/// Compares two `owl_timestamp`
bool owl_timestamp_equals(const owl_timestamp *const d1,
                          const owl_timestamp *const d2) ;
/// Converts an `owl_timestamp` date value into milliseconds
uint64_t owl_timestamp_to_ms(const owl_timestamp *const d) ;
/// Converts an `owl_timestamp` date value into a printable string
void owl_timestamp_to_string(const owl_timestamp *const src,
                             char *const dst) ;
/// Returns the time (in milliseconds) between two dates
uint_fast32_t owl_time_elapsed_ms(const owl_timestamp *const d1,
                                  const owl_timestamp *const d2) ;
/// Computes the time difference between two dates
void owl_time_elapsed(const owl_timestamp *const d1,
                      const owl_timestamp *const d2,
                      owl_timestamp *const elapsed) ;
//@}

/** @name Endianness */
//@{
/// Changes the endianness of an `owl_timestamp` from host to network
/// order
void owl_hton_timestamp(owl_timestamp *const d) ;
/// Changes the endianness of an `owl_timestamp` from network to host
/// order
void owl_ntoh_timestamp(owl_timestamp *const d) ;
/// Changes the order of the bytes of a `float`
float owl_swap_float(const float f) ;
//@}

/** @name Endianness-related macros */
//@{
/**
 * @def owl_htonf
 * @brief Changes the endianness of a `float` from host to network
 *
 * This macro converts the byte order of a `float` if needed, depending
 * on the endianness of the host, to adopt the endianness of the network.
 * To actually change the byte order, the owl_swap_float() function is
 * used.
 *
 * @def owl_ntohf
 * @brief Changes the endianness of a `float` from network to host
 *
 * This macro converts the byte order of a `float` encoded with a network
 * endianness to adopt the endianness of the host, if the two endianness
 * are different.
 * To actually change the byte order, the owl_swap_float() function is
 * used.
 */
//@}
#if __BYTE_ORDER == __BIG_ENDIAN
# define owl_htonf(f) (f)
# define owl_ntohf(f) (f)
#else // __BYTE_ORDER == __BIG_ENDIAN
# if __BYTE_ORDER == __LITTLE_ENDIAN
#  define owl_htonf(f) owl_swap_float(f)
#  define owl_ntohf(f) owl_swap_float(f)
# else // __BYTE_ORDER == __LITTLE_ENDIAN
#  error "This program does not handle strange architectures."
# endif // __BYTE_ORDER == __LITTLE_ENDIAN
#endif // __BYTE_ORDER == __BIG_ENDIAN


/** @name Network */
//@{
/// Opens a UDP transmission socket
int owl_create_udp_trx_socket(const char *const server_address,
                              const uint_fast16_t server_port,
                              struct sockaddr *const server_description) ;
/// Opens a UDP reception socket
int owl_create_udp_listening_socket(const uint_fast16_t port) ;
//@}

/** @name Signals */
//@{
/// Generic signal handler for SIGINT
void owl_sigint_handler(const int num) ;
/// Generic signal handler for SIGTERM
void owl_sigterm_handler(const int num) ;
//@}

/** @name Threads */
//@{
/// Closes the file descriptor `fd`
void owl_close_fd(void *const fd) ;
/// Closes the stream `file`
void owl_close_file(void *const file) ;
//@}


#ifdef __cplusplus
}
#endif // __cplusplus

#endif // _LIBOWLPS_H_
