//----------------------------------------------------
/**
	\file		cortex-M4/inc/hwmodel.h
	\ingroup	platform
	\ingroup	cortex-M4

	\brief		Knihovna HW funkci modelu
				<<< CORTEX M4 >>>

	\author		ConTeK s.r.o., Pavel Bazika
	\version	01.01
	\date		23.02.2007
*/
//----------------------------------------------------


#ifndef _HWMODEL_H_
#define _HWMODEL_H_

#include <cortex-m4/inc/ctktypes.h>
#ifdef USE_FREERTOS
 #include <FreeRTOSConfig.h>
#endif
#include <hwsubmdl.h>

#include <string.h>
#include <stdio.h>
#include <alloca.h>
#include <malloc.h>
#include <math.h>

#define strcat8_C(_dest, _size, _src) \
	strlcat((_dest), (_src), (_size))

#define strcpy8_C(dest, size, src) \
	strlcpy((dest), (src), (size))

//----------------------------------------------------
//!	\name	Funkce pro kompatibilitu
//@{

//!< Funkce strcat - skladani retezce
#define strcat_C(_dest, _size, _src) \
	strlcat((_dest), (_src), (_size))

//!< Funkce strstr - vyhledani retezce v druhem retezci
#define strstr_C(_str1, _str2) \
	strstr((_str1), (_str2))

//!< Funkce printf - vytiskne retezec na stdout 0
#define printf_C(...) \
	printf(__VA_ARGS__)

//!< Funkce fprintf - vytiskne retezec do daneho souboru
#define fprintf_C(_file, ...) \
	frprintf((_file), __VA_ARGS__)

//!< Funkce sprintf - formatovani retezce
#define sprintf_C(_dest, _size, ...) \
	snprintf((_dest), (_size), __VA_ARGS__)

//!< Funkce vsprintf - Write formatted output using a pointer to a list of arguments
#define vsprintf_C(_dest, _size, _format, _argList) \
	vsnprintf((_dest), (_size), (_format), (_argList))

//!< Funkce strftime - formatovani casoveho retezce
#define strftime_C(_ptr, _size, _format, _timePtr) \
	strftime((_ptr), (_size), (_format), (_timePtr))

//!< Funkce gmtime - Converts a time value to a structure
#define gmtime_C(_tm, _time) \
	gmtime_r((_time), (_tm))

//!< Funkce localtime - Converts a time value and corrects for the local time zone
#define localtime_C(_tm, _time) \
	localtime_s((_tm), (_time))

//!< Funkce ctime - Convert a time value to a string and adjust for local time zone settings
#define ctime_C(_dest, _size, _time) \
	_dest = ctime(_time)

//!< - Convert string to long integer
#define strtol_C(_str, _endPtr, _base) \
	strtol((_str), (_endPtr), (_base))

//!< - Convert string to unsigned long integer
#define strtoul_C(_str, _endPtr, _base) \
	strtoul((_str), (_endPtr), (_base))

//!< - Convert string to 64-bit __int64 integer
#define strtoll_C(_str, _endPtr, _base) \
	strtoll((_str), (_endPtr), (_base))

//!< - Convert string to unsigned 64-bit __int64 integer
#define strtoull_C(_str, _endPtr, _base) \
	strtoull((_str), (_endPtr), (_base))

//!< - Convert strings to a single-precision value
#define strtof_C(_str, _endPtr) \
	strtof((_str), (_endPtr))

//!< - Convert strings to a double-precision value
#define strtod_C(_str, _endPtr) \
	strtod((_str), (_endPtr))

//!< - Convert strings to a long double-precision value
#define strtold_C(_str, _endPtr) \
	strtold((_str), (_endPtr))

//!< Funkce strcmp - porovnani dvou retezcu
#define strcmp_C(_str1, _str2) \
	strcmp((_str1), (_str2))

//!< Funkce strlen - vraci delku retezce
#define strlen_C(_str) \
	strlen(_str)

//!< Funkce strchr - vyhleda znak v retezci
#define strchr_C(_str, _char) \
	strchr((_str), (_char))

//!< Funkce string copy
#define strcpy_C(dest, size, src) \
	strlcpy((dest), (src), (size))

#define _T(x)		x												//!< Funce pro kompatibilitu unicode

#define ltoa8_C(_val, _buf, _size) \
	ltoa_C((_val), (_buf), (_size))
//@}

//----------------------------------------------------
//!	\name	Makra pro praci v programove pameti (AVR,..)
//@{

