#include "usbapp/usb_application_service.h"
#include "usbapp/usb_application_service_def.h"
#include "usbapp/usb_application_flash.h" // usb_application_flash_fGetWritingFlag
#include "usb/usb_bridge.h"
#include "usb/usb_config.h"
#include "usbd_vendor.h"
#include "app/thermo/tsensor.h"
#include "drivers/flash/base/extmem_flash.h"
#include "app/version/version.h"
#include "app/nfm/nfm_base.h"

#define  READ_INFO                        0xA4   // чтение служебной информации

static int8_t fServiceAppInit();
static int8_t fServiceAppDeInit();
static void   fServiceAppReset();
static bool   fServiceAppSetup( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success );
static size_t fServiceAppControlRx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining );
static size_t fServiceAppControlTx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining );

static TServiceRequestBuffer_t sRequestBuffer;

const sUSBAppEntry_Control_t usbapplication_ACM_planarproto_service = {

     .fUsbInit     = fServiceAppInit,
     .fUsbDeInit   = fServiceAppDeInit,
     .fUsbSetup    = fServiceAppSetup,
     .fUsbCtlEpRx  = fServiceAppControlRx,
     .fUsbCtlEpTx  = fServiceAppControlTx,
     .fResetEvent  = fServiceAppReset,
};

static int8_t fServiceAppInit()
{
    return 0;
}

static int8_t fServiceAppDeInit()
{
    return 0;
}

static void fServiceAppReset()
{

}

static bool fServiceAppSetup( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success )
{
    switch ( pSetup->bRequest )
    {
        case READ_INFO:
        {
            if( 0x8000 == pSetup->wValue )
            {
                switch( pSetup->wIndex )
                {
                    #if CONFIG_EXTMEM
                    case 0x001:  // get flash info
                    {
                        // check the request data stage size
                        if( sizeof(sRequestBuffer.svcReq_Flash) >= pSetup->wLength )
                        {
                            memset( &sRequestBuffer, 0, sizeof(sRequestBuffer) );

                            sRequestBuffer.svcReq_Flash.dwProtocolVersion = 1;
                            sRequestBuffer.svcReq_Flash.cReserved0 = 0;
                            sRequestBuffer.svcReq_Flash.cReserved1 = 0;
                            sRequestBuffer.svcReq_Flash.wDriverVersion = pProgramVersion->firmware_version;

                            // Maximum Chip size
                            sRequestBuffer.svcReq_Flash.dwFlashSize =
                                ExtMemHandle.pFlashProperties->maxSectors * ExtMemHandle.pFlashProperties->sectorSize;

                            // Available capacity
                            sRequestBuffer.svcReq_Flash.dwCapacity =
                                ExtMemHandle.pBanksProperties->factoryBankSize + ExtMemHandle.pBanksProperties->userBankSize;

                            bool protectStatus = false;
                            if( ExtMemHandle.CheckBankProtect( extmem_bank_factory, &protectStatus ) )
                                sRequestBuffer.svcReq_Flash.bBank0Protection = (protectStatus)?1:0;
                            else
                                sRequestBuffer.svcReq_Flash.bBank0Protection = 0;

                            // Write-Erase-count Resource descriptor: useless, supported only for protocol compatibility
                            sRequestBuffer.svcReq_Flash.WERInfo.wReserved2 = 0;
                            sRequestBuffer.svcReq_Flash.WERInfo.wRBSCount = 0;
                            sRequestBuffer.svcReq_Flash.WERInfo.dwMaxWER = 0;

                            sRequestBuffer.svcReq_Flash.ChipDescription.cDesc[0] = '\0';
                                strncpy( sRequestBuffer.svcReq_Flash.ChipDescription.cDesc,
                                         ExtMemHandle.pFlashProperties->pChipDescription,
                                         sizeof(sRequestBuffer.svcReq_Flash.ChipDescription.cDesc) );
                            sRequestBuffer.svcReq_Flash.ChipDescription.cDescLength =
                                strlen( sRequestBuffer.svcReq_Flash.ChipDescription.cDesc );

                            return true;
                        }

                     }
                     break;

                     case 0x002:  //      get flash busy flag (char)
                     {
                        // check the request data stage size
                        if( sizeof(sRequestBuffer.svcReq_FlashReady) >= pSetup->wLength )
                        {
                            memset( &sRequestBuffer, 0, sizeof(sRequestBuffer) );

                            sRequestBuffer.svcReq_FlashReady = (usb_application_flash_fGetWritingFlag()?0x01:0x00);

                            return true;
                        }
                     }
                     break;
                     #endif

                     case 0x003:  //      get model info
                     {
                        // check the request data stage size
                        if( sizeof(sRequestBuffer.svcReq_Model) >= pSetup->wLength )
                        {
                            memset( &sRequestBuffer, 0, sizeof(sRequestBuffer) );

                            sRequestBuffer.svcReq_Model.requestVersion = 0x0001;
                            sRequestBuffer.svcReq_Model.deviceId = NFMClass->properties.deviceId;
                            sRequestBuffer.svcReq_Model.firmwareId = pProgramVersion->firmware_version;

                            NFMClass->methods.getModelName(
                                  (char*)sRequestBuffer.svcReq_Model.modelName, sizeof(sRequestBuffer.svcReq_Model.modelName) );
                            
                            sRequestBuffer.svcReq_Model.ampStatesCount = 0; //NFMClass->properties.allowedAmpStatesCount;
                            
                            sRequestBuffer.svcReq_Model.filtStatesCount = 0; //NFMClass->properties.allowedFiltStatesCount;
                            return true;
                        }
                     }
                     break;
                }
            }
        }
        break;
    }

    return false;
}

static size_t fServiceAppControlRx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining )
{
    return 0;
}

static size_t fServiceAppControlTx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining )
{
    switch ( pSetup->bRequest )
    {
        case READ_INFO:
        {
            if( 0x8000 == pSetup->wValue )
            {
                switch( pSetup->wIndex )
                {
                    case 0x001:  //      get flash info
                    {
                         // try to write the 'svcReq_Flash' structure to the output buffer.
                         // If the buffer size has insufficient space to store whole structure,
                         //  only part of target structure is written to the buffer, the rest part
                         //  will be transmitted in the next transaction ( @idx pointer is used )
                         usb_write_transfer( tx, &sRequestBuffer.raw_bytes[ idx ], sizeof( sRequestBuffer.svcReq_Flash ) );
                    }
                    break;

                    case 0x002:  //      get flash busy flag (char)
                    {
                         usb_write_transfer( tx, &sRequestBuffer.raw_bytes[ idx ], sizeof( sRequestBuffer.svcReq_FlashReady ) );
                    }
                    break;

                    case 0x003:  //      get model info
                    {
                         // try to write the 'svcReq_Model' structure to the output buffer.
                         // If the buffer size has insufficient space to store whole structure,
                         //  only part of target structure is written to the buffer, the rest part
                         //  will be transmitted in the next transaction ( @idx pointer is used )
                         usb_write_transfer( tx, &sRequestBuffer.raw_bytes[ idx ], sizeof( sRequestBuffer.svcReq_Model ) );
                    }
                    break;
                }
            }
        }
        break;
    }

    return usb_count_transfer( tx );
}
