#ifndef BTA_H_INCLUDED
#define BTA_H_INCLUDED

#include <bta_status.h>
#include <bta_frame.h>
#include <bta_discovery.h>
#include <bta_flash_update.h>
#include <bta_event.h>

#ifdef PLAT_WINDOWS
    #define BTA_CALLCONV __stdcall
    #ifdef PLAT_32_BIT
        // for matlab compatibility
        #define BTA_PRAGMA_ALIGN __declspec(align(4))
    #elif PLAT_64_BIT
        // for matlab compatibility
        #define BTA_PRAGMA_ALIGN __declspec(align(8))
    #else
        #error "Please define PLAT_64_BIT or PLAT_32_BIT"
    #endif
    #ifdef COMPILING_DLL
        #define DLLEXPORT __declspec(dllexport)
    #else
        #define DLLEXPORT __declspec(dllimport)
    #endif
#else
    // ### LINUX ###
    //must be empty
    #define DLLEXPORT
    //must be empty
    #define BTA_CALLCONV
    #define BTA_PRAGMA_ALIGN
#endif


#define BTA_Handle unsigned long

/**     @brief Callback function to report on informative events
*              The implementation of this function in the application must copy the relevant data and return immediately
*              The library defines on its own which events are used, at what rate and at what time they are generated.
*              The parameter 'verbosity' in BTAsetInfoEventCallback can be used in that matter.
*       @param event An identifier for the event that the library is reporting on
*       @param msg A string containing the information for the user        */
typedef void (BTA_CALLCONV *FN_BTA_InfoEvent)(BTA_EventId eventId, int8_t *msg);

/**     @brief Callback function to report on data frames from the sensor
*              The implementation of this function in the application must copy the relevant data and return immediately
*       @param frame A pointer to the structure containing the data frame        */
typedef void (BTA_CALLCONV *FN_BTA_FrameArrived)(BTA_Frame *frame);

/**     @brief Enumerator with valid queuing modes        */
typedef enum {
    BTA_QueueModeDoNotQueue       = 0,
    BTA_QueueModeDropOldest       = 1,
    BTA_QueueModeDropCurrent      = 2
} BTA_QueueMode;

/**     @brief Configuration structure to be passed with BTAopen        */
typedef struct {
    BTA_PRAGMA_ALIGN uint8_t *udpDataIpAddr;           //<<< The IP address for the UDP data interface
    BTA_PRAGMA_ALIGN uint8_t udpDataIpAddrLen;
    BTA_PRAGMA_ALIGN uint16_t udpDataPort;             //<<< The port for the UDP data interface
    BTA_PRAGMA_ALIGN uint8_t *udpControlOutIpAddr;     //<<< The IP address for the UDP control interface (outbound connection)
    BTA_PRAGMA_ALIGN uint8_t udpControlOutIpAddrLen;
    BTA_PRAGMA_ALIGN uint16_t udpControlOutPort;       //<<< The port for the UDP control interface (outbound connection)
    BTA_PRAGMA_ALIGN uint8_t *udpControlInIpAddr;      //<<< The IP address for the UDP control interface (inbound connection)
    BTA_PRAGMA_ALIGN uint8_t udpControlInIpAddrLen;
    BTA_PRAGMA_ALIGN uint16_t udpControlInPort;        //<<< The port for the UDP control interface (inbound connection)
    BTA_PRAGMA_ALIGN uint8_t *tcpDeviceIpAddr;         //<<< The IP address for the TCP data and control interface
    BTA_PRAGMA_ALIGN uint8_t tcpDeviceIpAddrLen;
    BTA_PRAGMA_ALIGN uint16_t tcpDataPort;             //<<< The port for the TCP data interface
    BTA_PRAGMA_ALIGN uint16_t tcpControlPort;          //<<< The port for the TCP control interface

    BTA_PRAGMA_ALIGN int8_t *uartPortName;             //<<< The port name of the UART to use
    BTA_PRAGMA_ALIGN uint32_t uartBaudRate;            //<<< The UART baud rate
    BTA_PRAGMA_ALIGN uint8_t uartDataBits;             //<<< The number of UART data bits used
    BTA_PRAGMA_ALIGN uint8_t uartStopBits;             //<<< 0: None, 1: One, 2: Two, 3: 1.5 stop bits
    BTA_PRAGMA_ALIGN uint8_t uartParity;               //<<< 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space Parity
    BTA_PRAGMA_ALIGN uint8_t uartTransmitterAddress;   //<<< The source address for UART communications
    BTA_PRAGMA_ALIGN uint8_t uartReceiverAddress;      //<<< The target address for UART communications

    BTA_PRAGMA_ALIGN uint32_t serialNumber;            //<<< Serial number of device to be opened (byte array)

    BTA_PRAGMA_ALIGN uint8_t *calibFileName;           //<<< name of calibartion file

    BTA_PRAGMA_ALIGN BTA_FrameMode frameMode;
    
    BTA_PRAGMA_ALIGN FN_BTA_InfoEvent infoEvent;       //<<< Function pointer to the function to be called upon an informative event
    BTA_PRAGMA_ALIGN uint8_t verbosity;                //<<< A value to tell the library when and when not to generate InfoEvents (to be defined by the library) (0: Only critical events, 10: Maximum amount of events)
    BTA_PRAGMA_ALIGN FN_BTA_FrameArrived frameArrived; //<<< Function pointer to the function to be called when a frame is ready
    
    BTA_PRAGMA_ALIGN uint16_t frameQueueLength;        //<<< The library queues this amount of frames internally
    BTA_PRAGMA_ALIGN BTA_QueueMode frameQueueMode;     //<<< The frame queue configuration parameter
} BTA_Config;

