#define __CORE_USB_HEADER__
#include "usb/usb_bridge.h"
#include "usb/usb_config.h"
#include "usbd_vendor.h" // PLANAR Protocol
#include "usbd_usbtmc.h" // USBTMC Protocol
#include "core/config.h" // CONFIG_AUTOMAT_MODE_USB_POWERBANK
#include "core/gpio.h"
#include "usbapp/usb_application_switch.h"
#include "usbapp/usb_application_flash.h"
#include "usbapp/usb_application_temp.h"
#if CONFIG_AUTOMAT_MODE_USB_POWERBANK && CONFIG_AUTOMAT_MODE
#include "usbapp/usb_application_enumspy.h"
#endif
#include "usbapp/usb_application_usbtmc.h"
#include "usbapp/usb_application_benchmark.h"
#include "usbapp/usb_application_service.h"
#include "usbapp/usb_application_default_dataapp.h"
#include "usbapp/usb_application_interfaceswitch.h"
#include "usbapp/usb_application_power.h"
#include "usbapp/usb_application_led.h"

// Module usb_bridge
// File: usb_bridge.c
// Description: this module connects the middleware USBD-drivers and user USB
// applications using so calling "bridges".
// The bridge - is a user declared special structure that announces the application
// in the module application pool - this process is also knowed as "registering".
// The registering process is fully static process. Once registerd the application
// can not be unregisted. In the same time there no way to register application in runtime.
// Each USBD-class (VENDOR, USBTMC, etc.) have dedicated application pool.
// For example: VENDOR interface is responsible for PLANAR protocol that used to
// interact to the host applications using VENDOR-requests only via CONTROL transfers.
// This VENDOR interface has application pool: @usbCtlAppList_Vendor.
// Each set of logically combined vendor-specific requests represent "application".
// You must register such sets in the @usbCtlAppList_Vendor.
// All the applications are services in order of registering in the pool.
// The first application that successfully processed the CONTROL-request is considered
// as an application that the request is intended for.
// So you must delegate only unique requests for each application.
// As for BULK transfers, the bridges for bulk transfers work some kind differ.
// Unlike CONTROL transfer, the bulk ones does not relies on SETUP packets that
// could help to identify the application the data is intended for.
// Instead of the SETUP-requests, the data flow is being controlled by the USB endpoint
// address (EP number). Unlike the CONTROL application, each data-application must register
// the data handler dynamically during the CONTROL application initializing.
// So, the data applications are depends on the control applications and can not be used
// separately. The pool for the data applications have equal size as a number of hardware
// possible number of data-endpoints plus one (including the only control endpoint).
// This extra entry (for control endpoint) is reserved in the beginning of the pool and is
// used for the endpoints are not occupated by any data-application, aka default handler.
// Once registed the application can be unregisted in the any time. The endpoint
// the application registered for remains occupied until the application is un-registered.
// So only one data-application can be registered to serve each logical endpoint.

// -----------------------------------------------------------------------------
#define cellsof(A)                        (sizeof(A)/sizeof(A[0]))
#define lastcell(A)                       A[cellsof(A)]
#define foreachidx_ctl_app_vendor(app,i)  for( size_t i = ( app = usbCtlAppList_Vendor[0], 0 ); i < cellsof(usbCtlAppList_Vendor); app = usbCtlAppList_Vendor[++i] )
#define foreach_ctl_app_vendor(app)       foreachidx_ctl_app_vendor(app,__i)
#define foreachidx_ctl_app_usbtmc(app,i)  for( size_t i = ( app = usbCtlAppList_Usbtmc[0], 0 ); i < cellsof(usbCtlAppList_Usbtmc); app = usbCtlAppList_Usbtmc[++i] )
#define foreach_ctl_app_usbtmc(app)       foreachidx_ctl_app_usbtmc(app,__i)
#define foreachidx_data_app_usbtmc(app,i) for( size_t i = ( app = usbDataAppList_Usbtmc[0], 0 ); i < cellsof(usbDataAppList_Usbtmc); app = usbDataAppList_Usbtmc[++i] )
#define foreach_data_app_usbtmc(app)      foreachidx_ctl_app_usbtmc(app,__i)
#define with_data_app_for(ep, app)        if(((ep) > 0)\
                                            && ((0x7F & (ep)) < (MAX_DATAAPP_ENDPOINTS))\
                                            && (NULL != (app = usbDataAppList_Usbtmc[ (0x7F & (ep)) ], app) ) )
#define with_default_data_app(ep, app)    if(((ep) > 0)\
                                            && (NULL != (app = usbDataAppList_Usbtmc[ 0 ], app) ) )