#define read_pconst_byte(x)		(*(x))			//!< Funkce pro cteni bytu z programove pameti AVR
#define read_pconst_word(x)		(*(x))			//!< Funkce pro cteni wordu z programove pameti AVR
#define read_pconst_dword(x)	(*(x))			//!< Funkce pro cteni dwordu z programove pameti AVR
#define read_pconst_qword(x)	(*(x))			//!< Funkce pro cteni qwordu z programove pameti AVR
#define read_pconst_float(x)	(*(x))			//!< Funkce pro cteni float z programove pameti AVR
#define read_pconst_char(x)		(*(x))			//!< Funkce pro cteni char_t z programove pameti AVR
#define read_pconst_ptr(x)		((pptr_t)(*(x)))		//!< Funkce pro cteni pointeru z programove pameti na konstantu v programove pameti AVR
#define read_pconst_pvoid(x)	((pvoidptr_t)(*(x)))	//!< Funkce pro cteni pointeru z programove pameti na konstantu v programove pameti AVR
#define read_pconst_pchar(x)	(*(const pcharptr_t *)(x))	//!< Funkce pro cteni pointeru z programove pameti na retezec v programove pameti AVR

#define memcpy_pconst			memcpy			//!< Kopirovani programove pameti AVR
#define strcpy_pconst			strcpy_C		//!< Kopirovani stringu v programove pameti AVR
#define strcpy_pconst_store		strcpy_pconst	//!< Kopirovani stringu s ulozenim v programove pameti AVR
#define strcat_pconst			strcat_C		//!< Bezpecny strcat z programove pameti AVR
#define strcat_pconst_store		strcat_pconst	//!< Bezpecny strcat s ulozenim v programove pameti AVR
#define sprintf_pconst			sprintf_C		//!< Funkce sprintf v programove pameti AVR
#define sprintf_pconst_store	sprintf_pconst	//!< Funkce sprintf s ulozenim fmt v programove pameti AVR
#define vsprintf_pconst			vsnprintf		//!< Bezpecna funkce vsprintf s ulozenim fmt v programove pameti AVR
#define strftime_pconst			strftime_C		//!< Formatovani casu v programove pameti AVR
#define strlen_pconst			strlen			//!< Delka retezce v programove pameti AVR

#define str_pconst(dest) \
	const char_t dest[]								//!< Odkaz na retezec src v programove pameti AVR pod jmenem dest

#define str_pconst_store(dest, src) \
	str_pconst(dest) = src						//!< Ulozi retezec src v programove pameti AVR pod jmenem dest

#define _const	__attribute__((const))
//@}

//----------------------------------------------------
//!	\name	Makra pro predikci skoku
//@{

#define likely(cond)	__builtin_expect((cond), 1)

#define unlikely(cond)	__builtin_expect((cond), 0)
//@}


//----------------------------------------------------
//!	\name	Konstanty nekonecen a NAN
//@{
#define float_INF		INFINITY
#define float_NAN		NAN
//@}

//----------------------------------------------------
//!	\name	Makra pro alokaci mista na heap
//@{

#ifdef _DEBUG

#define malloc_C(_type) \
	({ \
		void * ___malloctmp = malloc(sizeof(_type)); \
		DbgScr(1, "malloc: addr %p, size %d", ___malloctmp, sizeof(_type)); \
		char_t * ___mallocbuf = __FILE__; \
		DbgScr(1, "  in %s at line %d", &___mallocbuf[strlen(___mallocbuf) - 10], __LINE__); \
		(_type *)___malloctmp; \
	})

#define mallocarray_C(_type, _count) \
	({ \
		void * ___malloctmp = malloc(sizeof(_type) * (_count)); \
		DbgScr(1, "malloc array: addr %p, size %d, count %d, total %d", ___malloctmp, sizeof(_type), (_count), sizeof(_type) * (_count)); \
		char_t * ___mallocbuf = __FILE__; \
		DbgScr(1, "  in %s at line %d", &___mallocbuf[strlen(___mallocbuf) - 10], __LINE__); \
		(_type *)___malloctmp; \
	})

#define free_C(_var) \
	do { \
		DbgScr(1, "free %p", _var); \
		free(_var); \
	} while (0)

#define freearray_C(_var) \
	do { \
		DbgScr(1, "free array %p", _var); \
		free(_var); \
	} while (0)

#else

#define malloc_C(_type) \
	((_type *)malloc(sizeof(_type)))

#define mallocarray_C(_type, _count) \
	((_type *)malloc(sizeof(_type) * (_count)))

#define free_C(_var) \
	(free(_var))

