/**
 * @file bt.hpp
 * @brief Bluetooth wrapper component header - Wrapper for ESP-IDF Bluetooth functionality
 * @author Mahmoud Elmohtady
 * @company Nabd solutions - ASF
 * @copyright Copyright (c) 2025
 */

#ifndef BT_HPP
#define BT_HPP

#include <cstdint>
#include <cstring>
#include "esp_err.h"
#include "nvs_flash.h"

#ifdef CONFIG_BT_ENABLED
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gattc_api.h"
#include "esp_bt_defs.h"
#endif

/**
 * @brief Bluetooth mode enumeration
 */
enum class BtMode
{
#ifdef CONFIG_BT_ENABLED
    CLASSIC = ESP_BT_MODE_CLASSIC_BT,
    BLE = ESP_BT_MODE_BLE,
    DUAL = ESP_BT_MODE_BTDM
#else
    CLASSIC,
    BLE,
    DUAL
#endif
};

/**
 * @brief BLE advertising type enumeration
 */
enum class BleAdvType
{
#ifdef CONFIG_BT_ENABLED
    ADV_IND = ADV_TYPE_IND,
    ADV_DIRECT_IND_HIGH = ADV_TYPE_DIRECT_IND_HIGH,
    ADV_SCAN_IND = ADV_TYPE_SCAN_IND,
    ADV_NONCONN_IND = ADV_TYPE_NONCONN_IND,
    ADV_DIRECT_IND_LOW = ADV_TYPE_DIRECT_IND_LOW
#else
    ADV_IND,
    ADV_DIRECT_IND_HIGH,
    ADV_SCAN_IND,
    ADV_NONCONN_IND,
    ADV_DIRECT_IND_LOW
#endif
};

/**
 * @brief BLE connection state enumeration
 */
enum class BleConnectionState
{
    DISCONNECTED,
    CONNECTING,
    CONNECTED,
    DISCONNECTING
};

/**
 * @brief BLE advertising parameters structure
 */
struct BleAdvParams
{
    uint16_t advIntMin;         ///< Minimum advertising interval
    uint16_t advIntMax;         ///< Maximum advertising interval
    BleAdvType advType;         ///< Advertising type
    uint8_t ownAddrType;        ///< Own address type
    uint8_t peerAddrType;       ///< Peer address type
    uint8_t peerAddr[6];        ///< Peer address
    uint8_t channelMap;         ///< Channel map
    uint8_t advFilterPolicy;    ///< Advertising filter policy
};

/**
 * @brief BLE advertising data structure
 */
struct BleAdvData
{
    bool setName;               ///< Set device name in advertising data
    bool setTxPower;            ///< Set TX power in advertising data
    bool includeUuid;           ///< Include service UUID
    bool setManufacturerData;   ///< Set manufacturer data
    uint16_t appearance;        ///< Device appearance
    uint16_t manufacturerId;    ///< Manufacturer ID
    uint8_t manufacturerDataLen; ///< Manufacturer data length
    uint8_t* manufacturerData;  ///< Manufacturer data
    uint8_t serviceUuidLen;     ///< Service UUID length
    uint8_t* serviceUuid;       ///< Service UUID
    char* deviceName;           ///< Device name
};

/**
 * @brief GATT service structure
 */
struct GattService
{
    uint16_t serviceId;         ///< Service ID
    uint16_t serviceUuid;       ///< Service UUID
    uint16_t numHandles;        ///< Number of handles
    bool isPrimary;             ///< Primary service flag
};

/**
 * @brief GATT characteristic structure
 */
struct GattCharacteristic
{
    uint16_t charUuid;          ///< Characteristic UUID
    uint8_t properties;         ///< Characteristic properties
    uint8_t permissions;        ///< Characteristic permissions
    uint16_t maxLen;            ///< Maximum value length
    bool autoRsp;               ///< Auto response flag
};

/**
 * @brief Bluetooth event callback function types
 */
#ifdef CONFIG_BT_ENABLED
using BtGapEventCallback = void (*)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param, void* userData);
using BtGattsEventCallback = void (*)(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param, void* userData);
using BtGattcEventCallback = void (*)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param, void* userData);
#else
using BtGapEventCallback = void (*)(int event, void* param, void* userData);
using BtGattsEventCallback = void (*)(int event, int gatts_if, void* param, void* userData);
using BtGattcEventCallback = void (*)(int event, int gattc_if, void* param, void* userData);
#endif

