/**
 * @file gadget_bridge.h
 * @author Anatole SCHRAMM-HENRY
 * @brief Header file exposing the API used to communicate/interact with the GadgetBridge Android application
 *        over BLE.
 * @version 0.1
 * @date 2023-04-04
 * 
 * @copyright MIT
 * 
 */

#ifndef GADGET_BRIDGE_H
#define GADGET_BRIDGE_H

#include "wm_type_def.h"
#include <time.h>

/**
 * @brief GADGET_BRIDGE_PARSER_BUFFER_SIZE allows to set the size of the buffer
 * which is internally used by the parser to do it's job.
 * 
 */
#define GADGET_BRIDGE_PARSER_BUFFER_SIZE (300)

/**
 * @brief GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD permits to set a size threshold used to free up
 * some space in the parser's internal buffer when the threshold is reached.
 * This ensures that we can keep on feeding new data and not get stuck.
 * 
 */
#define GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD (100)

/**
 * @brief GADGET_BRIDGE_PARSER_MAX_BODY_SIZE defines the max body size that will be saved in the event_data
 * structure when parsing the body of a notification.
 * 
 */
#define GADGET_BRIDGE_PARSER_MAX_BODY_SIZE (200)

/**
 * @brief GADGET_BRIDGE_PARSER_MAX_TITLE_SIZE defines the max title size that will be saved in the event_data
 * structure when parsing the title of a notification.
 * 
 */
#define GADGET_BRIDGE_PARSER_MAX_TITLE_SIZE (100)

typedef enum gadget_bridge_toast_type
{
    GADGET_BRIDGE_TOAST_TYPE_INFO = 0,
    GADGET_BRIDGE_TOAST_TYPE_WARN,
    GADGET_BRIDGE_TOAST_TYPE_ERROR,
} gadget_bridge_toast_type_e;

typedef enum gadget_bridge_music_control
{
    GADGET_BRIDGE_MUSIC_CONTROL_PLAY = 0,
    GADGET_BRIDGE_MUSIC_CONTROL_PAUSE,
    GADGET_BRIDGE_MUSIC_CONTROL_PLAYPAUSE,
    GADGET_BRIDGE_MUSIC_CONTROL_NEXT,
    GADGET_BRIDGE_MUSIC_CONTROL_PREVIOUS,
    GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEUP,
    GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEDOWN,
    GADGET_BRIDGE_MUSIC_CONTROL_FORWARD,
    GADGET_BRIDGE_MUSIC_CONTROL_REWIND,
} gadget_bridge_music_control_e;

typedef enum gadget_bridge_call_action
{
    GADGET_BRIDGE_CALL_ACTION_ACCEPT = 0,
    GADGET_BRIDGE_CALL_ACTION_END,
    GADGET_BRIDGE_CALL_ACTION_INCOMING,
    GADGET_BRIDGE_CALL_ACTION_OUTGOING,
    GADGET_BRIDGE_CALL_ACTION_REJECT,
    GADGET_BRIDGE_CALL_ACTION_START,
    GADGET_BRIDGE_CALL_ACTION_IGNORE,
    GADGET_BRIDGE_CALL_ACTION_UNKNOWN,
} gadget_bridge_call_action_e;

typedef enum gadget_bridge_notification_action
{
    GADGET_BRIDGE_NOTIFICATION_ACTION_DISMISS = 0,
    GADGET_BRIDGE_NOTIFICATION_ACTION_DISMISS_ALL,
    GADGET_BRIDGE_NOTIFICATION_ACTION_OPEN,
    GADGET_BRIDGE_NOTIFICATION_ACTION_MUTE,
    GADGET_BRIDGE_NOTIFICATION_ACTION_REPLY,
} gadget_bridge_notification_action_e;

typedef enum gadget_bridge_http_request_method
{
    GADGET_BRIDGE_HTTP_REQUEST_GET = 0,
    GADGET_BRIDGE_HTTP_REQUEST_POST,
    GADGET_BRIDGE_HTTP_REQUEST_HEAD,
    GADGET_BRIDGE_HTTP_REQUEST_PUT,
    GADGET_BRIDGE_HTTP_REQUEST_PATCH,
    GADGET_BRIDGE_HTTP_REQUEST_DELETE,

} gadget_bridge_http_request_method_e;