// -----------------------------------------------------------------------------

// PLANAR protocol: application index
// @usbCtlApp_appIdx_Vendor
// stores the active application index the next control transaction
// will be passed for.
static size_t usbCtlApp_appIdx_Vendor = 0;

// USBTMC protocol: application index
// @usbCtlApp_appIdx_Usbtmc
// stores the active application index the next control transaction
// will be passed for.
static size_t usbCtlApp_appIdx_Usbtmc = 0;

/* --- Vendor Specific Applications ---
   PLANAR Protocol:
   1. Application Key Switch [usb_application_switch.h]
   2. Application Flash Control [usb_application_switch.h]
   3. Application Temperature Monitor [usb_application_temp.h]
   4. Application USB-Enumeration Spy [usb_application_enumspy.h]
   ------------------------------------- */

// PLANAR protocol: applications list
// @usbCtlAppList_Vendor
// USB Control Interface applications list
static const sUSBAppEntry_Control_t * const usbCtlAppList_Vendor[] = {

     #if CONFIG_AUTOMAT_MODE_USB_POWERBANK && CONFIG_AUTOMAT_MODE
     &usbapplication_ACM_planarproto_enumspy,
     #endif
     &usbapplication_ACM_planarproto_flash,
     //&usbapplication_ACM_planarproto_temp,
     //&usbapplication_ACM_planarproto_switch,
     &usbapplication_ACM_planarproto_service,
     &usbapplication_ACM_planarproto_interfaceswitch,
     //&usbapplication_ACM_planarproto_power,
     &usbapplication_ACM_planarproto_led
};

// USBTMC protocol: CONTROL-applications list
// @usbCtlAppList_Usbtmc
// USB Control Interface applications list
static const sUSBAppEntry_Control_t * const usbCtlAppList_Usbtmc[] = {

     #if CONFIG_AUTOMAT_MODE_USB_POWERBANK && CONFIG_AUTOMAT_MODE
     &usbapplication_ACM_planarproto_enumspy,
     #endif
     &usbapplication_USBTMC_control,
   //&usbapplication_BENCHMARK_control,
};

// USBTMC protocol: BULK-applications list
// @usbDataAppList_Usbtmc
// USB Bulk Interface applications list
static const sUSBAppEntry_Data_t * usbDataAppList_Usbtmc[ MAX_DATAAPP_ENDPOINTS ] = {

     NULL // no applications registered by default: you must register applications in runtime
};
// -----------------------------------------------------------------------------

// @usb_bridge_register_dataapp
// Register (attach) the data-stream handler @dataApp for the specified endpoint (@epnum)
// After the registering all the data requests relates to the endpoint will be routed to
// the aproptiate handler.
bool usb_bridge_register_dataapp( uint8_t epnum, const sUSBAppEntry_Data_t * dataApp )
{
     epnum = epnum & 0x7F;

     if( ((epnum > 0) && (epnum < MAX_DATAAPP_ENDPOINTS)) && ( NULL != dataApp ) )
     {
         if( NULL == usbDataAppList_Usbtmc[ epnum ] )
         {
             if( NULL == dataApp->fDataInitHandler )
             {
                 usbDataAppList_Usbtmc[ epnum ] = dataApp;
                 return true;
             }
             else
             if( dataApp->fDataInitHandler( epnum ) )
             {
                 usbDataAppList_Usbtmc[ epnum ] = dataApp;
                 return true;
             }
         }
     }

     return false;
}

// @usb_bridge_unregister_dataapp
// De-Register (detach) the data-stream handler @dataApp from the specified endpoint (@epnum)
// After the deregistering all the data requests relates to the endpoint will be routed to
// the default data handler handler or will be discared
bool usb_bridge_unregister_dataapp( uint8_t epnum )
{
     epnum = epnum & 0x7F;

     if( (epnum > 0) && (epnum < MAX_DATAAPP_ENDPOINTS) )
     {
         if( (NULL != usbDataAppList_Usbtmc[ epnum ]) && (NULL != usbDataAppList_Usbtmc[ epnum ]->fDataDeInitHandler) )
         {
             usbDataAppList_Usbtmc[ epnum ]->fDataDeInitHandler( epnum );
         }

         usbDataAppList_Usbtmc[ epnum ] = NULL;

         return true;
     }

     return false;
}

// -----------------------------------------------------------------------------
// PLANAR protocol procedures:
// "usblib"-layer procedures
static int8_t fUsbInit_Vendor();
static void   fUsbReset_Vendor();
static size_t fUsbEp0Rx_Vendor( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining );
static size_t fUsbEp0Tx_Vendor( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining );
static bool   fUsbSetInterface_Vendor( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc );
static void   fUsbClrInterface_Vendor( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc );
static bool   fUsbSetupRx_Vendor( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success );
static int8_t fUsbDeInit_Vendor();