/**
 * @brief Bluetooth wrapper class
 * 
 * Provides a C++ wrapper for ESP-IDF Bluetooth functionality.
 * This class encapsulates ESP-IDF Bluetooth and BLE driver functions in an object-oriented interface.
 */
class Bluetooth
{
public:
    /**
     * @brief Constructor
     * @details Initializes the Bluetooth wrapper instance
     */
    Bluetooth();

    /**
     * @brief Destructor
     * @details Cleans up resources and deinitializes Bluetooth
     */
    ~Bluetooth();

    /**
     * @brief Initialize Bluetooth stack
     * @param mode Bluetooth mode (Classic, BLE, or Dual)
     * @return true if initialized successfully, false otherwise
     */
    bool initialize(BtMode mode);

    /**
     * @brief Deinitialize Bluetooth stack
     * @return true if deinitialized successfully, false otherwise
     */
    bool deinitialize();

    /**
     * @brief Set device name
     * @param name Device name (max 248 characters)
     * @return true if set successfully, false otherwise
     */
    bool setDeviceName(const char* name);

    /**
     * @brief Get device name
     * @param name Buffer to store device name
     * @param maxLen Maximum length of name buffer
     * @return true if retrieved successfully, false otherwise
     */
    bool getDeviceName(char* name, size_t maxLen);

    /**
     * @brief Set advertising parameters
     * @param params Advertising parameters
     * @return true if set successfully, false otherwise
     */
    bool setAdvParams(const BleAdvParams& params);

    /**
     * @brief Set advertising data
     * @param advData Advertising data
     * @return true if set successfully, false otherwise
     */
    bool setAdvData(const BleAdvData& advData);

    /**
     * @brief Start BLE advertising
     * @return true if started successfully, false otherwise
     */
    bool startAdvertising();

    /**
     * @brief Stop BLE advertising
     * @return true if stopped successfully, false otherwise
     */
    bool stopAdvertising();

    /**
     * @brief Start BLE scanning
     * @param duration Scan duration in seconds (0 = continuous)
     * @return true if started successfully, false otherwise
     */
    bool startScanning(uint32_t duration = 0);

    /**
     * @brief Stop BLE scanning
     * @return true if stopped successfully, false otherwise
     */
    bool stopScanning();

    /**
     * @brief Create GATT service
     * @param service Service configuration
     * @return Service handle, or 0 on failure
     */
    uint16_t createGattService(const GattService& service);

    /**
     * @brief Add characteristic to service
     * @param serviceHandle Service handle
     * @param characteristic Characteristic configuration
     * @return Characteristic handle, or 0 on failure
     */
    uint16_t addCharacteristic(uint16_t serviceHandle, const GattCharacteristic& characteristic);

    /**
     * @brief Start GATT service
     * @param serviceHandle Service handle
     * @return true if started successfully, false otherwise
     */
    bool startGattService(uint16_t serviceHandle);

    /**
     * @brief Stop GATT service
     * @param serviceHandle Service handle
     * @return true if stopped successfully, false otherwise
     */
    bool stopGattService(uint16_t serviceHandle);

    /**
     * @brief Send notification to connected client
     * @param connId Connection ID
     * @param attrHandle Attribute handle
     * @param data Data to send
     * @param dataLen Length of data
     * @return true if sent successfully, false otherwise
     */
    bool sendNotification(uint16_t connId, uint16_t attrHandle, const uint8_t* data, size_t dataLen);

    /**
     * @brief Send indication to connected client
     * @param connId Connection ID
     * @param attrHandle Attribute handle
     * @param data Data to send
     * @param dataLen Length of data
     * @return true if sent successfully, false otherwise
     */
    bool sendIndication(uint16_t connId, uint16_t attrHandle, const uint8_t* data, size_t dataLen);

    /**
     * @brief Connect to remote device
     * @param remoteAddr Remote device address
     * @param addrType Address type
     * @return true if connection initiated successfully, false otherwise
     */
    bool connectToDevice(const uint8_t* remoteAddr, uint8_t addrType);

    /**
     * @brief Disconnect from remote device
     * @param connId Connection ID
     * @return true if disconnection initiated successfully, false otherwise
     */
    bool disconnectDevice(uint16_t connId);

    /**
     * @brief Get connection state
     * @return Current connection state
     */
    BleConnectionState getConnectionState() const;

    /**
     * @brief Check if device is connected
     * @return true if connected, false otherwise
     */
    bool isConnected() const;

    /**
     * @brief Get number of connected devices
     * @return Number of connected devices
     */
    uint8_t getConnectedDeviceCount() const;

