#include "FreeRTOS.h"
#include "queue.h"
#include "queue_datastructure.h"

#ifndef CBMC_OBJECT_BITS
#define CBMC_OBJECT_BITS 7
#endif

#ifndef CBMC_OBJECT_MAX_SIZE
#define CBMC_OBJECT_MAX_SIZE (UINT32_MAX>>(CBMC_OBJECT_BITS+1))
#endif

/* Using prvCopyDataToQueue together with prvNotifyQueueSetContainer
   leads to a problem space explosion. Therefore, we use this stub
   and a sepearted proof on prvCopyDataToQueue to deal with it.
   As prvNotifyQueueSetContainer is disabled if configUSE_QUEUE_SETS != 1,
   in other cases the original implementation should be used. */
#if( configUSE_QUEUE_SETS == 1 )
	BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition )
	{
		if(pxQueue->uxItemSize > ( UBaseType_t ) 0)
		{
			__CPROVER_assert(__CPROVER_r_ok(pvItemToQueue, ( size_t ) pxQueue->uxItemSize), "pvItemToQueue region must be readable");
			if(xPosition == queueSEND_TO_BACK){
				__CPROVER_assert(__CPROVER_w_ok(( void * ) pxQueue->pcWriteTo, ( size_t ) pxQueue->uxItemSize), "pxQueue->pcWriteTo region must be writable");
			}else{
				__CPROVER_assert(__CPROVER_w_ok(( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize), "pxQueue->u.xQueue.pcReadFrom region must be writable");
			}
			return pdFALSE;
		}else
		{
			return nondet_BaseType_t();
		}
	}
#endif

/* xQueueCreateSet is compiled out if configUSE_QUEUE_SETS != 1.*/
#if( configUSE_QUEUE_SETS == 1 )
	QueueSetHandle_t xUnconstrainedQueueSet()
	{
		UBaseType_t uxEventQueueLength = 2;
		QueueSetHandle_t xSet = xQueueCreateSet(uxEventQueueLength);
		if( xSet )
		{
			xSet->cTxLock = nondet_int8_t();
			xSet->cRxLock = nondet_int8_t();
			xSet->uxMessagesWaiting = nondet_UBaseType_t();
			xSet->xTasksWaitingToReceive.uxNumberOfItems = nondet_UBaseType_t();
			/* This is an invariant checked with a couple of asserts in the code base.
			   If it is false from the beginning, the CBMC proofs are not able to succeed*/
			__CPROVER_assume(xSet->uxMessagesWaiting < xSet->uxLength);
			xSet->xTasksWaitingToSend.uxNumberOfItems = nondet_UBaseType_t();
		}
		return xSet;
	}
#endif

/* Create a mostly unconstrained Queue but bound the max item size.
   This is required for performance reasons in CBMC at the moment. */
QueueHandle_t xUnconstrainedQueueBoundedItemSize( UBaseType_t uxItemSizeBound ) {
	UBaseType_t uxQueueLength;
	UBaseType_t uxItemSize;
	uint8_t ucQueueType;
	__CPROVER_assume(uxQueueLength > 0);
	__CPROVER_assume(uxItemSize < uxItemSizeBound);

	// QueueGenericCreate method does not check for multiplication overflow
	size_t uxQueueStorageSize;
	__CPROVER_assume(uxQueueStorageSize < CBMC_OBJECT_MAX_SIZE);
	__CPROVER_assume(uxItemSize < uxQueueStorageSize/uxQueueLength);

	QueueHandle_t xQueue =
		xQueueGenericCreate(uxQueueLength, uxItemSize, ucQueueType);
	if(xQueue){
		xQueue->cTxLock = nondet_int8_t();
		xQueue->cRxLock = nondet_int8_t();
		xQueue->uxMessagesWaiting = nondet_UBaseType_t();
		/* This is an invariant checked with a couple of asserts in the code base.
		   If it is false from the beginning, the CBMC proofs are not able to succeed*/
		__CPROVER_assume(xQueue->uxMessagesWaiting < xQueue->uxLength);
		xQueue->xTasksWaitingToReceive.uxNumberOfItems = nondet_UBaseType_t();
		xQueue->xTasksWaitingToSend.uxNumberOfItems = nondet_UBaseType_t();
		#if( configUSE_QUEUE_SETS == 1)
			xQueueAddToSet(xQueue, xUnconstrainedQueueSet());
		#endif
	}
	return xQueue;
}

/* Create a mostly unconstrained Queue */
QueueHandle_t xUnconstrainedQueue( void ) {
	UBaseType_t uxQueueLength;
	UBaseType_t uxItemSize;
	uint8_t ucQueueType;

	__CPROVER_assume(uxQueueLength > 0);

	// QueueGenericCreate method does not check for multiplication overflow
	size_t uxQueueStorageSize;
	__CPROVER_assume(uxQueueStorageSize < CBMC_OBJECT_MAX_SIZE);
	__CPROVER_assume(uxItemSize < uxQueueStorageSize/uxQueueLength);

	QueueHandle_t xQueue =
		xQueueGenericCreate(uxQueueLength, uxItemSize, ucQueueType);

	if(xQueue){
		xQueue->cTxLock = nondet_int8_t();
		xQueue->cRxLock = nondet_int8_t();
		xQueue->uxMessagesWaiting = nondet_UBaseType_t();
		/* This is an invariant checked with a couple of asserts in the code base.
		   If it is false from the beginning, the CBMC proofs are not able to succeed*/
		__CPROVER_assume(xQueue->uxMessagesWaiting < xQueue->uxLength);
		xQueue->xTasksWaitingToReceive.uxNumberOfItems = nondet_UBaseType_t();
		xQueue->xTasksWaitingToSend.uxNumberOfItems = nondet_UBaseType_t();
		#if( configUSE_QUEUE_SETS == 1)
			xQueueAddToSet(xQueue, xUnconstrainedQueueSet());
		#endif
	}
	return xQueue;
}

/* Create a mostly unconstrained Mutex */
QueueHandle_t xUnconstrainedMutex( void ) {
	uint8_t ucQueueType;
	QueueHandle_t xQueue =
		xQueueCreateMutex(ucQueueType);
	if(xQueue){
		xQueue->cTxLock = nondet_int8_t();
		xQueue->cRxLock = nondet_int8_t();
		xQueue->uxMessagesWaiting = nondet_UBaseType_t();
		/* This is an invariant checked with a couple of asserts in the code base.
		   If it is false from the beginning, the CBMC proofs are not able to succeed*/
		__CPROVER_assume(xQueue->uxMessagesWaiting < xQueue->uxLength);
		xQueue->xTasksWaitingToReceive.uxNumberOfItems = nondet_UBaseType_t();
		xQueue->xTasksWaitingToSend.uxNumberOfItems = nondet_UBaseType_t();
		#if( configUSE_QUEUE_SETS == 1)
			xQueueAddToSet(xQueue, xUnconstrainedQueueSet());
		#endif
	}
	return xQueue;
}
