#ifndef TinyFrameH
#define TinyFrameH

/**
 * TinyFrame protocol library
 *
 * (c) Ondřej Hruška 2017-2018, MIT License
 * no liability/warranty, free for any use, must retain this notice & license
 *
 * Upstream URL: https://github.com/MightyPork/TinyFrame
 */

#define TF_VERSION "2.3.0"

//---------------------------------------------------------------------------
#include <stdint.h>  // for uint8_t etc
#include <stdbool.h> // for bool
#include <stddef.h>  // for NULL
#include <string.h>  // for memset()
#include "adt3102_type_define.h"
//---------------------------------------------------------------------------

// Checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32)
#define TF_CKSUM_NONE  0  // no checksums
#define TF_CKSUM_XOR   8  // inverted xor of all payload bytes
#define TF_CKSUM_CRC8  9  // Dallas/Maxim CRC8 (1-wire)
#define TF_CKSUM_CRC16 16 // CRC16 with the polynomial 0x8005 (x^16 + x^15 + x^2 + 1)
#define TF_CKSUM_CRC32 32 // CRC32 with the polynomial 0xedb88320
#define TF_CKSUM_CUSTOM8  1  // Custom 8-bit checksum
#define TF_CKSUM_CUSTOM16 2  // Custom 16-bit checksum
#define TF_CKSUM_CUSTOM32 3  // Custom 32-bit checksum

#include "TF_Config.h"

//adt3012 Tx frame design
/*
1,Tx
  TYPE  | data type |
 0x0A00 |   float   | number(uint32) array(range, velocity, power, phase0, phase1, angleH, angleV)
 0x0A01 |   float   | number(uint32) array(range, power, phase1, angleH)
 0x0A02 |   float   | number(uint32) array(range, phase1)
 0x0A03 |   float   | breathrate
 ---------------------------------------------------------------------------------------------------
 0x0B00 |   int16   | adc0 I data
 0x0B01 |   int16   | adc0 Q data
 0x0B02 |   int16   | adc1 I data
 0x0B03 |   int16   | adc1 Q data
 0x0B04 |   int16   | adc0 IQ data
 0x0B05 |   int16   | adc1 IQ data
 0x0B06 |   int16   | 0x0055: frame ADC transfer start, 0xaa: frame ADC transfer stop
 ---------------------------------------------------------------------------------------------------
 0x0Cxx |   int16   | adc0 IQ range fft data(real and image part)
 0x0Dxx |   int16   | adc1 IQ range fft data(real and image part)
 ---------------------------------------------------------------------------------------------------
 0x0Exx |   int16   | adc0 and adc1 IQ velocity fft data
 0x0F00 |   uint16  | 0x0055: taget found; 0x00aa: taget lose
 ---------------------------------------------------------------------------------------------------
 0x0100 |   int16   | printf to gui


2,Rx

  TYPE  | data type |
 0x0100 |   float   | powerThresold
 0x0101 |   uint32  | rmStaticTarget
 0x0102 |   uint32  | getStaticTarget
 0x0103 |   float   | velocityThresold
*/
//#define ADT_TF_TYPE 0x0A02
#define TF_RX_BUF_SIZE 128
#define TARGET_NUM 256
#define ADC_NUM SAMPLE_POINT*2
/**0x0A00
example 
    uint16  num = 0;
    TF_Msg  msg_tmp;
    
    store target data : num++;
    
    msg_tmp.len = 2+num*7;
    multTargetInfo.targetNum = num;
    TinyFrame * ft_tmp =NULL;
    ft_tmp = TF_Init(TF_SLAVE);
    if(ft_tmp == NULL)
    {
        ft_tmp = TF_Init(TF_SLAVE);
    }
    if(ft_tmp == NULL)
    {
        //printf("\n\r cfar mallco err \n\r");
    } else
    {
        TF_SendSimple(ft_tmp,0x0A00,(uint8*)&multTargetInfo,msg_tmp.len);
        TF_DeInit(ft_tmp);
    }
*/
#if ADT_TF_TYPE==0x0A00
typedef struct targetInfo
{
    float range;
    float velocity;
    float power; 
    float phase0; 
    float phase1;
    float angleH; 
    float angleV;
} OneTargetInfo;
#elif ADT_TF_TYPE==0x0A01
typedef struct targetInfo
{
    float range;
    float power;  
    float phase1;
    float angleH; 
} OneTargetInfo;
#elif ADT_TF_TYPE==0x0A02
typedef struct targetInfo
{
    float range;
    float phase1;

} OneTargetInfo;
#elif ADT_TF_TYPE==0x0A03
typedef struct targetInfo
{
    float rate;

} OneTargetInfo;
#elif ADT_TF_TYPE==0x0A04
typedef struct targetInfo
{
    float x;
    float y;
    int32  dop_idx; 
    int32 cluster_id;

} OneTargetInfo;
#elif ADT_TF_TYPE==0x0A05
typedef struct targetInfo
{
    float x;
    float y;
    int32  dop_idx; 
    int32 cluster_id;

} OneTargetInfo;
#elif ADT_TF_TYPE==0x0A08
typedef struct targetInfo
{
    float x;
    float y;
    int32  dop_idx; 
    int32 cluster_id;

} OneTargetInfo;
#elif ADT_TF_TYPE==0x0A10
typedef struct targetInfo
{
    float range;
    float velocity;    
    float power;
    uint32 detected;

} OneTargetInfo;
#else //
typedef struct targetInfo
{
    float range;
    //float velocity;    
    //float power;
    //uint32 detected;
} OneTargetInfo;
#endif