// -----------------------------------------------------------------------------
// PLANAR Protocol: VENDOR bridge descriptor
const USBD_COMMON_ItfTypeDef USBD_Interface_Vendor_UsbInterface =
{
    .fUsbInit        = fUsbInit_Vendor,          //
    .fUsbDeInit      = fUsbDeInit_Vendor,        //
    .fUsbSetup       = fUsbSetupRx_Vendor,       // SETUP-packet filtering
    .fUsbCtlEpRx     = fUsbEp0Rx_Vendor,         // Control-transfer-OUT handler
    .fUsbCtlEpTx     = fUsbEp0Tx_Vendor,         // Control-transfer-IN handler
    .fResetEvent     = fUsbReset_Vendor,         // USB Reset handler
    .fSuspendEvent   = NULL,                     //
    .fSetIface       = fUsbSetInterface_Vendor,  // SetInterface/SetConfiguration request handler
    .fClrIface       = fUsbClrInterface_Vendor,  // SetInterface/SetConfiguration request handler
    .fGetIface       = NULL,                     //
    .fDataRxHandler  = NULL,                     // Bulk-transfer-OUT handler
    .fDataTxHandler  = NULL,                     // Bulk-transfer-IN handler
    .fDataErrHandler = NULL,                     // Bulk-transfer error handler
};
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// USBTMC protocol procedures:
// "usblib"-layer procedures
static int8_t fUsbInit_Usbtmc();
static void   fUsbReset_Usbtmc();
static size_t fUsbEp0Rx_Usbtmc( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining );
static size_t fUsbEp0Tx_Usbtmc( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining );
static bool   fUsbSetInterface_Usbtmc( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc );
static void   fUsbClrInterface_Usbtmc( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc );
static bool   fUsbDataRx_Usbtmc( uint8_t bEpLogAddress, sUSBTransfer_t * rx );
static bool   fUsbDataTx_Usbtmc( uint8_t bEpLogAddress, sUSBTransfer_t * tx );
static bool   fUsbDataErr_Usbtmc( uint8_t bEpLogAddress, uint32_t err );
static bool   fUsbSetupRx_Usbtmc( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success );
static int8_t fUsbDeInit_Usbtmc();

// USBTMC Protocol: USBTMC bridge descriptor
#if CONFIG_USB_USBTMC_ENABLE
const USBD_COMMON_ItfTypeDef USBD_Interface_Usbtmc_UsbInterface =
{
    .fUsbInit        = fUsbInit_Usbtmc,          //
    .fUsbDeInit      = fUsbDeInit_Usbtmc,        //
    .fUsbSetup       = fUsbSetupRx_Usbtmc,       // SETUP-packet filtering
    .fUsbCtlEpRx     = fUsbEp0Rx_Usbtmc,         // Control-transfer-OUT handler
    .fUsbCtlEpTx     = fUsbEp0Tx_Usbtmc,         // Control-transfer-IN handler
    .fResetEvent     = fUsbReset_Usbtmc,         // USB Reset handler
    .fSuspendEvent   = NULL,                     //
    .fSetIface       = fUsbSetInterface_Usbtmc,  // SetInterface/SetConfiguration request handler
    .fClrIface       = fUsbClrInterface_Usbtmc,  // SetInterface/SetConfiguration request handler
    .fGetIface       = NULL,                     //
    .fDataRxHandler  = fUsbDataRx_Usbtmc,        // Bulk-transfer-OUT handler
    .fDataTxHandler  = fUsbDataTx_Usbtmc,        // Bulk-transfer-IN handler
    .fDataErrHandler = fUsbDataErr_Usbtmc,       // Bulk-transfer error handler
};
#endif
// -----------------------------------------------------------------------------


// -----------------------------------------------------------------------------
// fUsbInit_Vendor()
// VENDOR interface: initialize VENDOR bridge
static int8_t fUsbInit_Vendor()
{
   usbCtlApp_appIdx_Vendor = 0;

   const sUSBAppEntry_Control_t * app;
   foreach_ctl_app_vendor( app )
   {
       int8_t ret = app->fUsbInit();  // "fUsbDeInit_Vendor -> fUsbInit_Vendor", Fixed: 08/07/19

       if( ret != 0 ) return ret;
   }

   return 0;
}