    /**
     * @brief Set GAP event callback
     * @param callback Callback function
     * @param userData User data passed to callback
     */
    void setGapEventCallback(BtGapEventCallback callback, void* userData);

    /**
     * @brief Set GATTS event callback
     * @param callback Callback function
     * @param userData User data passed to callback
     */
    void setGattsEventCallback(BtGattsEventCallback callback, void* userData);

    /**
     * @brief Set GATTC event callback
     * @param callback Callback function
     * @param userData User data passed to callback
     */
    void setGattcEventCallback(BtGattcEventCallback callback, void* userData);

    /**
     * @brief Get MAC address
     * @param mac Buffer to store MAC address (6 bytes)
     * @return true if retrieved successfully, false otherwise
     */
    bool getMacAddress(uint8_t* mac);

    /**
     * @brief Set TX power
     * @param powerType Power type
     * @param powerLevel Power level
     * @return true if set successfully, false otherwise
     */
#ifdef CONFIG_BT_ENABLED
    bool setTxPower(esp_ble_power_type_t powerType, esp_power_level_t powerLevel);
#endif

    /**
     * @brief Get TX power
     * @param powerType Power type
     * @return Current power level, or ESP_PWR_LVL_INVALID on error
     */
#ifdef CONFIG_BT_ENABLED
    esp_power_level_t getTxPower(esp_ble_power_type_t powerType);
#endif

    /**
     * @brief Check if Bluetooth is initialized
     * @return true if initialized, false otherwise
     */
    bool isInitialized() const;

    /**
     * @brief Get default advertising parameters
     * @return Default BLE advertising parameters
     */
    static BleAdvParams getDefaultAdvParams();

    /**
     * @brief Get default advertising data
     * @return Default BLE advertising data
     */
    static BleAdvData getDefaultAdvData();

    /**
     * @brief Get default GATT service configuration
     * @return Default GATT service configuration
     */
    static GattService getDefaultGattService();

    /**
     * @brief Get default GATT characteristic configuration
     * @return Default GATT characteristic configuration
     */
    static GattCharacteristic getDefaultGattCharacteristic();

private:
    bool m_isInitialized_;                    ///< Initialization status
    BtMode m_mode_;                           ///< Bluetooth mode
    BleConnectionState m_connectionState_;    ///< Current connection state
    uint8_t m_connectedDeviceCount_;          ///< Number of connected devices
#ifdef CONFIG_BT_ENABLED
    esp_gatt_if_t m_gattsIf_;                ///< GATTS interface
    esp_gatt_if_t m_gattcIf_;                ///< GATTC interface
#else
    int m_gattsIf_;
    int m_gattcIf_;
#endif
    uint16_t m_appId_;                        ///< Application ID
    
    // Callback functions and user data
    BtGapEventCallback m_gapCallback_;        ///< GAP event callback
    void* m_gapUserData_;                     ///< GAP callback user data
    BtGattsEventCallback m_gattsCallback_;    ///< GATTS event callback
    void* m_gattsUserData_;                   ///< GATTS callback user data
    BtGattcEventCallback m_gattcCallback_;    ///< GATTC event callback
    void* m_gattcUserData_;                   ///< GATTC callback user data
    
#ifdef CONFIG_BT_ENABLED
    /**
     * @brief GAP event handler
     * @param event GAP event
     * @param param Event parameters
     */
    static void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
    
    /**
     * @brief GATTS event handler
     * @param event GATTS event
     * @param gatts_if GATTS interface
     * @param param Event parameters
     */
    static void gattsEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
    
    /**
     * @brief GATTC event handler
     * @param event GATTC event
     * @param gattc_if GATTC interface
     * @param param Event parameters
     */
    static void gattcEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param);
#endif
    
    /**
     * @brief Convert BtMode to ESP-IDF esp_bt_mode_t
     * @param mode Bluetooth mode
     * @return ESP-IDF bluetooth mode
     */
#ifdef CONFIG_BT_ENABLED
    esp_bt_mode_t convertMode(BtMode mode);
#endif
    
    /**
     * @brief Convert BleAdvType to ESP-IDF esp_ble_adv_type_t
     * @param advType Advertising type
     * @return ESP-IDF advertising type
     */
#ifdef CONFIG_BT_ENABLED
    esp_ble_adv_type_t convertAdvType(BleAdvType advType);
#endif
    
    static Bluetooth* s_instance_;            ///< Static instance for callbacks
};

#endif // BT_HPP