typedef struct 
{
    uint32 targetNum;
    OneTargetInfo multTarget[TARGET_NUM];
} multTargetInfo;

typedef struct 
{
    uint16 targetFound;
    uint16 targetLose;
} targetState;

//adt3012 Rx frame design
/**
  TYPE  | data type | 
 0x0100 |   float   | powerThresold
 0x0101 |   uint32  | rmStaticTarget
 0x0102 |   uint32  | getStaticTarget
 0x0103 |   float   | velocityThresold
*/
typedef struct 
{
    uint16 receiveType;
    int32 revuint32Data;
    float revfloatData;
} TinyFrameRx;

typedef struct 
{
    uint8 project;
    uint8 version_major;
    uint8 version_minor;
    uint8 version_patch;
} verStatusTypedef;


void tfTxExample(void);
void tfRxExample(void);
TinyFrameRx tinyFramefRx(void);
int16 printfTf(const char *format, ...);
//region Resolve data types

#if TF_LEN_BYTES == 1
    typedef uint8_t TF_LEN;
#elif TF_LEN_BYTES == 2
    typedef uint16_t TF_LEN;
#elif TF_LEN_BYTES == 4
    typedef uint32_t TF_LEN;
#else
    #error Bad value of TF_LEN_BYTES, must be 1, 2 or 4
#endif


#if TF_TYPE_BYTES == 1
    typedef uint8_t TF_TYPE;
#elif TF_TYPE_BYTES == 2
    typedef uint16_t TF_TYPE;
#elif TF_TYPE_BYTES == 4
    typedef uint32_t TF_TYPE;
#else
    #error Bad value of TF_TYPE_BYTES, must be 1, 2 or 4
#endif


#if TF_ID_BYTES == 1
    typedef uint8_t TF_ID;
#elif TF_ID_BYTES == 2
    typedef uint16_t TF_ID;
#elif TF_ID_BYTES == 4
    typedef uint32_t TF_ID;
#else
    #error Bad value of TF_ID_BYTES, must be 1, 2 or 4
#endif


#if (TF_CKSUM_TYPE == TF_CKSUM_XOR) || (TF_CKSUM_TYPE == TF_CKSUM_NONE) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM8) || (TF_CKSUM_TYPE == TF_CKSUM_CRC8)
    // ~XOR (if 0, still use 1 byte - it won't be used)
    typedef uint8_t TF_CKSUM;
#elif (TF_CKSUM_TYPE == TF_CKSUM_CRC16) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM16)
    // CRC16
    typedef uint16_t TF_CKSUM;
#elif (TF_CKSUM_TYPE == TF_CKSUM_CRC32) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM32)
    // CRC32
    typedef uint32_t TF_CKSUM;
#else
    #error Bad value for TF_CKSUM_TYPE
#endif

//endregion