// fUsbInit_Usbtmc()
// USBTMC interface: initialize USBTMC bridge
static int8_t fUsbInit_Usbtmc()
{
   usbCtlApp_appIdx_Usbtmc = 0;

   {
     const sUSBAppEntry_Data_t * dataapp;

     foreachidx_data_app_usbtmc( dataapp, dataapp_idx )
     {
         // deinitialize applications
         usbDataAppList_Usbtmc[ dataapp_idx ] = NULL; // data application must be registed in runtime
     }
     (void)dataapp;

     // register default data-application
     usbDataAppList_Usbtmc[ 0 ] = &usbapplication_default_dataapp;
   }
   {
     const sUSBAppEntry_Control_t * ctlapp;

     foreach_ctl_app_usbtmc( ctlapp )
     {
         int8_t ret = ctlapp->fUsbInit();

         if( ret != 0 ) return ret;
     }
   }
   return 0;
}
// -----------------------------------------------------------------------------
// fUsbDeInit_Vendor()
// VENDOR interface: deinitialize VENDOR bridge
static int8_t fUsbDeInit_Vendor()
{
    usbCtlApp_appIdx_Vendor = 0;

    const sUSBAppEntry_Control_t * app;

    foreach_ctl_app_vendor( app )
    {
       app->fUsbDeInit();
    }

    return 0;
}

// fUsbDeInit_Usbtmc()
// USBTMC interface: deinitialize USBTMC bridge
static int8_t fUsbDeInit_Usbtmc()
{
    usbCtlApp_appIdx_Usbtmc = 0;

    {
      const sUSBAppEntry_Control_t * app;

      foreach_ctl_app_usbtmc( app )
      {
         app->fUsbDeInit();
      }
    }
    {
       const sUSBAppEntry_Data_t * dataapp;

       foreachidx_data_app_usbtmc( dataapp, dataapp_idx )
       {
           // destroy application pointer
           usbDataAppList_Usbtmc[ dataapp_idx ] = NULL;
       }
       (void)dataapp;
    }
    return 0;
}
// -----------------------------------------------------------------------------
// fUsbReset_Vendor()
// VENDOR interface: USB-bus Reset handler
static void fUsbReset_Vendor()
{
   const sUSBAppEntry_Control_t * app;

   foreach_ctl_app_vendor( app )
   {
       app->fResetEvent();
   }
}

// fUsbReset_Usbtmc()
// USBTMC interface: USB-bus Reset handler
static void fUsbReset_Usbtmc()
{
   const sUSBAppEntry_Control_t * app;

   foreach_ctl_app_usbtmc( app )
   {
       app->fResetEvent();
   }
}

// -----------------------------------------------------------------------------
// fUsbSetupRx_Vendor()
// VENDOR interface: SETUP-packet interceptor
static bool fUsbSetupRx_Vendor( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success )
{
    if( bFirstStage )
    {
        const sUSBAppEntry_Control_t * app;

        foreachidx_ctl_app_vendor( app, idx )
        {
            if( app->fUsbSetup( pSetup, bFirstStage, success ) )
            {
                usbCtlApp_appIdx_Vendor = idx;
                return true;
            }
        }
    }

    usbCtlApp_appIdx_Vendor = 0;

    return false;
}

// -----------------------------------------------------------------------------
// fUsbSetupRx_Usbtmc()
// USBTMC interface: SETUP-packet interceptor
static bool fUsbSetupRx_Usbtmc( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success )
{
    if( bFirstStage )
    {
        const sUSBAppEntry_Control_t * app;

        foreachidx_ctl_app_usbtmc( app, idx )
        {
            if( app->fUsbSetup( pSetup, bFirstStage, success ) )
            {
                usbCtlApp_appIdx_Usbtmc = idx;
                return true;
            }
        }
    }

    usbCtlApp_appIdx_Usbtmc = 0;

    return false;
}
// -----------------------------------------------------------------------------
// fUsbEp0Rx_Vendor()
// VENDOR interface: Control-transfer-OUT handler
static size_t fUsbEp0Rx_Vendor( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining )
{
     return usbCtlAppList_Vendor[ usbCtlApp_appIdx_Vendor ]->fUsbCtlEpRx( pSetup, rx, idx, bytesRemaining );
}
// -----------------------------------------------------------------------------
// fUsbEp0Rx_Usbtmc()
// USBTMC interface: Control-transfer-OUT handler
static size_t fUsbEp0Rx_Usbtmc( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining )
{
     return usbCtlAppList_Usbtmc[ usbCtlApp_appIdx_Usbtmc ]->fUsbCtlEpRx( pSetup, rx, idx, bytesRemaining );
}
// -----------------------------------------------------------------------------
// fUsbEp0Tx_Vendor()
// VENDOR interface: Control-transfer-IN handler
static size_t fUsbEp0Tx_Vendor( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining )
{
     return usbCtlAppList_Vendor[ usbCtlApp_appIdx_Vendor ]->fUsbCtlEpTx( pSetup, tx, idx, bytesRemaining );
}