typedef enum gadget_bridge_parser_code
{
    GADGET_BRIDGE_PARSER_CODE_OK = 0,
    GADGET_BRIDGE_PARSER_CODE_PARSING,
    GADGET_BRIDGE_PARSER_CODE_BUFFER_FULL,
    GADGET_BRIDGE_PARSER_CODE_DATA_TOO_LONG,
} gadget_bridge_parser_code_e;

typedef struct http_header
{
    const char *key;
    const char *value;
} http_header_t;

typedef enum gadget_bridge_event_type
{
    GADGET_BRIDGE_EVENT_TYPE_NONE = 0,
    GADGET_BRIDGE_EVENT_TYPE_SET_TIME,
    GADGET_BRIDGE_EVENT_TYPE_NOTIFY,
    GADGET_BRIDGE_EVENT_TYPE_CALL,
    GADGET_BRIDGE_EVENT_TYPE_WEATHER,
    GADGET_BRIDGE_EVENT_TYPE_FIND,
    GADGET_BRIDGE_EVENT_TYPE_ACT,
    GADGET_BRIDGE_EVENT_TYPE_MUSIC_INFO,
    GADGET_BRIDGE_EVENT_TYPE_MUSIC_STATE,
    GADGET_BRIDGE_EVENT_TYPE_UNKNOWN,
} gadget_bridge_event_type_e;

typedef enum gadget_bridge_notification_type
{
    GADGET_BRIDGE_NOTIFICATION_TYPE_SMS = 0,
    GADGET_BRIDGE_NOTIFICATION_TYPE_EMAIL,
    GADGET_BRIDGE_NOTIFICATION_TYPE_UNKNOWN,
} gadget_bridge_notification_type_e;

typedef enum
{
    GADGET_BRIDGE_MUSIC_STATE_PAUSE = 0,
    GADGET_BRIDGE_MUSIC_STATE_PLAY,
    GADGET_BRIDGE_MUSIC_STATE_UNKNOWN,
} gadget_bridge_music_state_e;

typedef struct gadget_bridge_event_data
{
    gadget_bridge_event_type_e event_type;
    union
    {
        struct
        {
            struct tm gm_time;
            struct tm local_time;
            int8_t time_zone;
        } time;
        struct
        {
            uint32_t handle;
            gadget_bridge_notification_type_e notification_type;
            char *title;
            char *body;
        } notification;
        struct
        {
            char *phone_number;
            char *contact;
            gadget_bridge_call_action_e call_action;
        } call;
        struct
        {
            float temperature_celsius;
            uint8_t humidity;
            char *weather_desc;
            float wind_speed_kmh;
            uint16_t wind_dir;
            char *location;
        } weather;
        struct
        {
            bool find;
        } find;
        struct
        {
            bool heart_rate_monitor;
            bool steps;
            uint8_t heart_rate_interval;
        } act;
        struct
        {
            gadget_bridge_music_state_e music_state;
            uint16_t position_in_seconds;
            uint8_t shuffle;
            uint8_t repeat;
        } music_state;
        struct
        {
            char *artist;
            char *track;
            uint16_t duration_in_seconds;
        } music_info;
    };
} gadget_bridge_event_data_t;

typedef void (*parser_event_callback_t)(const gadget_bridge_event_data_t *gadget_bridge_event_data);

/**
 * @brief Sends an Android toast to GadgetBridge to be displayed on the phone.
 * 
 * @param toast_type the type of the toast (INFO, WARN or ERROR).
 * @param message a string representing the message to display.
 * @return true if the command was successfully sent.
 * @return false otherwise.
 */
bool gadget_bridge_send_toast(gadget_bridge_toast_type_e toast_type, const char *message);

/**
 * @brief Sends up to two firmwares version to GadgetBridge.
 * These are displayed in the display details section of the watch in GadgetBridge.
 * 
 * @param fw1 a string representing the first firmware version.
 * @param fw2 a string representing the second firmware version.
 * @return true if the command was successfully sent.
 * @return false otherwise.
 */
bool gadget_bridge_send_firmware_version(const char *fw1, const char *fw2);