//---------------------------------------------------------------------------
void tinyFramefTx(TF_TYPE type, uint8 *data, TF_LEN len);
/** Peer bit enum (used for init) */
typedef enum {
    TF_SLAVE = 0,
    TF_MASTER = 1,
} TF_Peer;


/** Response from listeners */
typedef enum {
    TF_NEXT = 0,   //!< Not handled, let other listeners handle it
    TF_STAY = 1,   //!< Handled, stay
    TF_RENEW = 2,  //!< Handled, stay, renew - useful only with listener timeout
    TF_CLOSE = 3,  //!< Handled, remove self
} TF_Result;


/** Data structure for sending / receiving messages */
typedef struct TF_Msg_ {
    TF_ID frame_id;       //!< message ID
    bool is_response;     //!< internal flag, set when using the Respond function. frame_id is then kept unchanged.
    TF_TYPE type;         //!< received or sent message type

    /**
     * Buffer of received data, or data to send.
     *
     * - If (data == NULL) in an ID listener, that means the listener timed out and
     *   the user should free any userdata and take other appropriate actions.
     *
     * - If (data == NULL) and length is not zero when sending a frame, that starts a multi-part frame.
     *   This call then must be followed by sending the payload and closing the frame.
     */
    const uint8_t *data;
    TF_LEN len; //!< length of the payload

    /**
     * Custom user data for the ID listener.
     *
     * This data will be stored in the listener slot and passed to the ID callback
     * via those same fields on the received message.
     */
    void *userdata;
    void *userdata2;
} TF_Msg;

/**
 * Clear message struct
 *
 * @param msg - message to clear in-place
 */
static inline void TF_ClearMsg(TF_Msg *msg)
{
    memset(msg, 0, sizeof(TF_Msg));
}

/** TinyFrame struct typedef */
typedef struct TinyFrame_ TinyFrame;

/**
 * TinyFrame Type Listener callback
 *
 * @param tf - instance
 * @param msg - the received message, userdata is populated inside the object
 * @return listener result
 */
typedef TF_Result (*TF_Listener)(TinyFrame *tf, TF_Msg *msg);


// ---------------------------------- INIT ------------------------------

/**
 * Initialize the TinyFrame engine.
 * This can also be used to completely reset it (removing all listeners etc).
 *
 * The field .userdata (or .usertag) can be used to identify different instances
 * in the TF_WriteImpl() function etc. Set this field after the init.
 *
 * This function is a wrapper around TF_InitStatic that calls malloc() to obtain
 * the instance.
 *
 * @param tf - instance
 * @param peer_bit - peer bit to use for self
 * @return TF instance or NULL
 */
TinyFrame *TF_Init(TF_Peer peer_bit);


/**
 * Initialize the TinyFrame engine using a statically allocated instance struct.
 *
 * The .userdata / .usertag field is preserved when TF_InitStatic is called.
 *
 * @param tf - instance
 * @param peer_bit - peer bit to use for self
 * @return success
 */
bool TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit);

/**
 * De-init the dynamically allocated TF instance
 *
 * @param tf - instance
 */
void TF_DeInit(TinyFrame *tf);


// ---------------------------------- API CALLS --------------------------------------

/**
 * Accept incoming bytes & parse frames
 *
 * @param tf - instance
 * @param buffer - byte buffer to process
 * @param count - nr of bytes in the buffer
 */
char TF_Accept(TinyFrame *tf, const uint8_t *buffer, uint32_t count);

/**
 * Accept a single incoming byte
 *
 * @param tf - instance
 * @param c - a received char
 *accept state {fail =0;sucesse =0xff}
 */
 char  TF_AcceptChar(TinyFrame *tf, uint8_t c);

/**
 * This function should be called periodically.
 * The time base is used to time-out partial frames in the parser and
 * automatically reset it.
 * It's also used to expire ID listeners if a timeout is set when registering them.
 *
 * A common place to call this from is the SysTick handler.
 *
 * @param tf - instance
 */
void TF_Tick(TinyFrame *tf);

/**
 * Reset the frame parser state machine.
 * This does not affect registered listeners.
 *
 * @param tf - instance
 */
void TF_ResetParser(TinyFrame *tf);


// ---------------------------- MESSAGE LISTENERS -------------------------------

