#ifndef USBTMCLIB_BASIC_H
#define USBTMCLIB_BASIC_H

    // USB Test And Measurement Control Protocol (TMC)
    // Library version 1.0
    // 23/07/19
    // Sychov A.
    //----------------------------------------------------------------
    // Refer to:
    // [1] USBTMC Standard, rev. 1.0, 14/04/2003
    //     "Universal Serial Bus Test and Measurement Class Specification (USBTMC)"
    // [2] USBTMC-USB488 Standard, rev. 1.0, 14/04/2003
    //     "Universal Serial Bus Test and Measurement Class, Subclass USB488 Specification (USBTMC-USB488)
    //----------------------------------------------------------------

    #include <stddef.h>
    #include <stdint.h>
    #include "usbtmclib/usbtmclib_types.h"
    #include "usbtmclib/usbtmclib_requests.h"
    #include "usbtmclib/usbtmclib_errors.h"
    #include "app/usb/usb_transfers.h"
    #include "app/usb/usb_transfer_flags.h"

    #define USBTMC_VERSION 0x0100 // 4.2.1.8 GET_CAPABILITIES, table 37, [1]
    #define USB488_VERSION 0x0100 // 4.2.2 GET_CAPABILITIES, table 8, [2]

    typedef struct
    {
        bool bInProgress;
        uint32_t transferSize;
        uint32_t bytesCounter;
        bool bEndOfMessage;
        sUSBTransfer_t * transf;
        bool bNewTransfer;

        // @bytesCounter for BulkOut:
        // The total number of USBTMC message data bytes (not including
        // Bulk-OUT Header or alignment bytes) in the transfer received,
        // and not discarded, by the device

        // @bytesCounter for BulkIn:
        // The total number of USBTMC message data bytes (not including
        // Bulk-IN Header or alignment bytes) sent in the transfer

        // @bEndOfMessage for BulkOut:
        // Specifies if the EOM flag is set in Bulk-OUT header
        // See @bm_DEV_DEP_MSG_OUT_EOM for details.

        // @transf for BulkOUT:
        // Receiving buffer object
        // @transf for BulkIN:
        // Transmitting buffer object

        // @bNewTransfer for BulkOUT:
        // Set if new transfer just has been started

        // bLatestTag - the latest aborted/completed transaction bTag
        uint8_t bLatestTag;
    }
    sUSBTMCTransferState_t;

    typedef struct
    {
        bool bInProgress;

        uint32_t bytesCounter;
        bool bEndOfMessage;

        // @STB - status byte (IEEE 488.1/IEEE 488.2)
        // The part of USBTMC-USB488 Interrupt-IN Header, [2], 3.4 Interrupt-IN
        uint8_t STB;

        // @bTag - transfer identifier (bTag), [2], 4.3.1 READ_STATUS_BYTE
        // The part of USBTMC-USB488 Interrupt-IN Header, [2], 3.4 Interrupt-IN
        uint8_t bTag;

        // @transf for InterruptIn:
        // Transmitting buffer object
        sUSBTransfer_t * transf;

        // @bNewTransfer for InterruptIn:
        // Set if new transfer just has been started
        bool bNewTransfer;
    }
    sUSBTMCIntTransferState_t;

    typedef struct
    {
        sUSBTMCBulkOutHeader_t bulkOutHeader;
        sUSBTMCBulkInHeader_t  bulkInHeader;
        sUSBTMCNotificationHeader_t interruptInHeader;
        sUSBTMCTransferState_t bulkOutState;
        sUSBTMCTransferState_t bulkInState;
        sUSBTMCIntTransferState_t interruptInState;
        sUSBTMCBulkInHeader_t * psActiveInHeader;
        sUSBTMCNotificationHeader_t * psActiveNotificationHeader;
    }
    sUSBTMCContext_t;

    typedef enum
    {
        tmclib_status_min = -2147483648,                 
        tmclib_status_invalid_param = -10,               // ERROR/FAIL
        tmclib_status_invalid_length = -9,               // ERROR/FAIL
        tmclib_status_halt_bulkin = -8,                  // ERROR/FAIL
        tmclib_status_unsupported_termchar = -7,         // ERROR/FAIL
        tmclib_status_unsupported_message_error = -6,    // ERROR/FAIL
        tmclib_status_invalid_message_error = -5,        // ERROR/FAIL
        tmclib_status_header_error = -4,                 // ERROR/FAIL
        tmclib_status_read_error = -3,                   // ERROR/FAIL
        tmclib_status_header_fragmented_error = -2,      // ERROR/FAIL 
        tmclib_status_failure = -1,                      // ERROR/FAIL
        tmclib_status_success =  0,                      // SUCCESS/OK
        tmclib_status_in_progress = 1,                   // SIGNAL/WARNING
        tmclib_status_not_in_progress = 2,               // SIGNAL/WARNING
        tmclib_status_again = 3,                         // SIGNAL/WARNING
        tmclib_status_need_data = 4,                     // SIGNAL/WARNING
        tmclib_status_need_read = 5,                     // SIGNAL/WARNING
        tmclib_status_no_more_data = 6,                  // SIGNAL/WARNING
        tmclib_status_max = 2147483647,
    }
    eTMCLibStatus_t;

    // @eTMCLibEvent_t
    // Common TMCLibrary event identifier.
    // Is used in tmclib_*_event() routines
    typedef enum
    {
        eTMCEventInterruptInStart,  // InterruptIn initialize (only for Interrupt-IN)
        eTMCEventBulkIn,            // BulkIn event
        eTMCEventBulkOut,           // BulkOut event
        eTMCEventInterruptIn,       // InterruptIn event      
        eTMCEventBulkInStop,        // BulkIn abort
        eTMCEventBulkOutStop,       // BulkOut abort
        eTMCEventInterruptInStop,   // InterruptIn abort
        eTMCEventDeviceClear,       // Common Clear Event (*RST or Device Clear)
    }
    eTMCLibEvent_t;

    typedef struct
    {
        sUSBTransfer_t * tx;
    }
    sTMCBulkInEventCtx_t;
    #define DEFINE_tmclib_event_context_bulkin( _name, _tx ) \
                                                     sTMCEventContext_t eventCtx = { \
                                                          .event = (eTMCEventBulkIn),\
                                                          .bulkIn = { \
                                                                .tx = (_tx) \
                                                           } \
                                                     }

    #define COMPOUND_tmclib_event_context_bulkin( _name, _tx ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = (eTMCEventBulkIn),\
                                                          .bulkIn = { \
                                                                .tx = (_tx) \
                                                           } \
                                                     })

    typedef struct
    {
        sUSBTransfer_t * rx;
    }
    sTMCBulkOutEventCtx_t;
    #define DEFINE_tmclib_event_context_bulkout( _name, _rx ) \
                                                     sTMCEventContext_t eventCtx = { \
                                                          .event = (eTMCEventBulkOut),\
                                                          .bulkOut = { \
                                                                .rx = (_rx) \
                                                           } \
                                                     }

    #define COMPOUND_tmclib_event_context_bulkout( _name, _rx ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = (eTMCEventBulkOut),\
                                                          .bulkOut = { \
                                                                .rx = (_rx) \
                                                           } \
                                                     })
    typedef struct
    {
        sUSBTransfer_t * ntx;
        uint8_t bTag;
    }
    sTMCInterruptInEventCtx_t;
    #define DEFINE_tmclib_event_context_interruptin_start( _name, _ntx, _btag ) \
                                                     sTMCEventContext_t _name = { \
                                                          .event = (eTMCEventInterruptInStart),\
                                                          .interruptIn = { \
                                                                .ntx = (_ntx),\
                                                                .bTag = (_btag) \
                                                          } \
                                                     }
    #define DEFINE_tmclib_event_context_interruptin( _name, _ntx ) \
                                                     sTMCEventContext_t _name = { \
                                                          .event = (eTMCEventInterruptIn),\
                                                          .interruptIn = { \
                                                                .ntx = (_ntx),\
                                                                .bTag = (0) \
                                                          } \
                                                     }
    #define COMPOUND_tmclib_event_context_interruptin_start( _name, _ntx, _btag ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = (eTMCEventInterruptInStart),\
                                                          .interruptIn = { \
                                                                .ntx = (_ntx),\
                                                                .bTag = (_btag) \
                                                          } \
                                                     })
    #define COMPOUND_tmclib_event_context_interruptin( _name, _ntx ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = (eTMCEventInterruptIn),\
                                                          .interruptIn = { \
                                                                .ntx = (_ntx),\
                                                                .bTag = (0) \
                                                          } \
                                                     })
    typedef struct
    {
        // @force - makes to ignore the @transfer_bTag if true
        bool force;

        // @clearShortPacketIndicator - clear Short Packet Indicator assotiated with USB transfer
        bool clearShortPacketIndicator;

        // @noResetTransfer
        // By default shall be false.
        // If set, the transfer will not be reset. Is used only if buffer contains more data than
        // expected for the transfer
        bool noResetTransfer;

        // @transfer_bTag - The bTag value associated with the transfer to abort,
        //  - BulkOut: see 4.2.1.2 [1] 
        //  - BulkIn: see 4.2.1.4 [1]
        //  - InterruptIn: see 4.3.1 [2]
        uint8_t transfer_bTag;
    }
    sTMCAbortEventCtx_t;

    typedef struct
    {
        // @resetTransport - reset all the USBTMC transfers
        bool resetTransport;
    }
    sTMCDeviceClearEventCtx_t;

    #define DEFINE_tmclib_event_context_abort_bulkout( _name, _btag, _force ) \
                                                     sTMCEventContext_t _name = { \
                                                          .event = eTMCEventBulkOutStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (false), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     }

    #define DEFINE_tmclib_event_context_abort_bulkin( _name, _btag, _force ) \
                                                     sTMCEventContext_t _name = { \
                                                          .event = eTMCEventBulkInStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (true), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     }

    #define DEFINE_tmclib_event_context_complete_bulkin( _name, _btag ) \
                                                     sTMCEventContext_t _name = { \
                                                          .event = eTMCEventBulkInStop,\
                                                          .abort = { \
                                                                .force = (false),\
                                                                .clearShortPacketIndicator = (false), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     }

    #define DEFINE_tmclib_event_context_abort_interruptin( _name, _btag, _force ) \
                                                     sTMCEventContext_t _name = { \
                                                          .event = eTMCEventInterruptInStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (false), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     }

    #define COMPOUND_tmclib_event_context_abort_bulkout( _btag, _force ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = eTMCEventBulkOutStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (false), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     })

    #define COMPOUND_tmclib_event_context_abort_bulkout_noreset( _btag, _force ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = eTMCEventBulkOutStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (false), \
                                                                .noResetTransfer = (true), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     })

    #define COMPOUND_tmclib_event_context_abort_bulkin( _btag, _force ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = eTMCEventBulkInStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (true), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     })

    #define COMPOUND_tmclib_event_context_complete_bulkin( _btag, _force ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = eTMCEventBulkInStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (false), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     })

    #define COMPOUND_tmclib_event_context_abort_interruptin( _btag, _force ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = eTMCEventInterruptInStop,\
                                                          .abort = { \
                                                                .force = (_force),\
                                                                .clearShortPacketIndicator = (false), \
                                                                .noResetTransfer = (false), \
                                                                .transfer_bTag = (_btag) \
                                                          } \
                                                     })

    #define COMPOUND_tmclib_event_context_device_clear( _fullReset ) \
                                                     &((sTMCEventContext_t){ \
                                                          .event = eTMCEventDeviceClear,\
                                                          .clear = { \
                                                                .resetTransport = (_fullReset),\
                                                          } \
                                                     })
    typedef struct
    { 
       eTMCLibEvent_t event;        // event identified
       union
       {
          sTMCBulkInEventCtx_t      bulkIn;        // for events: eTMCEventBulkIn
          sTMCBulkOutEventCtx_t     bulkOut;       // for events: eTMCEventBulkOut
          sTMCInterruptInEventCtx_t interruptIn;   // for events: eTMCEventInterruptIn, eTMCEventBulkInStart
          sTMCAbortEventCtx_t       abort;         // for events: eTMCEventBulkInStop, eTMCEventBulkOutStop, eTMCEventInterruptInStop
          sTMCDeviceClearEventCtx_t clear;         // for events: eTMCEventDeviceClear
       };
    }
    sTMCEventContext_t;


    #define USBTMC_CHECK_BITMAP( Bitmap, Flag )      ((bool)((Bitmap)&(Flag))) 
    #define USBTMC_ERROR(status)                     ( tmclib_status_success > (status) )    
    #define USBTMC_NOTERROR(status)                  ( tmclib_status_success <=(status) )
    #define USBTMC_WARNING(status)                   ( tmclib_status_success < (status) )

    // @tmclib_init()
    // Initialize TMC library
    // @rx - receiving buffer object
    // @tx - transmitting buffer object
    // @ntx - interrupt/notify transmitting buffer object
    eTMCLibStatus_t tmclib_init( sUSBTransfer_t * rx, sUSBTransfer_t * tx, sUSBTransfer_t * ntx );

    // @tmclib_deinit()
    // Deinitialize TMC library
    eTMCLibStatus_t tmclib_deinit();

    // // @tmclib_new_bulkout()
    // // Starts new BulkOUT transfer    
    // eTMCLibStatus_t tmclib_new_bulkout();
    // 
    // // @tmclib_new_bulkin()
    // // Starts new BulkIN transfer
    // eTMCLibStatus_t tmclib_new_bulkin();

    // @tmclib_bulkout_gettag
    // Returns the bTag field for current BulkOut transfer
    // @pbTag - pointer to the bTag to be filled, can not be NULL
    // Note: if there no transaction is, function returns bTag=0
    // Returns: returns @tmclib_status_success if @pbTag is not NULL,
    // otherwise returns @tmclib_status_failure
    eTMCLibStatus_t tmclib_bulkout_gettag( uint8_t * pbTag );

    // @tmclib_bulkin_gettag
    // Returns the bTag field for current BulkIn transfer
    // @pbTag - pointer to the bTag to be filled, can not be NULL
    // Note: if there no transaction is, function returns bTag=0
    // Returns: returns @tmclib_status_success if @pbTag is not NULL,
    // otherwise returns @tmclib_status_failure
    eTMCLibStatus_t tmclib_bulkin_gettag( uint8_t * pbTag );

    // @tmclib_bulkout_gettag_latest
    // Returns the bTag field for latest valid BulkOut transfer
    // @pbTag - pointer to the bTag to be filled, can not be NULL
    // Note: if there no transaction is, function returns bTag for the latest valid 
    // BulkOut transfer, or bTag for current transaction otherwise.
    // Returns: returns @tmclib_status_success if @pbTag is not NULL,
    // otherwise returns @tmclib_status_invalid_param
    eTMCLibStatus_t tmclib_bulkout_gettag_latest( uint8_t * pbTag );

    // @tmclib_bulkin_gettag_latest
    // Returns the bTag field for latest valid BulkIn transfer
    // @pbTag - pointer to the bTag to be filled, can not be NULL
    // Note: if there no transaction is, function returns bTag for the latest valid 
    // BulkIn transfer, or bTag for current transaction otherwise.
    // Returns: returns @tmclib_status_success if @pbTag is not NULL,
    // otherwise returns @tmclib_status_invalid_param
    eTMCLibStatus_t tmclib_bulkin_gettag_latest( uint8_t * pbTag );

    // @tmclib_bulkout_getcounter
    // Returns the bytes counter for current BulkOut transfer
    // @pnBytes - pointer to the uint32_t variable to be filled, can not be NULL
    // Note: if there no transaction is, function returns the counter for the last transfer
    // Returns: returns @tmclib_status_success if @pnBytes is not NULL,
    // otherwise returns @tmclib_status_failure
    eTMCLibStatus_t tmclib_bulkout_getcounter( uint32_t * pnBytes );
    
    // @tmclib_bulkin_getcounter
    // Returns the bytes counter for current BulkIn transfer
    // @pnBytes - pointer to the uint32_t variable to be filled, can not be NULL
    // Note: if there no transaction is, function returns the counter for the last transfer
    // Returns: returns @tmclib_status_success if @pnBytes is not NULL,
    // otherwise returns @tmclib_status_failure
    eTMCLibStatus_t tmclib_bulkin_getcounter( uint32_t * pnBytes );
    
    // @tmclib_generic_event()
    // Processes USBTMC specific event.
    // @eventCtx - event context, all the fields must be filled in accordance to @eventCtx->event code
    // @eventCtx->event - event identifier:
    //
    //  +------------------------------------------------------------------------------------------------------------
    //  * eTMCEventBulkInStop - Aborts currently running BulkIN transfer
    //  |---> @eventCtx->abort:
    //  |       - @transfer_bTag - The bTag value associated with the transfer to abort, see 4.2.1.4 [1]
    //  |       - @force - makes to ignore the @transfer_bTag if true
    //  +------------------------------------------------------------------------------------------------------------
    //  * eTMCEventBulkOutStop - Aborts currently running BulkOUT transfer
    //  |---> @eventCtx->abort:
    //  |       - @transfer_bTag - The bTag value associated with the transfer to abort, see 4.2.1.2 [1]
    //  |       - @force - makes to ignore the @transfer_bTag if true
    //  +------------------------------------------------------------------------------------------------------------
    //  * eTMCEventInterruptInStop - Aborts currently running InterruptIN transfer
    //  |---> @eventCtx->abort:
    //  |       - @transfer_bTag - The bTag value associated with the transfer to abort, see 4.3.1 [2]
    //  |       - @force - makes to ignore the @transfer_bTag if true
    //  +------------------------------------------------------------------------------------------------------------
    //  * eTMCEventBulkIn - Processes USBTMC Bulk-IN event
    //  |---> @eventCtx->bulkIn:
    //  |       - @tx - the buffer object to be used to transmit data
    //  +------------------------------------------------------------------------------------------------------------
    //  * eTMCEventBulkOut - Processes USBTMC Bulk-OUT event
    //  |---> @eventCtx->bulkOut:
    //  |       - @rx - the buffer object to be used to receive data
    //  +------------------------------------------------------------------------------------------------------------
    //  * eTMCEventInterruptInStart - Initialize new Interrupt-IN transfer w/o data processing    
    //  | ...The function only creates new transfer without calling Tx-Handler. This call must be
    //  | ...provided before 'eTMCEventInterruptIn' event to prapare transfer.
    //  |----> @eventCtx->interruptIn:
    //  |        - @ntx - must be NULL, or 'tmclib_status_invalid_param' will be returned otherwise
    //  |        - @bTag - the Tag identifier, [2], 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request,
    //  +------------------------------------------------------------------------------------------------------------
    //  * eTMCEventInterruptIn - Processes USBTMC Interrupt-IN event.
    //  | Note that 'eTMCEventInterruptInStart' must be generated before 'eTMCEventInterruptIn' for normal operation.
    //  | Note: Each event generates a single INTERRUPT-IN packet begining with Interrupt-IN header
    //  | Note: Each interrupt-in event must be not longer than @ntx transfer length.
    //  | Note: In case it is required to send more data than @ntx can carry, you need:
    //  |  - increase Interrupt-IN EP buffer (see USB specification) and increase @ntx transfer space
    //  | or:
    //  |  - send the data using multiple packets (each packet begins with INTERRUPT-IN header), pay
    //  | attention that @ctx->interruptInState will not be reset until user layer return tmclib_status_success
    //  | or tmclib_status_no_more_data.
    //  |----> @eventCtx->interruptIn:
    //  |        - @ntx - the buffer object to be used to transmit data
    //  |        - @bTag - don't care
    //  +------------------------------------------------------------------------------------------------------------
    //       
    // Returns: the operation status:
    //   tmclib_status_success in case of success,
    //   tmclib_status_failure otherwise
    //    
    eTMCLibStatus_t tmclib_generic_event( sTMCEventContext_t * eventCtx );
#endif