typedef struct {
    char *variableName;
    uint8_t pointer;                                //<<< 1 --> variable is a pointer, 0 --> not a pointer
} BTA_ConfigStructOrg;


DLLEXPORT extern BTA_ConfigStructOrg btaConfigStructOrg[];
DLLEXPORT extern uint32_t btaConfigStructOrgLen;

#ifdef __cplusplus
extern "C"
{
#endif

/* -------------------------------------------------------------------------- */

/**     @brief For querying API version
*       @param buildDateTime A char array allocated by the caller containing the date/time string of build on return.
*       @param buildDateTimeLen Size of the preallocated buffer behind buildDateTime in bytes
*       @param supportedDeviceTypes Array containing the codes of all devices supported by a specifiy BTA implementation on return
*       @param supportedDeviceTypesLen Pointer to size of supportedDeviceTypes (= the number of supported devices, not in bytes!)
*                                      On return it contains number of supported device types
*       @return The version of the BltTofApi implemented      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetVersion(uint32_t *verMaj, uint32_t *verMin, uint32_t *verNonFun, uint8_t *buildDateTime, uint32_t buildDateTimeLen, uint16_t *supportedDeviceTypes, uint32_t *supportedDeviceTypesLen);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Callback function to report on a discovered device
*       @param deviceInfo A struct containing information about the discovered device        */
typedef void (BTA_CALLCONV *FN_BTA_DeviceFound)(BTA_DeviceInfo *deviceInfo);

/**     @brief Starts sending of discovery messages to sensors.
*              If possible, this is implemented as a broadcast.
*              The library defines which parameters have to be set prior to starting discovery.
*              Different modes of thoroughness are possible by defining different sets/values of configuration.
*       @param deviceFound This function is called when a device is discovered
*       @return Refer to BTA_Status                   */
DLLEXPORT BTA_Status BTA_CALLCONV BTAstartDiscovery(BTA_DiscoveryConfig *config, FN_BTA_DeviceFound deviceFound);

/**     @brief Stops sending of discovery messages
*       @return Refer to BTA_Status            */
DLLEXPORT BTA_Status BTA_CALLCONV BTAstopDiscovery();

//----------------------------------------------------------------------------------------------------------------

/**     @brief Fills the config structure with standard values
*       @param config Pointer to the structure to be initialized to standard values
*       @return Refer to BTA_Status      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAinitConfig(BTA_Config *config);

/**     @brief  Starts the service and connects to the device
*               If the connection is can not be fully established, the function returns an error
*       @param handle Pointer containing the handle to the device on return
*       @param config Address of the previously initialized config structure
*       @return Refer to BTA_Status                                          */
DLLEXPORT BTA_Status BTA_CALLCONV BTAopen(BTA_Config *config, BTA_Handle *handle);

/**     @brief Disconnects from the sensor and stops the service            
*       @param handle Pointer to the handle of the device to be closed
*       @return Refer to BTA_Status        */
DLLEXPORT BTA_Status BTA_CALLCONV BTAclose(BTA_Handle *handle);

/**     @brief For querying information about the device
*       @param handle Handle of the device to be used
*       @param deviceInfo Pointer to pointer to structure with information about the device on return      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetDeviceInfo(BTA_Handle handle, BTA_DeviceInfo **deviceInfo);

/**     @brief For freeing the device informatuion structure
*       @param deviceInfo Pointer to structure to be freed      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAfreeDeviceInfo(BTA_DeviceInfo *deviceInfo);

/**     @brief Queries whether the service is running or not
*       @param handle Handle of the device to be used
*       @return True if the service is running, false otherwise                       */
DLLEXPORT uint8_t BTA_CALLCONV BTAisRunning(BTA_Handle handle);

/**     @brief Queries whether the library has a valid connection to the sensor
*       @param handle Handle of the device to be used
*       @return True if connected to the sensor, false otherwise                       */
DLLEXPORT uint8_t BTA_CALLCONV BTAisConnected(BTA_Handle handle);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Sets the interval of keep-alive-messages.
*              The library takes responsibility of sending a keep-alive-message if there is no
*              communication during <interval> seconds.
*       @param handle Handle of the device to be used
*       @param interval The interval for keep-alive-messages to be sent
*       @return Refer to BTA_Status                       */
DLLEXPORT BTA_Status BTA_CALLCONV BTAsetKeepAliveMsgInterval(BTA_Handle handle, float interval);

/**     @brief Sets whether the control interface should use a CRC checksum (if device supports it)
*       @param handle Handle of the device to be used
*       @param enabled Value to set
*       @return Refer to BTA_Status          */
DLLEXPORT BTA_Status BTA_CALLCONV BTAsetControlCrcEnabled(BTA_Handle handle, uint8_t enabled);

//----------------------------------------------------------------------------------------------------------------

/**     @brief  Allows to specify which channels will be included in a BTA_Frame
*               see the BLT_FrameMode for possible options
*       @param handle Handle of the device to be used
*       @param frameMode The desired frame-mode                */
DLLEXPORT BTA_Status BTA_CALLCONV BTAsetFrameMode(BTA_Handle handle, BTA_FrameMode frameMode);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Actively requests a frame from the corresponding device
*              The library defines if it triggers the capturing of a new frame, if it waits for the next frame,
*              if it returns the last frame, or if it's not implemented at all
*       @param  handle Handle of the device to be used
*       @param frame Pointer to frame or null on return (needs to be free'd with BTAfreeFrame)
*       @param timeout Timeout to wait if no frame is yet available in [ms]
*                      If timeout == 0 the function waits endlessly for a frame.
*       @return Refer to BTA_Status                                    */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetFrame(BTA_Handle handle, BTA_Frame **frame, uint32_t timeout);

/**     @brief Helper function to free a BTA_Frame structure
*       @param frame The pointer to the frame to be freed
*       @return Refer to BTA_Status        */
DLLEXPORT BTA_Status BTA_CALLCONV BTAfreeFrame(BTA_Frame **frame);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Convenience function for extracting distances from a provided frame
*              If there is no channel with distance data present in the frame, an error is returned
*              The size of the buffer can be determined using 'dataFormat', 'xRes' and 'YRes' from 'frame'
*       @param distBuffer Pointer to the distances on return (null on error)      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetDistances(BTA_Frame *frame, void **distBuffer, BTA_DataFormat *dataFormat, BTA_Unit *unit, uint16_t *xRes, uint16_t *yRes);

/**     @brief Convenience function for extracting amplitudes from a provided frame
*              If there is no channel with amplitude data present in the frame, an error is returned
*              The size of the buffer can be determined using 'dataFormat', 'xRes' and 'YRes' from 'frame'
*       @param ampBuffer Pointer to the amplitudes on return (null on error)      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetAmplitudes(BTA_Frame *frame, void **ampBuffer, BTA_DataFormat *dataFormat, BTA_Unit *unit, uint16_t *xRes, uint16_t *yRes);

/**     @brief Convenience function for extracting flags from a provided frame
*              If there is no channel with flag data present in the frame, an error is returned
*       @param flagBuffer Pointer to the flags on return (null on error)      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetFlags(BTA_Frame *frame, void **flagBuffer, BTA_DataFormat *dataFormat, BTA_Unit *unit, uint16_t *xRes, uint16_t *yRes);

/**     @brief Convenience function for extracting the 3D-Coordinates from a provided frame
*              If there are no channels with coordinate data present in the sensor data, an error is returned
*              The size of the buffer can be determined using 'dataFormat', 'xRes' and 'YRes' from 'frame'
*       @param frame Pointer to the frame containing the data
*       @param xBuffer A pointer to the cartesian x coordinates on return (null on error)
*       @param yBuffer A pointer to the cartesian y coordinates on return (null on error)
*       @param zBuffer A pointer to the cartesian z coordinates on return (null on error)     */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetXYZcoordinates(BTA_Frame *frame, void **xBuffer, void **yBuffer, void **zBuffer, BTA_DataFormat *dataFormat, BTA_Unit *unit, uint16_t *xRes, uint16_t *yRes);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Facilitates setting the integration time for the default capture sequence
*       @param handle Handle of the device to be used
*       @param integrationTime The desired integration time                      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAsetIntegrationTime(BTA_Handle handle, uint32_t integrationTime);

/**     @brief Facilitates the retrieval of the current integration time of the default capture sequence
*       @param handle Handle of the device to be used
*       @param integrationTime The desired integration time       */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetIntegrationTime(BTA_Handle handle, uint32_t *integrationTime);

/**     @brief Facilitates setting the frame rate for the default capture sequence
*       @param handle Handle of the device to be used
*       @param frameRate The desired frame rate                      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAsetFrameRate(BTA_Handle handle, float frameRate);

/**     @brief Facilitates the retrieval of the current theoretical frame rate of the default capture sequence
*       @param handle Handle of the device to be used
*       @param frameRate Pointer containing the frame rate on return */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetFrameRate(BTA_Handle handle, float *frameRate);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Function for setting the distance offset being applied to all pixels equally
*       @param handle Handle of the device to be used
*       @param globalOffset offset in [mm]      
*       @return Refer to BTA_Status                */
DLLEXPORT BTA_Status BTA_CALLCONV BTAsetGlobalOffset(BTA_Handle handle, float globalOffset);


/**     @brief Function for getting the distance offset being applied to all pixels equally
*       @param handle Handle of the device to be used
*       @param globalOffset Pointer to hold offset in mm      
*       @return Refer to BTA_Status                */
DLLEXPORT BTA_Status BTA_CALLCONV BTAgetGlobalOffset(BTA_Handle handle, float *globalOffset);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Reads *registerCount registers starting at the specified register address
*       @param handle Handle of the device to be used
*       @param address The address of the first register to read from
*       @param data Pointer which contains register data on return
*                   The data stream consists of one or more register values, each 4 bytes wide
*       @param registerCount Pointer which contains the number of registers to be read. On return, if not null, it will contain
                      the number of registers actually read
*                     If NULL is passed, one register is read
*       @return BTA_StatusOk is returned if all requested bytes were read. For error codes refer to BTA_Status                       */
DLLEXPORT BTA_Status BTA_CALLCONV BTAreadRegister(BTA_Handle handle, uint32_t address, uint32_t *data, uint32_t *registerCount);

/**     @brief Writes *registerCount registers starting at the specified register address
*       @param handle Handle of the device to be used
*       @param address The address of the first register to write to
*       @param data Pointer which contains register data to be written
*                   The data stream consists of one or more register values, each 4 bytes wide
*       @param registerCount Pointer which contains the number of registers to be written. On return, if not null, it will contain
                      the number of registers actually written
*                     If NULL is passed, one register is written
*       @return BTA_StatusOk is returned if all requested bytes were written. For error codes refer to BTA_Status                      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAwriteRegister(BTA_Handle handle, uint32_t address, uint32_t *data, uint32_t *registerCount);

//----------------------------------------------------------------------------------------------------------------

/**     @brief Initiates a reset of the device
*              Before resetting, the library shall return a status
*       @param  handle Handle of the device to be used
*       @return Refer to BTA_Status                                      */
DLLEXPORT BTA_Status BTA_CALLCONV BTAsendReset(BTA_Handle handle);

//----------------------------------------------------------------------------------------------------------------

/**     @brief  Callback function to report status and progress during transfer and programming
*       @param  status refer to BTA_Status
*       @param  percentage Contains the progress in [%]
*                              0: File transfer started (can only be reported once per file transfer)
*                            100: File transfer finished (can only be reported once per file transfer)                    */
typedef int (BTA_CALLCONV *FN_BTA_ProgressReport)(BTA_Status status, uint8_t percentage);

/**     @brief  Sends a file and a set of parameters to the sensor.
*               Mainly this is used for sending calibration data and firmware updates.
*               This function handles the complete transfer of the file and returns after a successful finish (or an error).
*               If possible, this function performs its task in a way that allows the OS to
*               regain control periodically, i.e. it uses blocking functions in a fine granularity.
*               The callback function is called (if not null) in the following cases:
*               - An error occurs -> report <error> with any percentage
*               - An transmission starts -> report BTA_StatusOk with percentage 0%
*               - An transmission ends -> report BTA_StatusOk with percentage 100%
*               (--> so the callback will always be used at least once and at least twice in case of success)
*               During transmission, progress is reported repeatedly when possible
*       @param  handle Handle of the device to be used
*       @param  config Contains the data and all the necessary information for handling it
*       @param  progressReport Callback function for reporting the status and progress during transfer and programming
*       @return Refer to BTA_Status                                     */
DLLEXPORT BTA_Status BTA_CALLCONV BTAflashUpdate(BTA_Handle handle, BTA_FlashUpdateConfig *config, FN_BTA_ProgressReport progressReport);

/**     @brief  Convenience function for doing a firmware update. Uses BTAflashUpdate() internally. 
*       @param  handle Handle of the device to be used.
*       @param  filename Name of the firmware binary file.
*       @param  progressReport Callback function for reporting the status and progress during transfer and programming
*       @return Refer to BTA_Status                                     */
DLLEXPORT BTA_Status BTA_CALLCONV BTAfirmwareUpdate(BTA_Handle handle, const uint8_t *filename, FN_BTA_ProgressReport progressReport);

/**     @brief  Writes the current configuration (= register settings) to non-volatile memory. 
*       @param  handle Handle of the device to be used.
*       @return Refer to BTA_Status                                     */
DLLEXPORT BTA_Status BTA_CALLCONV BTAwriteCurrentConfigToNvm(BTA_Handle handle);

/**     @brief  Restores the default configuration (but does not save it). 
*       @param  handle Handle of the device to be used.
*       @return Refer to BTA_Status                                     */
DLLEXPORT BTA_Status BTA_CALLCONV BTArestoreDefaultConfig(BTA_Handle handle);

#ifdef __cplusplus
}
#endif

#endif
