#ifndef BLUESPY_INCLUDE_GUARD
#define BLUESPY_INCLUDE_GUARD

//!
//! @file bluespy.h
//! @brief blueSPY C API
//!

#ifndef BLUESPY_API
#if defined _WIN32 || defined __CYGWIN__
#define BLUESPY_API __declspec(dllimport)
#else
#define BLUESPY_API __attribute__((visibility("default")))
#endif
#endif

#include "stdbool.h"
#include "stdint.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef enum bluespy_error : uint32_t {
    BLUESPY_NO_ERROR = 0,
    BLUESPY_ERROR_NO_DEVICE,
    BLUESPY_ERROR_LICENCE,
    BLUESPY_ERROR_NO_FILE,
    BLUESPY_ERROR_CAPTURE_NOT_STARTED,
    BLUESPY_ERROR_INVALID_PACKET,
    BLUESPY_ERROR_INVALID_CAPTURE_OPTIONS,
} bluespy_error;

/**
 * @brief Get message for error
 * @param[in] error
 * @return Internal pointer to null terminated string (do not free)
 */
BLUESPY_API const char* bluespy_error_string(bluespy_error error);

/**
 * @brief Initialise bluespy, run once at start of program
 */
BLUESPY_API void bluespy_init();

/**
 * @brief Clean up before program exits
 */
BLUESPY_API void bluespy_deinit();

/**
 * @brief Start a GUI instance
 *
 * The GUI exists in a background thread, this function returns immediately
 */
BLUESPY_API void bluespy_start_gui();

/**
 * @brief Connect to Moreph
 * @param[in] serial - Serial number of the Moreph
 * @return Error code
 *
 * Connect by serial number, or first found on USB if serial == -1
 */
BLUESPY_API bluespy_error bluespy_connect(uint32_t serial = -1);

/**
 * @brief Disconnect from Moreph
 * @return Error code
 */
BLUESPY_API bluespy_error bluespy_disconnect();

typedef struct bluespy_capture_options {
    bool enable_CL;
    bool enable_LE;
    bool enable_QHS;
    bool enable_15_4;
    bool enable_wifi;
    bool enable_MHDT_CL;
    bool enable_MHDT_LE;
    bool enable_Dukosi;
    bool enable_Varjo;
    bool enable_Channel_Sounding;
    /**
     * Valid spectrum periods in microseconds are:
     * 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000
     * 0 to disable spectrum_
     */
    uint16_t spectrum_period;
} bluespy_capture_options;

/**
 * @brief Create a bluespy_capture_options struct
 * @return New struct
 */
BLUESPY_API bluespy_capture_options* bluespy_capture_options_alloc();

/**
 * @brief Delete a bluespy_capture_options struct
 */
BLUESPY_API void bluespy_capture_options_delete(bluespy_capture_options* opts);

/**
 * @brief Start a capture
 * @param[in] filename - UTF8 filename
 * @param[in] opts - Capture options
 * @return Error code
 */
BLUESPY_API bluespy_error bluespy_capture(const char* filename, bluespy_capture_options* opts);

/**
 * @brief Stop a capture
 * @return Error code
 */
BLUESPY_API bluespy_error bluespy_stop_capture();

/**
 * @brief Load a capture
 * @param[in] filename - UTF8 filename
 * @return Error code
 */
BLUESPY_API bluespy_error bluespy_load_file(const char* filename);

/**
 * @brief Close current file
 * @return Error code
 */
BLUESPY_API bluespy_error bluespy_close_file();

/**
 * @brief Number of baseband packets loaded
 * @return N
 */
BLUESPY_API uint32_t bluespy_packet_count(void);

/**
 * @brief Indentifier for an packet or higher layer event
 * -1 means invalid
 * Only use bluespy_event_ids returned by the API
 * After bluespy_load_file or bluespy_capture all previous ids are invalid
 */
typedef uint64_t bluespy_event_id;

/**
 * @brief Get a baseband packet
 * @param[in] index - 0 <= index < bluespy_packet_count()
 * @return Event ID
 */
BLUESPY_API bluespy_event_id bluespy_get_baseband(uint32_t index);

/**
 * @brief Get higher layer packets
 * @param[in] event
 * @return Event ID
 */
BLUESPY_API bluespy_event_id bluespy_get_parent(bluespy_event_id event);

/**
 * @brief Get all lower layer packets
 * @param[in] event
 * @param[out] count - size of returned array
 * @return Event ID array
 *
 * Returned child array is valid until next call, so take a copy
 */
BLUESPY_API const bluespy_event_id* bluespy_get_children(bluespy_event_id event, uint32_t* count);

/**
 * @brief Query a packet
 * @param[in] event
 * @param[in] query - query string to apply
 * @return String result of a query
 *
 * Returned value is valid until next call, so take a copy
 */
BLUESPY_API const char* bluespy_query(bluespy_event_id event, const char* query);

/**
 * @brief Query a packet
 * @param[in] event
 * @param[in] query - query string to apply
 * @return Integer result of a query
 */
BLUESPY_API int64_t bluespy_query_int(bluespy_event_id event, const char* query);

/**
 * @brief Query a packet
 * @param[in] event
 * @param[in] query - query string to apply
 * @return Bool result of a query
 */
BLUESPY_API bool bluespy_query_bool(bluespy_event_id event, const char* query);

/**
 * @brief Query a packet
 * @param[in] event
 * @param[in] query - query string to apply
 * @param[out] s - return if string
 * @param[out] i - return if int
 * @param[out] b - return if bool
 * @return 0 = None, 1 = str, 2 = int, 3 = bool
 *
 * Returned string value is valid until next call, so take a copy
 */
BLUESPY_API int bluespy_query_auto(bluespy_event_id event, const char* query, const char** s,
                                   int64_t* i, bool* b);

/**
 * @brief Add a link key for decryption
 * @param[in] key - 16 bytes of binary
 * @param[in] addr0 - Bluetooth address of first device, set to 0 if unknown
 * @param[in] addr1 - Bluetooth address of second device, set to 0 if unknown
 *
 * The link key should be added before loading/capturing, or you should load again afterwards.
 */
BLUESPY_API void bluespy_add_link_key(const unsigned char* key, uint64_t addr0, uint64_t addr1);

/**
 * @brief Add an IRK
 * @param[in] key - 16 bytes of binary
 * @param[in] addr - Array of relevant Bluetooth addresses, can be empty. Set bit 48 == 1 for
 * Random, == 0 for Public
 * @param[in] n_addresses - Length of array
 *
 * The key should be added before loading/capturing, or you should load again afterwards.
 */
BLUESPY_API void bluespy_add_IRK(const unsigned char* key, uint64_t* addr, uint64_t n_addresses);

#ifdef __cplusplus
}
#endif

#endif