/**
 * Register a frame type listener.
 *
 * @param tf - instance
 * @param msg - message (contains frame_id and userdata)
 * @param cb - callback
 * @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever)
 * @return slot index (for removing), or TF_ERROR (-1)
 */
bool TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout);

/**
 * Remove a listener by the message ID it's registered for
 *
 * @param tf - instance
 * @param frame_id - the frame we're listening for
 */
bool TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id);

/**
 * Register a frame type listener.
 *
 * @param tf - instance
 * @param frame_type - frame type to listen for
 * @param cb - callback
 * @return slot index (for removing), or TF_ERROR (-1)
 */
bool TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb);

/**
 * Remove a listener by type.
 *
 * @param tf - instance
 * @param type - the type it's registered for
 */
bool TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type);

/**
 * Register a generic listener.
 *
 * @param tf - instance
 * @param cb - callback
 * @return slot index (for removing), or TF_ERROR (-1)
 */
bool TF_AddGenericListener(TinyFrame *tf, TF_Listener cb);

/**
 * Remove a generic listener by function pointer
 *
 * @param tf - instance
 * @param cb - callback function to remove
 */
bool TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb);

/**
 * Renew an ID listener timeout externally (as opposed to by returning TF_RENEW from the ID listener)
 *
 * @param tf - instance
 * @param id - listener ID to renew
 * @return true if listener was found and renewed
 */
bool TF_RenewIdListener(TinyFrame *tf, TF_ID id);


// ---------------------------- FRAME TX FUNCTIONS ------------------------------

/**
 * Send a frame, no listener
 *
 * @param tf - instance
 * @param msg - message struct. ID is stored in the frame_id field
 * @return success
 */
bool TF_Send(TinyFrame *tf, TF_Msg *msg);

/**
 * Like TF_Send, but without the struct
 */
bool TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len);

/**
 * Send a frame, and optionally attach an ID listener.
 *
 * @param tf - instance
 * @param msg - message struct. ID is stored in the frame_id field
 * @param listener - listener waiting for the response (can be NULL)
 * @param timeout - listener expiry time in ticks
 * @return success
 */
bool TF_Query(TinyFrame *tf, TF_Msg *msg,
              TF_Listener listener, TF_TICKS timeout);

/**
 * Like TF_Query(), but without the struct
 */
bool TF_QuerySimple(TinyFrame *tf, TF_TYPE type,
                    const uint8_t *data, TF_LEN len,
                    TF_Listener listener, TF_TICKS timeout);

/**
 * Send a response to a received message.
 *
 * @param tf - instance
 * @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout
 * @return success
 */
bool TF_Respond(TinyFrame *tf, TF_Msg *msg);


// ------------------------ MULTIPART FRAME TX FUNCTIONS -----------------------------
// Those routines are used to send long frames without having all the data available
// at once (e.g. capturing it from a peripheral or reading from a large memory buffer)

/**
 * TF_Send() with multipart payload.
 * msg.data is ignored and set to NULL
 */
bool TF_Send_Multipart(TinyFrame *tf, TF_Msg *msg);

/**
 * TF_SendSimple() with multipart payload.
 */
bool TF_SendSimple_Multipart(TinyFrame *tf, TF_TYPE type, TF_LEN len);

/**
 * TF_QuerySimple() with multipart payload.
 */
bool TF_QuerySimple_Multipart(TinyFrame *tf, TF_TYPE type, TF_LEN len, TF_Listener listener, TF_TICKS timeout);

/**
 * TF_Query() with multipart payload.
 * msg.data is ignored and set to NULL
 */
bool TF_Query_Multipart(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout);

/**
 * TF_Respond() with multipart payload.
 * msg.data is ignored and set to NULL
 */
void TF_Respond_Multipart(TinyFrame *tf, TF_Msg *msg);

/**
 * Send the payload for a started multipart frame. This can be called multiple times
 * if needed, until the full length is transmitted.
 *
 * @param tf - instance
 * @param buff - buffer to send bytes from
 * @param length - number of bytes to send
 */
void TF_Multipart_Payload(TinyFrame *tf, const uint8_t *buff, uint32_t length);

/**
 * Close the multipart message, generating chekcsum and releasing the Tx lock.
 *
 * @param tf - instance
 */
void TF_Multipart_Close(TinyFrame *tf);