#define freearray_C(_var) \
	(free(_var))

#endif

//@}


//----------------------------------------------------
/**
	\brief	Typ pro high-resolution performance counter
*/
//----------------------------------------------------

typedef uint32_t hrctime_t;		// Typ casu v mikrosekundach
typedef uint8_t hrccnt_t;

//----------------------------------------------------
/**
	\brief	Start mereni casu, high-resolution performance counter

	\param	_cnt	promena citace
*/
//----------------------------------------------------

void HrcStartMeasure(hrccnt_t * const _cnt);


//----------------------------------------------------
/**
	\brief	Ziskani doby od startu mereni casu, high-resolution performance counter

	\param	_cnt	promena s pocateni hodnotu casu

	\return			pocet mikrosekund
*/
//----------------------------------------------------

hrctime_t HrcGetUsMeasure(const hrccnt_t * const _cnt);


//----------------------------------------------------
//!	\name	Zajisti ulozeni funkce v RAM

//@{
#define __RAMFUNC		__attribute__((section(".ramfunc"), noinline))
//@}

//----------------------------------------------------
/**
	\brief	Funkce pro prevod double na ASCII

	\param	value	Hodnota double
	\param	witdh	Pocet celych mist
	\param	prec	Pocet desetinnych mist
	\param	buff	Buffer pro ulozeni vysledku
	\param	bSize	Velikost bufferu

	\return			Adresa bufferu
*/
//----------------------------------------------------

const char_t * dtostrf_C( const double_t value, const uint8_t width, const uint8_t prec, char_t * const buff, const uint16_t bSize );


//----------------------------------------------------
/**
	\brief	Funkce prevadejici unsigned long na ASCII retezec

	\param	value	Prevadena hodnota
	\param	buff	Vystupni buffer
	\param	bSize	Velikost bufferu

	\return			Adresa vystupniho bufferu
*/
//----------------------------------------------------
const char_t * ultoa_C( const uint32_t value, char_t * const buff, const uint16_t bSize );


//----------------------------------------------------
/*
	\brief	Funkce prevadejici long na ASCII retezec

	\param	value	Prevadena hodnota
	\param	buff	Vystupni buffer
	\param	bSize	Velikost bufferu

	\return			Adresa vystupniho bufferu
*/
//----------------------------------------------------
const char_t * ltoa_C( const uint32_t value, char_t * const buff, const uint16_t bSize );


//----------------------------------------------------
/**
	\brief	Funkce prevadejici unsigned long long na ASCII retezec

	\param	value	Prevadena hodnota
	\param	buff	Vystupni buffer
	\param	bSize	Velikost bufferu

	\return			Adresa vystupniho bufferu
*/
//----------------------------------------------------
const char_t * ulltoa_C( const uint64_t value, char_t * const buff, const uint16_t bSize );


//----------------------------------------------------
/**
	\brief	Funkce prevadejici long long na ASCII retezec

	\param	value	Prevadena hodnota
	\param	buff	Vystupni buffer
	\param	bSize	Velikost bufferu

	\return			Adresa vystupniho bufferu
*/
//-----------------------------------------------------
const char_t * lltoa_C( const uint64_t value, char_t * const buff, const uint16_t bSize );


//----------------------------------------------------
/**
	\brief	Funkce cekani pouze v MSVC
*/
//----------------------------------------------------

#define Sleep(_x)

#define SleepMain


//----------------------------------------------------
/**
	\brief	Zajisteni reentrance standardni knihovny
*/
//----------------------------------------------------

/* if we compile thread-safe library without FreeRTOS or thread-safe library with thread-unsafe FreeRTOS */
#if ((defined(THREAD_SAFE_LIB) && !defined(USE_FREERTOS)) || (defined(THREAD_SAFE_LIB) && defined(USE_FREERTOS) && configUSE_NEWLIB_REENTRANT == 0))

void ThreadEntry(const THREAD_ID ThreadID);

void ThreadEntryNoLock(const THREAD_ID ThreadID);

void ThreadExit(const THREAD_ID ThreadID);

void ThreadExitNoLock(const THREAD_ID ThreadID);

void HWModelInitialize(void);

void HWModelFinalize(void);

#else // THREAD_SAFE_LIB

#define ThreadEntry(ThreadID)
#define ThreadEntryNoLock(ThreadID)
#define ThreadExit(ThreadID)
#define ThreadExitNoLock(ThreadID)
#define HWModelInitialize()
#define HWModelFinalize()

#endif // THREAD_SAFE_LIB

