#define xBUFFER_CACHE_SIZE           10
#define xMAX_FAULT_INJECTION_RATE    15
#define xMIN_FAULT_INJECTION_RATE    3
#define xNUM_FAULT_TYPES             1

static NetworkBufferDescriptor_t * xNetworkBufferCache[ xBUFFER_CACHE_SIZE ] = { 0 };

#define xFAULT_LOG_SIZE    2048
uint32_t ulInjectedFault[ xFAULT_LOG_SIZE ];
uint32_t ulFaultLogIndex = 0;

static BaseType_t prvCachePacket( NetworkBufferDescriptor_t * pxNetworkBufferIn )
{
    BaseType_t x, xReturn = pdFALSE;

    for( x = 0; x < xBUFFER_CACHE_SIZE; x++ )
    {
        if( xNetworkBufferCache[ x ] == NULL )
        {
            xNetworkBufferCache[ x ] = pxNetworkBufferIn;
            xReturn = pdTRUE;
            break;
        }
    }

    return xReturn;
}
/*-----------------------------------------------------------*/

static NetworkBufferDescriptor_t * prvGetCachedPacket( void )
{
    BaseType_t x;
    NetworkBufferDescriptor_t * pxReturn = NULL;

    for( x = ( xBUFFER_CACHE_SIZE - 1 ); x >= 0; x-- )
    {
        if( xNetworkBufferCache[ x ] != NULL )
        {
            pxReturn = xNetworkBufferCache[ x ];
            xNetworkBufferCache[ x ] = NULL;
            break;
        }
    }

    return pxReturn;
}
/*-----------------------------------------------------------*/

static NetworkBufferDescriptor_t * prvDuplicatePacket( NetworkBufferDescriptor_t * pxOriginalPacket,
                                                       const uint8_t * pucPacketData )
{
    NetworkBufferDescriptor_t * pxReturn;

    /* Obtain a new descriptor. */
    pxReturn = pxGetNetworkBufferWithDescriptor( pxOriginalPacket->xDataLength, 0 );

    if( pxReturn != NULL )
    {
        /* Copy in the packet data. */
        pxReturn->xDataLength = pxOriginalPacket->xDataLength;
        memcpy( pxReturn->pucEthernetBuffer, pucPacketData, pxOriginalPacket->xDataLength );
    }

    return pxReturn;
}
/*-----------------------------------------------------------*/

static NetworkBufferDescriptor_t * prvRxFaultInjection( NetworkBufferDescriptor_t * pxNetworkBufferIn,
                                                        const uint8_t * pucPacketData )
{
    static uint32_t ulCallCount = 0, ulNextFaultCallCount = 0;
    NetworkBufferDescriptor_t * pxReturn = pxNetworkBufferIn;
    IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
    uint32_t ulFault;

    return pxNetworkBufferIn;

    ulCallCount++;

    if( ulCallCount > ulNextFaultCallCount )
    {
        xApplicationGetRandomNumber( &( ulNextFaultCallCount ) );
        ulNextFaultCallCount = ulNextFaultCallCount % xMAX_FAULT_INJECTION_RATE;

        if( ulNextFaultCallCount < xMIN_FAULT_INJECTION_RATE )
        {
            ulNextFaultCallCount = xMIN_FAULT_INJECTION_RATE;
        }

        ulCallCount = 0;

        xApplicationGetRandomNumber( &( ulFault ) );
        ulFault = ulFault % xNUM_FAULT_TYPES;

        if( ulFaultLogIndex < xFAULT_LOG_SIZE )
        {
            ulInjectedFault[ ulFaultLogIndex ] = ulFault;
            ulFaultLogIndex++;
        }

        switch( ulFault )
        {
            case 0:
                /* Just drop the packet. */
                vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn );
                pxReturn = NULL;
                break;

            case 1:

                /* Store the packet in the cache for later. */
                if( prvCachePacket( pxNetworkBufferIn ) == pdTRUE )
                {
                    /* The packet may get sent later, it is not being sent
                     * now. */
                    pxReturn = NULL;
                }

                break;

            case 2:
                /* Send a cached packet. */
                pxReturn = prvGetCachedPacket();

                if( pxReturn != NULL )
                {
                    /* A cached packet was obtained so drop the original
                     * packet. */
                    vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn );
                }
                else
                {
                    /* Could not obtain a packet from the cache so just return
                     * the packet that was passed in. */
                    pxReturn = pxNetworkBufferIn;
                }

                break;

            case 4:

                /* Send a duplicate of the packet right away. */
                pxReturn = prvDuplicatePacket( pxNetworkBufferIn, pucPacketData );

                /* Send the original packet to the stack. */
                xRxEvent.pvData = ( void * ) pxNetworkBufferIn;

                if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
                {
                    vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn );
                }

                break;

            case 5:

                /* Send both a cached packet and the current packet. */
                xRxEvent.pvData = ( void * ) prvGetCachedPacket();

                if( xRxEvent.pvData != NULL )
                {
                    if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
                    {
                        vReleaseNetworkBufferAndDescriptor( pxNetworkBufferIn );
                    }
                }

                break;

            case 6:
            case 7:
            case 8:

                /* Store the packet in the cache for later. */
                if( prvCachePacket( pxNetworkBufferIn ) == pdTRUE )
                {
                    /* The packet may get sent later, it is not being sent
                     * now. */
                    pxReturn = NULL;
                }

                break;
        }
    }

    return pxReturn;
}
/*-----------------------------------------------------------*/