/**
 * @brief Sends the current battery status to GadgetBridge.
 * 
 * @param battery_level_in_percent the current battery level from 0 to 100%.
 * @param battery_level_in_V the current battery voltage in volts (3.942 for example).
 * @param is_charging a boolean which indicates if the battery is currently charging or not.
 * @return true if the command was successfully sent.
 * @return false otherwise.
 */
bool gadget_bridge_send_battery_status(uint8_t battery_level_in_percent, float battery_level_in_V, bool is_charging);

/**
 * @brief Sends the find phone command to GagdetBridge, this will make the phone ring and vibrate
 * so that you can locate it.
 * 
 * @param find_phone a boolean which indicates to make the phone rind and vibrate or not.
 * @return true if the command was successfully sent.
 * @return false otherwise.
 */
bool gadget_bridge_send_find_phone(bool find_phone);

/**
 * @brief Sends a command to control the music playback of the phone through GadgetBridge.
 * 
 * @param music_control an enumeration value indicating the action to perform:
 *                      PLAY, PAUSE, NEXT, PREVIOUS, VOLUMEUP etc..
 * @return true if the command was successfully sent.
 * @return false otherwise.
 */
bool gadget_bridge_send_music_control(gadget_bridge_music_control_e music_control);

bool gadget_bridge_handle_call(gadget_bridge_call_action_e call_action);

bool gadget_bridge_handle_notification(gadget_bridge_call_action_e notification_action, uint32_t handle, const char *phone_number, const char *message);

/**
 * @brief Sends the provided activity data to GadgetBridge. This will then be displayed 
 * on the app in the activity section.
 * 
 * @param heart_rate_in_bpm the current heart rate in beat per minute
 * @param step_count the number of new steps since the last time the count was sent.
 * @return true if the command was successfully sent.
 * @return false otherwise.
 */
bool gadget_bridge_send_activity_data(uint16_t heart_rate_in_bpm, uint32_t step_count);

/**
 * @brief Tells GadgetBridge to perform an HTTP request for us.
 * @note THIS DOES NOT WORK as GadgetBridge don't and will never have network permission... what a pitty !
 * 
 * @param id an unsigned integer representing the ID of the http request
 * @param url a string representing the URL to fetch
 * @param http_request_method a enumeration value specifying the http verb to use : GET, POST, PATCH etc..
 * @param http_body the body to include in the request (not implemented yet)
 * @param http_headers various headers to include in the request (not implemented yet)
 * @return true if the request has been successfully sent to GadgetBridge
 * @return false otherwise
 */
bool gadget_bridge_send_http_request(uint32_t id, const char *url, gadget_bridge_http_request_method_e http_request_method, const char *http_body, const http_header_t *http_headers);

//bool gadget_bridge_send_force_calendar_sync(void);

/**
 * @brief Registers a callback function used to listen for GadgetBridge events.
 * 
 * @param parser_event_callback 
 */
void gadget_bridge_parser_register_event_callback(parser_event_callback_t parser_event_callback);

/**
 * @brief Feeds new data to the GadgetBridge parser.
 * 
 * @param data the new chunk of data to parse, it will be copied to the parser's internal buffer,
 * so you can free the memory containing the string after calling the function.
 * @param length the length in bytes of the new chunk.
 * @return gadget_bridge_parser_code_e GADGET_BRIDGE_PARSER_CODE_OK if no error occured.
 */
gadget_bridge_parser_code_e gadget_bridge_parser_feed(const char *data, uint16_t length);

/**
 * @brief Call this function to run the parser.
 * It should be safe to call if in a loop like : while((code = gadget_bridge_parser_run()) == GADGET_BRIDGE_PARSER_CODE_PARSING);
 * 
 * @return gadget_bridge_parser_code_e the parser's execution status code.
 */
gadget_bridge_parser_code_e gadget_bridge_parser_run(void);

const char *gadget_bridge_parser_code_2_str(gadget_bridge_parser_code_e parser_code);

const char *gadget_bridge_event_type_2_str(gadget_bridge_event_type_e event_type);

const char *gadget_bridge_notification_type_2_str(gadget_bridge_notification_type_e notification_type);

const char *gadget_bridge_music_state_2_str(gadget_bridge_music_state_e music_state);

void gadget_bridge_parser_debug(void);

#endif //GADGET_BRIDGE_H