// fUsbEp0Tx_Usbtmc()
// USBTMC interface: Control-transfer-IN handler
static size_t fUsbEp0Tx_Usbtmc( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining )
{
     return usbCtlAppList_Usbtmc[ usbCtlApp_appIdx_Usbtmc ]->fUsbCtlEpTx( pSetup, tx, idx, bytesRemaining );
}
// -----------------------------------------------------------------------------
// fUsbSetInterface_Vendor()
// VENDOR interface: USB-interface SetInterface request handler
static bool fUsbSetInterface_Vendor( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc )
{
   if(   (NULL != ifdesc)
      && USB_ACM_CONFIGURATION_VALUE == cfgidx
      && USB_ACM_INTERFACE_VALUE == ifdesc->bInterfaceNumber )
   {
      return true;
   }

   return false;
}

// fUsbSetInterface_Usbtmc()
// USBTMC interface: USB-interface SetInterface request handler
static bool fUsbSetInterface_Usbtmc( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc )
{
   if(   (NULL != ifdesc)
      && USB_TMC_CONFIGURATION_VALUE == cfgidx
      && USB_TMC_INTERFACE_VALUE == ifdesc->bInterfaceNumber )
   {
      return true;
   }

   return false;
}
// -----------------------------------------------------------------------------
// fUsbClrInterface_Vendor()
// VENDOR interface: USB-interface ClearInterface request handler
static void fUsbClrInterface_Vendor( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc )
{
   if(   (NULL != ifdesc)
      && USB_ACM_CONFIGURATION_VALUE == cfgidx
      && USB_ACM_INTERFACE_VALUE == ifdesc->bInterfaceNumber )
   {
      return;
   }
}

// fUsbClrInterface_Usbtmc()
// USBTMC interface: USB-interface ClearInterface request handler
static void fUsbClrInterface_Usbtmc( uint8_t cfgidx, uint8_t ifidx, const sUSBInterfaceDescriptor_t * ifdesc )
{
   if(   (NULL != ifdesc)
      && USB_TMC_CONFIGURATION_VALUE == cfgidx
      && USB_TMC_INTERFACE_VALUE == ifdesc->bInterfaceNumber )
   {
      return;
   }
}
// -----------------------------------------------------------------------------
// fUsbDataRx_Usbtmc()
// USBTMC interface: Data-Rx handler [Bulk-OUT-transfer]
static bool fUsbDataRx_Usbtmc( uint8_t bEpLogAddress, sUSBTransfer_t * rx )
{
   const sUSBAppEntry_Data_t * app = NULL;

   with_data_app_for( bEpLogAddress, app )
   {
       if( NULL != app->fDataRxHandler )
       {
           return app->fDataRxHandler( bEpLogAddress, rx );
       }
   }
   else
       with_default_data_app( bEpLogAddress, app )
       {
           if( NULL != app->fDataRxHandler )
           {
               return app->fDataRxHandler( bEpLogAddress, rx );
           }
       }

   return false;
}

// fUsbDataTx_Usbtmc()
// USBTMC interface: Data-Tx handler [Bulk-IN-transfer]
// Is called after the first packet sent (need to call @usb_bridge_datain_beginsend)
static bool fUsbDataTx_Usbtmc( uint8_t bEpLogAddress, sUSBTransfer_t * tx )
{
   const sUSBAppEntry_Data_t * app = NULL;

   with_data_app_for( bEpLogAddress, app )
   {
       if( NULL != app->fDataTxHandler )
       {
           return app->fDataTxHandler( bEpLogAddress, tx );
       }
   }
   else
       with_default_data_app( bEpLogAddress, app )
       {
           if( NULL != app->fDataTxHandler )
           {
               return app->fDataTxHandler( bEpLogAddress, tx );
           }
       }

   return false;
}

static bool fUsbDataErr_Usbtmc( uint8_t bEpLogAddress, uint32_t err )
{
   const sUSBAppEntry_Data_t * app = NULL;

   with_data_app_for( bEpLogAddress, app )
   {
       if( NULL != app->fDataErrHandler )
       {
           return app->fDataErrHandler( bEpLogAddress, err );
       }
   }
   else
       with_default_data_app( bEpLogAddress, app )
       {
          if( NULL != app->fDataErrHandler )
          {
              return app->fDataErrHandler( bEpLogAddress, err );
          }
       }

   return true;  // true by default
}