void tinyFrameInit(void);


// ---------------------------------- INTERNAL ----------------------------------
// This is publicly visible only to allow static init.

enum TF_State_ {
    TFState_SOF = 0,      //!< Wait for SOF
    TFState_LEN,          //!< Wait for Number Of Bytes
    TFState_HEAD_CKSUM,   //!< Wait for header Checksum
    TFState_ID,           //!< Wait for ID
    TFState_TYPE,         //!< Wait for message type
    TFState_DATA,         //!< Receive payload
    TFState_DATA_CKSUM    //!< Wait for Checksum
};

struct TF_IdListener_ {
    TF_ID id;
    TF_Listener fn;
    TF_TICKS timeout;     // nr of ticks remaining to disable this listener
    TF_TICKS timeout_max; // the original timeout is stored here (0 = no timeout)
    void *userdata;
    void *userdata2;
};

struct TF_TypeListener_ {
    TF_TYPE type;
    TF_Listener fn;
};

struct TF_GenericListener_ {
    TF_Listener fn;
};

/**
 * Frame parser internal state.
 */
struct TinyFrame_ {
    /* Public user data */
    void *userdata;
    uint32_t usertag;

    // --- the rest of the struct is internal, do not access directly ---

    /* Own state */
    TF_Peer peer_bit;       //!< Own peer bit (unqiue to avoid msg ID clash)
    TF_ID next_id;          //!< Next frame / frame chain ID

    /* Parser state */
    enum TF_State_ state;
    TF_TICKS parser_timeout_ticks;
    TF_ID id;               //!< Incoming packet ID
    TF_LEN len;             //!< Payload length
    uint8_t data[TF_MAX_PAYLOAD_RX]; //!< Data byte buffer
    TF_LEN rxi;             //!< Field size byte counter
    TF_CKSUM cksum;         //!< Checksum calculated of the data stream
    TF_CKSUM ref_cksum;     //!< Reference checksum read from the message
    TF_TYPE type;           //!< Collected message type number
    bool discard_data;      //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data.

    /* Tx state */
    // Buffer for building frames
    uint8_t sendbuf[TF_SENDBUF_LEN]; //!< Transmit temporary buffer

    uint32_t tx_pos;        //!< Next write position in the Tx buffer (used for multipart)
    uint32_t tx_len;        //!< Total expected Tx length
    TF_CKSUM tx_cksum;      //!< Transmit checksum accumulator

#if !TF_USE_MUTEX
    bool soft_lock;         //!< Tx lock flag used if the mutex feature is not enabled.
#endif

    /* --- Callbacks --- */

    /* Transaction callbacks */
    struct TF_IdListener_ id_listeners[TF_MAX_ID_LST];
    struct TF_TypeListener_ type_listeners[TF_MAX_TYPE_LST];
    struct TF_GenericListener_ generic_listeners[TF_MAX_GEN_LST];

    // Those counters are used to optimize look-up times.
    // They point to the highest used slot number,
    // or close to it, depending on the removal order.
    TF_COUNT count_id_lst;
    TF_COUNT count_type_lst;
    TF_COUNT count_generic_lst;
};


// ------------------------ TO BE IMPLEMENTED BY USER ------------------------

/**
 * 'Write bytes' function that sends data to UART
 *
 * ! Implement this in your application code !
 */
extern void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len);

// Mutex functions
#if TF_USE_MUTEX

    /** Claim the TX interface before composing and sending a frame */
    extern bool TF_ClaimTx(TinyFrame *tf);

    /** Free the TX interface after composing and sending a frame */
    extern void TF_ReleaseTx(TinyFrame *tf);

#endif

// Custom checksum functions
#if (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM8) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM16) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM32)

    /**
     * Initialize a checksum
     *
     * @return initial checksum value
     */
    extern TF_CKSUM TF_CksumStart(void);

    /**
     * Update a checksum with a byte
     *
     * @param cksum - previous checksum value
     * @param byte - byte to add
     * @return updated checksum value
     */
    extern TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte);

    /**
     * Finalize the checksum calculation
     *
     * @param cksum - previous checksum value
     * @return final checksum value
     */
    extern TF_CKSUM TF_CksumEnd(TF_CKSUM cksum);

#endif

#endif