#ifdef USE_FREERTOS

/* makra pro obsluhu preruseni v tasku pro odlozena preruseni pri pouziti FreeRTOS
 * proc: pro synchronizaci ve FreeRTOS pouzivame mutexy misto zakazu preruseni,
 * abychom mohli pouzivat synchronizaci (a dale napr. dynamickou pamet a milion dalsich veci),
 * neni mozne preruseni obslouzit hned, protoze tyto veci se v preruseni provadet nesmi.
 * misto toho na zacatku preruseni posleme do fronty preruseni prislusny handler,
 * uloha ISR_Task ho nasledne z fronty vybere a spusti jiz v klasickem thread modu a nikoli interrupt modu.
 * jak to funguje:
 * 1. Na uplnem zacatku handleru preruseni zavolame FREERTOS_DEFERRED_INTERRUPT_START a stane se toto:
 *   zjistime stav procesoru, pokud jsme v interrupt modu (tedy handler probiha v ramci preruseni a ne odlozeneho preruseni):
 *   A) pokud to je dano parametrem, zakazeme prislusne preruseni pomoci NVIC_DisableIRQ
 *      handler posleme do fronty odlozenych preruseni, z ktere si ho pak vytahne ISR_Task
 *      pokud se nepovedlo ho poslat do fronty (napr. fornta plna), povolime dane preruseni pokud jsme ho predtim zakazali
 *      FreeRTOSu naridime prepnout kontext a skoncime (ANO ZDE MUSI BYT RETURN, TO NENI CHYBA - jinak bychom pokracovali dale do obsluhy preruseni a to nechceme)
 *   B) pokud jsme v thread modu, provadi se preruseni uz odlozene v ramci ISR_Tasku, takze nic nedelame a pokracujeme do obsluhy preruseni (NENI ZDE RETURN)
 *  2. Na uplnem konci handleru preruseni (tedy vzdy uz v thread modu) zavolame FREERTOS_DEFERRED_INTERRUPT_END, cimz dojde k prepnuti kontextu
 */

 #define FREERTOS_DEFERRED_INTERRUPT_START(__irqnum, __handler,__disable) {	\
  if (__get_IPSR() & 0x01FF) {						\
	BaseType_t __xHigherPriorityTaskWoken = pdFALSE;	\
	if (__disable) NVIC_DisableIRQ((__irqnum));						\
	void (*__fptr)(void) = (__handler);					\
	if ((xQueueSendFromISR(gISRQueue,&__fptr,&__xHigherPriorityTaskWoken) != pdTRUE) && ((__disable))   )	\
		NVIC_EnableIRQ((__irqnum));					\
	portYIELD_FROM_ISR(TRUE);						\
	return;											\
  }													\
 }
 #define FREERTOS_DEFERRED_INTERRUPT_END() taskYIELD()
#endif

//----------------------------------------------------
/**
	\brief	assert
*/
//----------------------------------------------------

typedef void (*assertFunc) (const char * const txt, const char * const file, const int line);

#ifdef _DEBUG

void _hwmodel_assert_C_func(const char * const txt, const char * const file, const int line);

extern assertFunc	AssertFunc;

void RegisterAssert(assertFunc Func);
void UnregisterAssert(void);

#define assert_C(_expr) \
	do { \
		if (!(_expr)) \
		{ \
			AssertFunc(#_expr, __FILE__, __LINE__); \
		} \
	} while(0)


#ifdef ASSERT_TODO

#define assert_todo() \
	assert_C(FALSE)

#else

#define assert_todo() \

#endif


//----------------------------------------------------
/**
	\brief	Kontrolni makro
*/
//----------------------------------------------------

#define verify_C(_expr) \
	assert_C(_expr)

#else

	#define assert_C(expr)

	#define verify_C(expr) \
		expr

	#define RegisterAssert(Func)

	#define UnregisterAssert(void)

	#define assert_todo() \

#endif


#ifdef _DEBUG
//--------------------------------------------------
/**
	\brief	Vypis ladici zpravy do vystupniho okna VisualStudia

	\param	_str	Formatovy retezec
	\param	...		Argumenty retezce
*/
//--------------------------------------------------
 #define DEBUG_MAX_TEXT_SIZE		160		//!< Maximalni velikost retezce
 void DebugMessage(const char_t * _str, ...);

#else

 #define DEBUG_MAX_TEXT_SIZE		160		//!< Maximalni velikost retezce
 #define DebugMessage(_str, ...)

#endif // _DEBUG

#endif	// _HWMODEL_H_
