/**
  ******************************************************************************
  * @file           : app_aliro.c
  * @brief          : Aliro application main logic and command processing
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
 
/* Includes ----------------------------------------------------------*/
#if defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#endif //defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)

#include "app_aliro.h"
#include "tlwrapper.h"
#include "app_nfc.h"

#if ALIRO_BLE
#include "app_ble.h"
#include "alirosvc_app.h"
#endif //ALIRO_BLE

//#include "app_uwb.h"
#include "provisioning.h"
#include "logger.h"
#include "rfal_nfc.h"

#ifdef ST25R200
#include "st25r200_irq.h"
#endif //ST25R200
#ifdef ST25R500
#include "st25r500_irq.h"
#endif //ST25R500

#include "ACWG_Security.h"
#include "sm_crypto_mcu.h"
#include "qcbor/UsefulBuf.h"
#include "qcbor/qcbor_decode.h"
#include "qcbor/qcbor_spiffy_decode.h"
#include "t_cose/t_cose_sign1_verify.h"

#include "ACWG_build_info.h"
#include "app_build_info.h"
#include "app_version.h"

#include <time.h>

/* Private typedef -----------------------------------------------------------*/



/* Defines ------------------------------------------------------------*/

#ifdef ALIRO_BLE
  #define FIRMWARE_NAME "Aliro_nfcble"
#else /* FIRMWARE_NAME */
  #define FIRMWARE_NAME "Aliro_nfc"
#endif /* FIRMWARE_NAME */

/**
 * @def MIN_DYNAMIC_SPSM_VALUE
 * @brief Minimum value for dynamic SPSM.
 */
#define MIN_DYNAMIC_SPSM_VALUE 0x80
/**
 * @def MAX_DYNAMIC_SPSM_VALUE
 * @brief Maximum value for dynamic SPSM.
 */
#define MAX_DYNAMIC_SPSM_VALUE 0xFF
/**
 * @def GET_SPSM_LOOP_NBR
 * @brief Number of loops for SPSM search.
 */
#define GET_SPSM_LOOP_NBR 10


/* Macros -------------------------------------------------------------*/

/**
 * @def __byteswap32
 * @brief Macro to swap bytes in a 32-bit unsigned integer.
 */
#define __byteswap32(_u32val) (\
  ((((uint32_t) (_u32val))<<24)&0xFF000000)| \
  ((((uint32_t) (_u32val))<<8 )&0x00FF0000)| \
  ((((uint32_t) (_u32val))>>8 )&0x0000FF00)| \
  ((((uint32_t) (_u32val))>>24)&0x000000FF)\
)


/**
 * @def PRINT_ERROR
 * @brief Macro to print an error message.
 */
#define PRINT_ERROR(S)  PLATFORM_LOG(VERB_FN_NAME, "%s%s\r\n", errstr, S)
/**
 * @def CREATE_CASE
 * @brief Macro to create a case statement for error printing.
 */
#define CREATE_CASE(S)  case S:PRINT_ERROR(#S);break;




/* Constants --------------------------------------------------------*/

/**
 * @brief Global build information for the Aliro application.
 *
 * This structure contains metadata about the firmware build, including version numbers,
 * build date/time, specification versions, and a description string.
 */
const app_build_info_t app_build_info =
{
   /** Magic cookie/tag for firmware info */
    .magic_cookie = { 'B', 'F', 'W', 'I' }, // FirmWare Info Tag
   /** Version of the build info structure */
    .build_info_version = ABI_VERSION,
   /** Application major version */
    .app_major = APP_VERSION_MAJ,
   /** Application minor version */
    .app_minor = APP_VERSION_MIN,
   /** Application subminor version */
    .app_subminor = APP_VERSION_SUBMIN,
    // .revision = "" SW_VERSION,
   /** Build date (from compiler macro) */
    .date = __DATE__,
   /** Build time (from compiler macro) */
    .time = __TIME__,
   /** Aliro specification major version */
    .aliro_spec_major = ALIRO_SPEC_VERSION_MAJ,
   /** Aliro specification minor version */
    .aliro_spec_minor = ALIRO_SPEC_VERSION_MIN,
   /** Aliro specification subminor version */
    .aliro_spec_subminor = ALIRO_SPEC_VERSION_SUBMIN,
   /** ACWG major version */
    .acwg_ver_major = APP_ACWG_VERSION_MAJ,
   /** ACWG minor version */
    .acwg_ver_minor = APP_ACWG_VERSION_MIN,
   /** ACWG subminor version */
    .acwg_ver_subminor = APP_ACWG_VERSION_SUBMIN,
   /** Firmware description string */
    .fw_description = FIRMWARE_NAME
};

/**
 * @brief Month name constants for RTC operations.
 */
static const char JAN[] = "Jan";
static const char FEB[] = "Feb";
static const char MAR[] = "Mar";
static const char APR[] = "Apr";
static const char MAY[] = "May";
static const char JUN[] = "Jun";
static const char JUL[] = "Jul";
static const char AUG[] = "Aug";
static const char SEP[] = "Sep";
static const char OCT[] = "Oct";
static const char NOV[] = "Nov";
static const char DEC[] = "Dec";

statItem_t statItems[] = 
{
  { SELECT_APP_POS,                 "START    ", 1 , 0 },
  { END_SELECT_APP_POS,             "SELECT   ", 1 , 0 },
  { AUTH0_POS,                      "PRE AUTH0", 1 , 0 },
  { END_AUTH0_POS,                  "AUTH0    ", 1 , 0 },
  { LOADCERT_POS,                   "PRE LOAD ", 1 , 0 },
  { END_LOADCERT_POS,               "LOADCERT ", 1 , 0 },
  { AUTH1_POS,                      "PRE AUTH1", 1 , 0 },
  { END_AUTH1_POS,                  "AUTH1    ", 1 , 0 },
  { CONTROL_FLOW_POS,               "PRE CFLOW", 1 , 0 },
  { END_CONTROL_FLOW_POS,           "CFLOW    ", 1 , 0 },
  { EXCHANGE_POS,                   "PRE EXCH ", 1 , 0 },
  { END_EXCHANGE_POS,               "EXCH     ", 1 , 0 },
  { STEPUP_POS,                     "PRE STUP ", 1 , 0 },
  { END_STEPUP_POS,                 "STUP     ", 1 , 0 },
  { DOOR_IS_OPENED_POS,             "DOOR OP  ", 1 , 0 },
  { DOOR_IS_CLOSED_POS,             "DOOR CL  ", 0 , 0 },
  { DOOR_IS_DISARMED_POS,           "DOOR DIS ", 0 , 0 },
  { DON_T_KNOW_POS,                 "DONT KNOW", 0 , 0 },
  { DERIVE_PERSISTENT_KEYS_POS,     "PERS KEYS", 1 , 0 },
  { END_DERIVE_PERSISTENT_KEYS_POS, "PERS KEYS", 1 , 0 },
  { ELAPSED_TIME_POS,               "ELAPSED  ", 1 , 0 },
};


uint8_t resp[] = {
0xa2, 0x01, 0xa2, 0x01, 0xa1, 0x67, 0x61, 0x6c, 0x69, 0x72, 0x6f, 0x2d, 0x61, 0x81, 0xd8, 0x18, 0x58, 0x43, 0xa4, 0x01, 0x00, 0x02, 0x58, 0x20, 0x27, 0x0e, 0xcc, 0x9f, 0x16, 0xff, 0xf3, 0x70, 0x43, 0x5c, 0x8d, 0x37, 0xeb, 0xa9, 0xa4, 0x7b, 0x7a, 0x42, 0xfa, 0xf8, 0xbc, 0xeb, 0x87, 0x47, 0xff, 0x53, 0xa2, 0x16, 0x6f, 0xde, 0x0b, 0x02, 0x03, 0x6b, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 
0x79, 0x34, 0x30, 0x34, 0x04, 0xa2, 0x00, 0x01, 0x01, 0x4a, 0x69, 0x64, 0x67, 0x6f, 0x65, 0x73, 0x68, 0x65, 0x72, 0x65, 0x02, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa1, 0x18, 0x21, 0x59, 0x01, 0x9a, 0x30, 0x82, 0x01, 0x96, 0x30, 0x82, 0x01, 0x3b, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x26, 0x9e, 0xf0, 0x1c, 0xe2, 0x83, 0xaf, 0xa8, 0x17, 0x67, 0x92, 0x32, 0x2a, 0x2f, 0xf4, 0x02, 0x5e, 0x68, 
0xe4, 0xe8, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x25, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x41, 0x6c, 0x69, 0x72, 0x6f, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x31, 0x30, 0x31, 0x31, 0x37, 
0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x25, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x41, 0x6c, 0x69, 0x72, 0x6f, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x59, 0x30, 0x13, 
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x2b, 0xf2, 0xb4, 0xce, 0x3a, 0xe9, 0xbe, 0x65, 0x10, 0x84, 0x59, 0x35, 0x24, 0x9d, 0x40, 0x4e, 0x6d, 0xb6, 0xfc, 0x54, 0x47, 0x39, 0x3b, 0x36, 0xc3, 0x5d, 0xd3, 0xa5, 0xc4, 0x99, 0x33, 0x50, 0x4f, 0xec, 0x86, 0xbe, 0x64, 0x41, 0x34, 0xb9, 0x26, 0xa6, 
0xd8, 0x8e, 0x4e, 0x49, 0xfc, 0x41, 0xff, 0x6a, 0xcb, 0xc8, 0xf6, 0xd0, 0x69, 0x86, 0x14, 0x93, 0xdb, 0xb4, 0x34, 0x1d, 0x21, 0x16, 0xa3, 0x47, 0x30, 0x45, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1f, 0x06, 
0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xf6, 0xb4, 0x04, 0xc2, 0x39, 0xbd, 0xbe, 0x48, 0x72, 0x42, 0x94, 0x9c, 0xb3, 0x4d, 0x38, 0xfa, 0x14, 0x72, 0x73, 0xbf, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xfb, 0xe1, 0x42, 0x2d, 0x18, 0xb4, 0xde, 0xbc, 0x16, 0x66, 0x37, 0xfe, 0xd3, 0x00, 0xab, 
0xb3, 0xcb, 0xea, 0xbf, 0xf5, 0x16, 0xf8, 0xcb, 0xb4, 0x25, 0xac, 0xc7, 0x1b, 0x77, 0x29, 0xb7, 0xf7, 0x02, 0x21, 0x00, 0xa1, 0x91, 0x20, 0x69, 0x18, 0xa1, 0xbe, 0x3e, 0xaf, 0xa5, 0xd2, 0xad, 0x51, 0x4b, 0x58, 0xd0, 0xb5, 0x8d, 0x28, 0xc8, 0x83, 0x6b, 0x0b, 0xc8, 0x1b, 0x88, 0x73, 0x31, 0x10, 0xa4, 0x5c, 0x35, 0x58, 0xe1, 0xd8, 0x18, 0x58, 0xdd, 0xa7, 0x01, 0x63, 0x31, 0x2e, 0x30, 0x02, 
0x67, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x03, 0xa1, 0x67, 0x61, 0x6c, 0x69, 0x72, 0x6f, 0x2d, 0x61, 0xa1, 0x00, 0x58, 0x20, 0xad, 0x4a, 0x6d, 0x3b, 0x1f, 0x5d, 0x3f, 0x5a, 0x22, 0x91, 0xe4, 0x4f, 0xb5, 0x42, 0xe0, 0x85, 0xd4, 0xfb, 0xad, 0x0f, 0xb5, 0x3e, 0xbe, 0xe6, 0xad, 0x99, 0x75, 0x6a, 0xaa, 0x64, 0x5c, 0x44, 0x04, 0xa1, 0x01, 0xa4, 0x01, 0x02, 0x20, 0x01, 0x21, 0x58, 0x20, 
0xc1, 0xe3, 0x67, 0x66, 0xa9, 0xbd, 0x99, 0x03, 0xe5, 0x01, 0x45, 0x72, 0x19, 0x2a, 0xe6, 0x18, 0x46, 0x80, 0x37, 0x38, 0x4d, 0x92, 0x56, 0x73, 0xa2, 0x7a, 0xa7, 0x9e, 0xf8, 0x14, 0x39, 0xda, 0x22, 0x58, 0x20, 0xfa, 0xec, 0x23, 0x9c, 0x87, 0x51, 0x09, 0x06, 0x49, 0x6b, 0xfe, 0x18, 0xd8, 0xf7, 0xe0, 0xb3, 0x87, 0x42, 0x79, 0xd5, 0xf8, 0x1a, 0x20, 0x3e, 0xdf, 0x91, 0x56, 0x0b, 0x18, 0xe3, 
0x76, 0xd3, 0x05, 0x67, 0x61, 0x6c, 0x69, 0x72, 0x6f, 0x2d, 0x61, 0x06, 0xa3, 0x01, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x36, 0x2d, 0x30, 0x31, 0x54, 0x31, 0x37, 0x3a, 0x31, 0x33, 0x3a, 0x33, 0x30, 0x5a, 0x02, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x36, 0x2d, 0x30, 0x31, 0x54, 0x31, 0x37, 0x3a, 0x31, 0x33, 0x3a, 0x33, 0x30, 0x5a, 0x03, 0xc0, 0x74, 0x32, 0x30, 0x32, 
0x35, 0x2d, 0x30, 0x36, 0x2d, 0x30, 0x31, 0x54, 0x31, 0x37, 0x3a, 0x31, 0x33, 0x3a, 0x33, 0x30, 0x5a, 0x07, 0xf4, 0x58, 0x40, 0xbc, 0xe2, 0x01, 0x8e, 0xaa, 0xa3, 0x7b, 0xb8, 0xcd, 0x49, 0x62, 0xf3, 0xd2, 0xf4, 0xea, 0x4e, 0xae, 0x20, 0xdc, 0xfd, 0xcf, 0x54, 0x61, 0x47, 0x3f, 0xf2, 0xeb, 0x13, 0x4c, 0x92, 0x3c, 0xe3, 0xd6, 0x6f, 0x99, 0xbd, 0xd8, 0xa6, 0xca, 0xc5, 0xa4, 0x31, 0x9f, 0xa7, 
0x89, 0x87, 0xe7, 0x33, 0x4c, 0x85, 0x67, 0x23, 0x29, 0x38, 0xc0, 0x41, 0xa8, 0x42, 0xdf, 0x80, 0x82, 0x10, 0x5c, 0xd8, 0x05, 0x67, 0x61, 0x6c, 0x69, 0x72, 0x6f, 0x2d, 0x61
};

/* Highly verbose if enabled: print all the buffers exchanged */
//#define PRINT_BUFFERS_HEX       0

/**
 * @brief Enable/disable printing of exchanged buffers in hex format.
 */
uint8_t PRINT_BUFFERS_HEX = 1;
/**
 * @brief Index for printing bytes in buffer output.
 */
uint8_t BYTE_INDEX_IN_PRINT = 0;



/* Extern variables ---------------------------------------------------------*/
#if defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)

/**
 * @brief Temporary UART character buffer.
 */
uint8_t tmpUartChar=0;
/**
 * @brief Length of temporary UART character buffer.
 */
uint16_t tmpUartChars_len = 0;
EXTI_HandleTypeDef H_EXTI_13;
#endif //defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)


/**
 * @brief External handle for CRYP hardware.
 */
extern CRYP_HandleTypeDef hcryp;
/**
 * @brief External handle for HASH hardware.
 */
extern HASH_HandleTypeDef hhash;
/**
 * @brief External handle for PKA hardware.
 */
extern PKA_HandleTypeDef hpka;
/**
 * @brief External handle for RNG hardware.
 */
extern RNG_HandleTypeDef hrng;
//extern SPI_HandleTypeDef H_NFC_SPI;

#if ALIRO_MEASURE_PERFORMANCE
/**
 * @brief External declaration of the TIM handle used in the application.
 *
 * This handle is typically initialized and configured elsewhere in the firmware
 * to manage timer operations. It allows access to timer functionalities such as
 * starting, stopping, and configuring timer events.
 *
 * @note Ensure that H_TIM is properly initialized before use.
 */
extern TIM_HandleTypeDef H_TIM;
#endif //ALIRO_MEASURE_PERFORMANCE

#if !(defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx))
/**
 * @brief External declaration of the UART handle for Aliro application.
 *
 * This handle is used to interface with the UART peripheral in the Aliro application.
 * It should be defined and initialized elsewhere in the project.
 */
extern UART_HandleTypeDef H_ALIRO_UART;
#endif //defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)




/* Variables ---------------------------------------------------------*/

/**
 * @brief Aliro protocol versions supported.
 */
uint32_t xProtocolVersionSupported = ALIRO_PROT_VER_DEFAULT;

/**
 * @brief Advertising flags for BLE.
 */
uint8_t advflags[] = { 0x02, 0x01, 0x06 };

/**
 * @brief Aliro advertising header for BLE.
 */
uint8_t aliroadvheader[] = { 0x1B, 0x16, 0xF2, 0xFF, BLE_ALIRO_FLOWS_SUPPORTED }; 

/**
 * @brief BLE transmit power value.
 */
uint8_t txpower[] = { 0xEB };

/**
 * @brief Default expiry timestamp value.
 */
uint32_t expirytimestamp = EXPIRY_TIME_STAMP_DEFAULT_VALUE;

/**
 * @brief Aliro service parameters.
 */
alirosvc_param_t alirosvc_param = 
  {
    .proposedSPSM = {0x00,0x8f}, 
    .supportedProtocolVersion = {HIGH_BYTE(ALIRO_PROT_VER_DEFAULT), LOW_BYTE(ALIRO_PROT_VER_DEFAULT)}, 
    .featuresSupported = {0x00}
  };

/**
 * @brief Indicates if Aliro is currently running.
 */
bool isAliroRunning = false;

/**
 * @brief NFC session context.
 */
ACWG_SessionContext_t context_nfc;

/**
 * @brief NFC session context.
 */
ACWG_SessionContext_t context_ble;

/**
 * @brief Verbosity level for logging.
 */
verblevel_t nVerbosityLevel = VERB_VERBOSE;

/**
 * @brief BLE inhibit flag.
 */
uint8_t ble_inhibit = 0;

/**
 * @brief Global communication protection counter.
 */
uint8_t globalCommProtectCnt = 0;

/**
 * @brief UART character buffer.
 */
uint8_t UartChars[128 + 1] = {0};

/**
 * @brief Length of UART character buffer.
 */
uint16_t UartChars_len = 0;

/**
 * @brief Timestamp offset value.
 */
uint32_t nTimeStampOffset = 0;

/**
 * @brief Delta offset for timestamp calculations.
 */
int32_t nTimeStampDeltaOffset = 0;

/**
 * @brief Buffer for SPSM data.
 */
uint8_t spsmDataBuf[2+1+((1+1)*2)+2] = {0};

/**
 * @brief Saved selected protocol version (16-bit).
 */
uint16_t savedselectedProtocolVersion_16=0;

/**
 * @brief EXTI handle for ST25.
 */
static EXTI_HandleTypeDef hst25_exti;

/**
 * @brief NFC proprietary buffer.
 */
static uint8_t NFC_PROP_BUF[] = NFC_PROP_BUF_CONTENT;

uint32_t timeoutPrepareRanging = TIMEOUT_PREPARE_RANGING_DEFAULT;
uint32_t timeoutExecuteRanging = TIMEOUT_EXECUTE_RANGING_DEFAULT;
uint32_t timeoutSuspendRanging = TIMEOUT_SUSPEND_RANGING_DEFAULT;
uint32_t timeoutResumeRanging  = TIMEOUT_RESUME_RANGING_DEFAULT;
uint32_t timeoutDoorOperation = TIMEOUT_DOOR_OPERATION_DEFAULT;
/* Private function prototypes -----------------------------------------------*/
/**
 * @brief Processes UART input data.
 */
void ProcessUartInput(void);

#if defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)

/**
 * @brief UART receive complete callback.
 * @param huart Pointer to UART handle.
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(tmpUartChar == '\r')
  {
    //can process buffer
    if(tmpUartChars_len==0)UartChars[tmpUartChars_len++] = tmpUartChar;
    UartChars_len = tmpUartChars_len;
    tmpUartChars_len = 0;
  }
  else
  {
    //discard '\n'
    if(tmpUartChar == '\n' && tmpUartChars_len == 0)
    {
    }
    else
    {
      //check tmpUartChars_len overflow
      UartChars[tmpUartChars_len++] = tmpUartChar;
    }
  }
  
  //  UartChars[0] = tmpUartChar;
//  UartChars_len = 1;

  HAL_UART_Receive_IT(HH_ALIRO_UART, &tmpUartChar, 1);
}


/**
 * @brief Debug print function using UART.
 * @param sFormatString Format string.
 * @return Status code.
 */
void Log_Module_Print(Log_Verbose_Level_t VerboseLevel, Log_Region_t Region, const char * sFormatString, ...)
{
  uint8_t xRes = 1;
  char cBufferPrintf[256+1] = { 0 };
  va_list ap;
  va_start (ap, sFormatString);
  uint16_t nCharsToPrint = vsnprintf(cBufferPrintf, 256, sFormatString, ap);
  va_end (ap);
  
  xRes |= AliroPort_UART_Transmit(HH_ALIRO_UART, (uint8_t *)cBufferPrintf, nCharsToPrint, 0x1000);
  
  return;
}
#endif //defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)




/* Private function -----------------------------------------------*/

#ifdef STM32WBA65xx

/**
 * @brief Initializes hardware cryptography modules.
 */
void HwCryptoInit(void)
{
	MX_HASH_Init();
}
#endif //STM32WBA65xx


/**
 * @brief Transmits data over UART.
 * @param huart UART handle.
 * @param pData Pointer to data buffer.
 * @param Size Number of bytes to transmit.
 * @param Timeout Timeout value.
 * @return HAL status.
 */
HAL_StatusTypeDef AliroPort_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
	return HAL_UART_Transmit(huart, pData, Size, Timeout);
}


/**
 * @brief Receives data over UART.
 * @param huart UART handle.
 * @param pData Pointer to data buffer.
 * @param Size Pointer to number of bytes received.
 * @param Timeout Timeout value.
 * @return HAL status.
 */
HAL_StatusTypeDef AliroPort_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t *Size, uint32_t Timeout)
{
  if(Timeout == 0)
  {
    if (UartChars_len)
    {
      memcpy(pData, UartChars, UartChars_len);
      *Size = UartChars_len;
      UartChars_len = 0;
      memset(UartChars, 0, sizeof(UartChars));
      return HAL_OK;
    }
    else
    {
      return HAL_ERROR;
    }
  }
  else
  {
    while(!UartChars_len){}
    memcpy(pData, UartChars, UartChars_len);
    *Size = UartChars_len;
    UartChars_len = 0;
    memset(UartChars, 0, sizeof(UartChars));
    return HAL_OK;
  }
}


/**
 * @brief Delays execution for a specified time.
 * @param Delay Delay time in milliseconds.
 */
void AliroPort_Delay(uint32_t Delay)
{
  HAL_Delay(Delay);
}


/**
 * @brief Triggers a system reset using NVIC.
 */
void AliroPort_NVIC_SystemReset(void)
{
  HAL_NVIC_SystemReset();
}

/**
 * @brief Hook for TRANSACTION_SUCCESS_OPEN
 */
__weak void AliroPort_TRANSACTION_SUCCESS_OPEN(void)
{
  AliroPort_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
  
  PLATFORM_LOG(VERB_VERBOSE, "OPEN\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "     OOOOOOOOO     PPPPPPPPPPPPPPPPP   EEEEEEEEEEEEEEEEEEEEEENNNNNNNN        NNNNNNNN\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "   OO:::::::::OO   P::::::::::::::::P  E::::::::::::::::::::EN:::::::N       N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, " OO:::::::::::::OO P::::::PPPPPP:::::P E::::::::::::::::::::EN::::::::N      N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::::OOO:::::::OPP:::::P     P:::::PEE::::::EEEEEEEEE::::EN:::::::::N     N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O::::::O   O::::::O  P::::P     P:::::P  E:::::E       EEEEEEN::::::::::N    N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::O     O:::::O  P::::P     P:::::P  E:::::E             N:::::::::::N   N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::O     O:::::O  P::::PPPPPP:::::P   E::::::EEEEEEEEEE   N:::::::N::::N  N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::O     O:::::O  P:::::::::::::PP    E:::::::::::::::E   N::::::N N::::N N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::O     O:::::O  P::::PPPPPPPPP      E:::::::::::::::E   N::::::N  N::::N:::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::O     O:::::O  P::::P              E::::::EEEEEEEEEE   N::::::N   N:::::::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::O     O:::::O  P::::P              E:::::E             N::::::N    N::::::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O::::::O   O::::::O  P::::P              E:::::E       EEEEEEN::::::N     N:::::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "O:::::::OOO:::::::OPP::::::PP          EE::::::EEEEEEEE:::::EN::::::N      N::::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, " OO:::::::::::::OO P::::::::P          E::::::::::::::::::::EN::::::N       N:::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "   OO:::::::::OO   P::::::::P          E::::::::::::::::::::EN::::::N        N::::::N\r\n");
  PLATFORM_LOG(VERB_VERBOSE, "     OOOOOOOOO     PPPPPPPPPP          EEEEEEEEEEEEEEEEEEEEEENNNNNNNN         NNNNNNN\r\n");
}

/**
 * @brief Hook for TRANSACTION_SUCCESS_CLOSE
 */
__weak void AliroPort_TRANSACTION_SUCCESS_CLOSE(void)
{
  AliroPort_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
  PLATFORM_LOG(VERB_VERBOSE, "AliroPort_TRANSACTION_SUCCESS_CLOSE\r\n");
}

/**
 * @brief Hook for TRANSACTION_FAILURE
 */
__weak void AliroPort_TRANSACTION_FAILURE(void)
{
//  AliroPort_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
  PLATFORM_LOG(VERB_VERBOSE, "AliroPort_TRANSACTION_FAILURE\r\n");
}

/**
 * @brief Writes a value to a GPIO pin.
 * @param GPIOx GPIO port.
 * @param GPIO_Pin GPIO pin number.
 * @param PinState Pin state to set.
 */
void AliroPort_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
  HAL_GPIO_WritePin(GPIOx, GPIO_Pin, PinState);
}


/**
 * @brief Executes a serial command received over UART.
 * @param pRxBuffer Pointer to received buffer.
 * @param iRxBufferSize Size of received buffer.
 */
void Aliro_SerialCmdExecute( uint8_t * pRxBuffer, uint16_t iRxBufferSize )
{
//	PLATFORM_LOG(VERB_NORMAL, "%s cmd received\r\n", pRxBuffer);

  if(iRxBufferSize != 0)
  {
    memcpy(UartChars, pRxBuffer, iRxBufferSize);
    UartChars_len = iRxBufferSize;
  }
  else
  {
    pRxBuffer[0] = '\n';
    UartChars_len = 1;
  }
        // tmpUartChar = pRxBuffer[0];
//	PLATFORM_LOG(VERB_NORMAL, "tmpUartChar is set to %c\r\n", tmpUartChar);
}


/**
 * @brief Sets the verbosity level for logging.
 * @param c Character representing verbosity level.
 */
void SetVerbosityLevel(uint8_t c)
{
  switch(c)
  {
  case '0':
    PLATFORM_LOG(VERB_ALWAYS, "Verbosity level set to VERB_NULL\r\n");
    nVerbosityLevel = VERB_NULL;
    break;
  case '1':
    PLATFORM_LOG(VERB_ALWAYS, "Verbosity level set to VERB_ALWAYS\r\n");
    nVerbosityLevel = VERB_ALWAYS;
    break;
  case '2':
    PLATFORM_LOG(VERB_ALWAYS, "Verbosity level set to VERB_CRITICAL\r\n");
    nVerbosityLevel = VERB_CRITICAL;
    break;
  case '3':
    PLATFORM_LOG(VERB_ALWAYS, "Verbosity level set to VERB_ERROR\r\n");
    nVerbosityLevel = VERB_ERROR;
    break;
  case '4':
    PLATFORM_LOG(VERB_ALWAYS, "Verbosity level set to VERB_NORMAL\r\n");
    nVerbosityLevel = VERB_NORMAL;
    break;
  case '5':
    PLATFORM_LOG(VERB_ALWAYS, "Verbosity level set to VERB_VERBOSE\r\n");
    nVerbosityLevel = VERB_VERBOSE;
    break;
  case '6':
    PLATFORM_LOG(VERB_ALWAYS, "Verbosity level set to VERB_INFO\r\n");
    nVerbosityLevel = VERB_INFO;
    break;
  default:
    break;
  }
  
}

/* ############ RTC FUNCIONS ########### */


/**
 * @brief Returns the number of days in a given month and year.
 * @param year Year value.
 * @param month Month value.
 * @return Number of days in the month.
 */
uint8_t DaysInMonth(uint16_t year, uint8_t month)//howManyDaysAreInThatMonth
{
  year += 2000;
  
  switch(month)
  {
  case 1: case 3: case 5: case 7: case 8: case 10: case 12:
    return 31;
  case 4: case 6: case 9: case 11:
    return 30;
  case 2:
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
      return 29;
    else
      return 28;
  default:
    return 28;
  }
}

void GetCompilerDateTime(struct tm *timeDate)
{
  uint8_t cur_date[] = __DATE__;
  uint8_t cur_time[] = __TIME__;
  /** Initialize RTC and set the Time and Date
  */
  timeDate->tm_hour = GET_IAR_HOURS(cur_time);
  timeDate->tm_min = GET_IAR_MINUTES(cur_time);
  timeDate->tm_sec = GET_IAR_SECONDS(cur_time);
  timeDate->tm_isdst = -1;
  
  timeDate->tm_mon = GET_IAR_MONTH(cur_date);
  timeDate->tm_mday = GET_IAR_DATE(cur_date);
  timeDate->tm_year = GET_IAR_YEAR(cur_date) - 2000;
}

/**
 * @brief Retrieves the current time and date from the RTC.
 * @param timeDate Pointer to struct tm to store the time and date.
 */
void GetTimeDate(struct tm *timeDate)
{
#if USE_RTC
  RTC_TimeTypeDef tempTime = {0};
  RTC_DateTypeDef tempDate = {0};
  
  HAL_RTC_GetTime(&ALIRO_HRTC, &tempTime, RTC_FORMAT_BIN);
  HAL_RTC_GetDate(&ALIRO_HRTC, &tempDate, RTC_FORMAT_BIN);
  
  timeDate->tm_sec = tempTime.Seconds;
  timeDate->tm_min = tempTime.Minutes;
  timeDate->tm_hour = tempTime.Hours;
  timeDate->tm_mday = tempDate.Date;
  timeDate->tm_mon = tempDate.Month - 1; // tm_mon is 0-11
  timeDate->tm_year = tempDate.Year + 100; // tm_year is years since 1900
  timeDate->tm_isdst = -1; // Not considering daylight saving time
#else //USE_RTC
  GetCompilerDateTime(timeDate);
#endif //USE_RTC
}

/**
 * @brief Displays the current date and time from RTC.
 */
void ShowTimeDate(void)
{
#if USE_RTC
  RTC_TimeTypeDef tempTime = {0};
  RTC_DateTypeDef tempDate = {0};
  HAL_RTC_GetTime(&ALIRO_HRTC, &tempTime, RTC_FORMAT_BIN);
  HAL_RTC_GetDate(&ALIRO_HRTC, &tempDate, RTC_FORMAT_BIN);
  
  PLATFORM_LOG(VERB_NORMAL, "\r\nDate: %02d/%02d/%04d - Time: %02d:%02d:%02d\r\n",
               tempDate.Date, tempDate.Month, tempDate.Year + 2000,
               tempTime.Hours, tempTime.Minutes, tempTime.Seconds);
#else //USE_RTC
  
#endif //USE_RTC
}

#if USE_RTC

/**
 * @brief Sets the RTC status in backup register.
 * @param nStatus Status value to set.
 */
void SetRtcSettingStatus(uint32_t nStatus)
{
  HAL_RTCEx_BKUPWrite(&ALIRO_HRTC, ALIRO_RTC_BKP_TIME, nStatus);
}

/**
 * @brief Checks the RTC status and logs the result.
 */
void checkRTC()
{
  PLATFORM_LOG(VERB_NORMAL, "checkRTC()\r\n");
  if (HAL_RTCEx_BKUPRead(&ALIRO_HRTC, ALIRO_RTC_BKP_TIME) == ALIRO_RTC_SET_STATUS_GOOD)
  {
    //everything ok
    PLATFORM_LOG(VERB_NORMAL, "Date Time GOOD: taken from serial port\r\n");
  }
  else if (HAL_RTCEx_BKUPRead(&ALIRO_HRTC, ALIRO_RTC_BKP_TIME) == ALIRO_RTC_SET_STATUS_IAR)
  {
    //IAR DATE and no set from serial port
    PLATFORM_LOG(VERB_NORMAL, "Date Time taken from COMPILE TIME\r\n");
  }
  else
  {
    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef sDate = {0};
    
    struct tm IARDateTime;
    GetCompilerDateTime(&IARDateTime);
    
    /** Initialize RTC and set the Time and Date
    */
    sTime.Hours = IARDateTime.tm_hour;
    sTime.Minutes = IARDateTime.tm_min;
    sTime.Seconds = IARDateTime.tm_sec;
    sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    sTime.StoreOperation = RTC_STOREOPERATION_RESET;
    if (HAL_RTC_SetTime(&ALIRO_HRTC, &sTime, RTC_FORMAT_BCD) != HAL_OK)
    {
      Error_Handler();
    }
    sDate.WeekDay = RTC_WEEKDAY_MONDAY;
    sDate.Month = IARDateTime.tm_mon;
    sDate.Date = IARDateTime.tm_mday;
    sDate.Year = IARDateTime.tm_year;
    if (HAL_RTC_SetDate(&ALIRO_HRTC, &sDate, RTC_FORMAT_BCD) != HAL_OK)
    {
      Error_Handler();
    }
    SetRtcSettingStatus(ALIRO_RTC_SET_STATUS_IAR);
    PLATFORM_LOG(VERB_NORMAL, "No date time set: setting COMPILE TIME\r\n");
  }
  PLATFORM_LOG(VERB_NORMAL, "checkRTC()\r\n");
}
#endif //USE_RTC


/**
 * @brief Initializes cryptographic modules and checks provisioning.
 * @return true if initialization is successful, false otherwise.
 */
bool CryptoInit()
{
  PLATFORM_LOG(VERB_NORMAL, "CryptoInit() start\r\n");
  
  PLATFORM_LOG(VERB_NORMAL, "SM_initCryptoMCU()\r\n");
  SM_initCryptoMCU(&hrng, &hpka, &hhash, &hcryp);

  extern bool CheckReaderPubK(void);
  if(!CheckReaderPubK())
  {
    return false;
  } 
  
  return true;
}

/**
 * @brief Receives a character from the USART interface in blocking mode.
 *
 * This function attempts to receive a character from the USART interface,
 * blocking until a character is received or a timeout occurs.
 *
 * @param[out] buf Pointer to the buffer where the received character will be stored.
 * @return Number of characters received (0 if none or on error).
 */
uint16_t UsartGetCharBlocking(uint8_t *buf)
{
  uint16_t num_chars = 0;
  if(HAL_OK == AliroPort_UART_Receive(HH_ALIRO_UART, buf, &num_chars, HAL_MAX_DELAY))
  {
    return num_chars;
  }
  return 0;
}

/**
 * @brief Receives a character from the USART interface in non-blocking mode.
 *
 * This function attempts to receive a character from the USART interface
 * without blocking. If no character is available, it returns immediately.
 *
 * @param[out] buf Pointer to the buffer where the received character will be stored.
 * @return Number of characters received (0 if none or on error).
 */
uint16_t UsartGetChar(uint8_t *buf)
{
  uint16_t num_chars = 0;
  if(HAL_OK == AliroPort_UART_Receive(HH_ALIRO_UART, buf, &num_chars, 0))
  {
    return num_chars;
  }
  return 0;
}


/* Public functions -----------------------------------------------*/

/* ############ LOG FUNCIONS ########### */

/**
 * @brief Generates a timestamp string in the format "[seconds.milliseconds] ".
 *
 * This function creates a timestamp based on the current tick value returned by HAL_GetTick().
 * The timestamp is formatted as "[%d.%03d] ", where %d is the number of seconds and %03d is the
 * number of milliseconds. The formatted string is stored in the provided Data buffer.
 *
 * @param[out] Data Pointer to the buffer where the timestamp string will be stored.
 * @param[in] SizeMax Maximum size of the Data buffer.
 * @param[out] TimeStampSize Pointer to a variable where the size of the generated timestamp string will be stored.
 *
 * @note If the formatted string fits in the buffer, TimeStampSize will be set to its length; otherwise, it will be zero.
 */
void SecondMillis_CallBack_TimeStamp(char * Data, uint16_t SizeMax, uint16_t * TimeStampSize)
{
  * TimeStampSize = 0;
  if(snprintf(Data, SizeMax, "[%ld.%03ld] ", HAL_GetTick()/1000, HAL_GetTick()%1000) >0)
  {
    * TimeStampSize = strnlen(Data, SizeMax);
  }
}

#if ALIRO_MEASURE_PERFORMANCE
/**
 * @brief Retrieves the current timestamp from the hardware timer.
 * @return Current timestamp in milliseconds.
 */

uint32_t GetTimeStamp()
{
  if(H_TIM.Instance->CNT < nTimeStampOffset)
  {
    //TODO: add rollover check
  }
  //us
//  return 100*(htim1.Instance->CNT - nTimeStampOffset);
  //ms
  return (H_TIM.Instance->CNT - nTimeStampOffset) / 10;
}

/**
 * @brief Prints the current timestamp along with a custom string and delta from the previous timestamp.
 * @param pstr Pointer to a string to print with the timestamp.
 * @return The current timestamp value.
 */
uint32_t PrintTimeStamp(char *pstr)
{
  uint32_t nTimeStampDelta = GetTimeStamp() - nTimeStampDeltaOffset;
  nTimeStampDeltaOffset = GetTimeStamp();
//  PLATFORM_LOG(VERB_CRITICAL, "TS: %ld %s\r\n", HAL_GetTick(), pstr);
//  PLATFORM_LOG(VERB_CRITICAL, "TS(ms): %ld %s\r\n", GetTimeStamp(), pstr);
  if(nTimeStampDeltaOffset != 0)
  {
    PLATFORM_LOG(VERB_STATISTICS, "TS(ms): %ld (delta: %ld) %s\r\n", nTimeStampDeltaOffset, nTimeStampDelta, pstr);
  }
  else
  {
    PLATFORM_LOG(VERB_STATISTICS, "TS(ms): %ld %s\r\n", nTimeStampDeltaOffset, pstr);
  }
  return nTimeStampDeltaOffset;
}

/**
 * @brief Resets all saved timestamps in the statistics items array.
 */
void ResetAllSavedTimeStamps()
{
  for(uint32_t i=0;i<countof(statItems);i++)
  {
    statItems[i].ts = 0;
  }
}

/**
 * @brief Searches for a statistics item by its position.
 * @param pos The position to search for.
 * @return Pointer to the found statistics item, or NULL if not found.
 */
statItem_t *SearchPos(timeStampsPos_t pos)
{
  statItem_t *retpointer = NULL;;
  for(uint32_t i=0;i<countof(statItems);i++)
  {
    if(statItems[i].pos == pos)
    {
      retpointer = &statItems[i];
    }
  }
  return retpointer;
}

/**
 * @brief Saves the current timestamp to the statistics item at the specified position.
 * @param pos The position to save the timestamp for.
 */
void SaveTimeStamp(timeStampsPos_t pos)
{
  statItem_t *statitem;
  statitem = SearchPos((timeStampsPos_t)pos);
  if(statitem)
  {
    statitem->ts = PrintTimeStamp(statitem->title);
  }
}

/**
 * @brief Prints a report of all saved timestamps and their deltas.
 */
void PrintTimeStampReport()
{
  PLATFORM_LOG(VERB_STATISTICS_REPORT, "\r\nSTATISTICS:\r\n");
  PLATFORM_LOG(VERB_STATISTICS_REPORT, "\r\nTITLE | ");
  for(uint32_t i=0;i<NUM_TIMESTAMPS;i++)
  {
    statItem_t *statitem;
    statitem = SearchPos((timeStampsPos_t)i);
    if(statitem)
    {
      if(statitem->toPrint)
      {
        PLATFORM_LOG(VERB_STATISTICS_REPORT, statitem->title);
        PLATFORM_LOG(VERB_STATISTICS_REPORT, " | ");
      }
    }
  }
  PLATFORM_LOG(VERB_STATISTICS_REPORT, "\r\n-------");
  for(uint32_t i=0;i<NUM_TIMESTAMPS;i++)
  {
    statItem_t *statitem;
    statitem = SearchPos((timeStampsPos_t)i);
    if(statitem)
    {
      if(statitem->toPrint)
      {
        PLATFORM_LOG(VERB_STATISTICS_REPORT, "------------");
      }
    }
  }
  PLATFORM_LOG(VERB_STATISTICS_REPORT, "\r\nTS    | ");
  for(uint32_t i=0;i<NUM_TIMESTAMPS;i++)
  {
    statItem_t *statitem;
    statitem = SearchPos((timeStampsPos_t)i);
    if(statitem)
    {
      if(statitem->toPrint)
      {
        PLATFORM_LOG(VERB_STATISTICS_REPORT, "%06ld   ", statitem->ts);
        PLATFORM_LOG(VERB_STATISTICS_REPORT, " | ");
      }
    }
  }
  PLATFORM_LOG(VERB_STATISTICS_REPORT, "\r\nDELTA | ");
  uint32_t previousTs = 0;
  for(uint32_t i=0;i<NUM_TIMESTAMPS;i++)
  {
    statItem_t *statitem;
    statitem = SearchPos((timeStampsPos_t)i);
    if(statitem)
    {
      if(statitem->toPrint)
      {

        if (statitem->ts == 0)
        {
          PLATFORM_LOG(VERB_STATISTICS_REPORT, "%06ld   ", statitem->ts);
          PLATFORM_LOG(VERB_STATISTICS_REPORT, " | ");
        }
        else
        {
          PLATFORM_LOG(VERB_STATISTICS_REPORT, "%06ld   ", statitem->ts - previousTs);
          PLATFORM_LOG(VERB_STATISTICS_REPORT, " | ");
          previousTs = statitem->ts;
        }
      }
    }
  }
  PLATFORM_LOG(VERB_STATISTICS_REPORT, "\r\n");
  PLATFORM_LOG(VERB_STATISTICS_REPORT, "\r\n");
}

/**
 * @brief Resets the timestamp offset to the current hardware timer value and prints a reset message.
 */
void ResetTimeStamp()
{
  nTimeStampOffset = H_TIM.Instance->CNT;
    PrintTimeStamp("ResetTimeStamp");
}

#endif //ALIRO_MEASURE_PERFORMANCE

/**
 * @brief Prints an error string corresponding to the given ACWG status code.
 * @param errstr Error string to print.
 * @param retval ACWG status code.
 */
void PrintErrorString(char*errstr, ACWG_Status_t retval)
{
  switch(retval)
  {
  CREATE_CASE(ACWG_OK)
  CREATE_CASE(ACWG_Error_Generic)
  CREATE_CASE(ACWG_Error_FastTransFailed)
  CREATE_CASE(ACWG_Error_Parsing)
  CREATE_CASE(ACWG_Error_ShortBuf)
  CREATE_CASE(ACWG_Error_Crypto_KeyGen)
  CREATE_CASE(ACWG_Error_Crypto_TransIDGen)
  CREATE_CASE(ACWG_Error_Crypto_ECDH)
  CREATE_CASE(ACWG_Error_Crypto_Derivation)
  CREATE_CASE(ACWG_Error_Crypto_EncryptDecrypt)
  CREATE_CASE(ACWG_Error_Crypto_Hash)
  CREATE_CASE(ACWG_Error_Crypto_SigGen)
  CREATE_CASE(ACWG_Error_Crypto_SigVerif)
  CREATE_CASE(ACWG_Error_HW)
  CREATE_CASE(ACWG_Error_WrongState)
  CREATE_CASE(ACWG_Error_KeyslotNotFound)
  CREATE_CASE(ACWG_Error_Parsing_NoKeyslot)
  CREATE_CASE(ACWG_Error_Parsing_NoPublicKey)
  default:
    break;
  }
}

/**
 * @brief Prints a buffer in hexadecimal format, always using info verbosity level.
 * @param pstr Description string to print before the buffer.
 * @param pBuf Pointer to the buffer to print.
 * @param nSize Size of the buffer.
 */
void PrintBufferHexAlways(const char *pstr, const uint8_t *pBuf, const uint32_t nSize)
{
  verblevel_t nTmpVerbosity = nVerbosityLevel;
  nVerbosityLevel = VERB_INFO;
  PrintBufferHex(pstr, pBuf, nSize);
  nVerbosityLevel = nTmpVerbosity;
}

/**
 * @brief Prints a buffer in hexadecimal format if enabled.
 * @param pstr Description string to print before the buffer.
 * @param pBuf Pointer to the buffer to print.
 * @param nSize Size of the buffer.
 */
void PrintBufferHex(const char *pstr, const uint8_t *pBuf, const uint32_t nSize)
{
  if(PRINT_BUFFERS_HEX)
  {
    PLATFORM_LOG(VERB_INFO, pstr);
    PLATFORM_LOG(VERB_INFO, " (size: %d)", nSize);
    for(uint32_t i=0;i<nSize;i++)
    {
      if(i%16 == 0)
      {
        if(BYTE_INDEX_IN_PRINT)
        {
          PLATFORM_LOG(VERB_INFO, "\r\n %3d -> %3d - ", i, (i+15 < nSize)?(i+15):(nSize-1));
        }
        else
        {
          PLATFORM_LOG(VERB_INFO, "\r\n");
        }
      }
      PLATFORM_LOG(VERB_INFO, "%.2x ", pBuf[i]);
    }
    PLATFORM_LOG(VERB_INFO, "\r\n");
  }
}

/**
 * @brief Interrupt service routine for Button 1. Resets the system after a delay.
 */
void Button1_isr( void )
{
  AliroPort_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
  uint32_t a = 10000000;while(a--);
  AliroPort_NVIC_SystemReset();
}

/**
 * @brief Interrupt service routine for the ST25R200 peripheral.
 */
void Alirost25r200Isr(void)
{
  HAL_EXTI_IRQHandler(&hst25_exti);
}

/* ############ APPLICATION FUNCIONS ########### */
/**
 * @brief Initializes the Aliro application.
 *
 * This function performs various initialization tasks for the Aliro application,
 * including RTC setup, performance measurement configuration, GPIO pin settings,
 * NFC context initialization, and logging of application and security library versions.
 *
 * @return true if initialization is successful, false otherwise.
 */
bool aliro_init()
{
#if USE_RTC
  // #define CFG_LOG_INSERT_TIME_STAMP_INSIDE_THE_TRACE  
  Log_Module_RegisterTimeStampFunction(SecondMillis_CallBack_TimeStamp);

  checkRTC();
#endif //USE_RTC
  
#if ALIRO_MEASURE_PERFORMANCE
  HAL_TIM_Base_Start(&H_TIM);
#endif //ALIRO_MEASURE_PERFORMANCE

  AliroPort_Delay(10);
  AliroPort_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_SET);
  AliroPort_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
//  PLATFORM_LOG(VERB_NORMAL, CLEAR_SCREEN_COMMAND);

  
  context_nfc.init.authPolicy = 0x01;
  context_nfc.init.useStepUp = 0x01;
  context_nfc.init.useExchange = 0x80;

  context_ble.init.authPolicy = 0x01;
  context_ble.init.useStepUp = 0x01;
  context_ble.init.useExchange = 0x80;
  
//  tlw_SetAccessElementString("floor1");
  tlw_SetAccessElementString((uint8_t*)"matter1");
  
  ShowTimeDate();

  PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_GREEN);
  PLATFORM_LOG(VERB_NORMAL, "************  " ALIRO_APP_NAME " ************\r\n");
  PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
  PLATFORM_LOG(VERB_NORMAL, "CSA Aliro demonstration using MCU crypto\r\n");

  PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
  PLATFORM_LOG(VERB_NORMAL, "Application: %s v%d.%d.%d\r\n",
    app_build_info.fw_description,
    app_build_info.app_major,  
    app_build_info.app_minor,  
    app_build_info.app_subminor);

    PLATFORM_LOG(VERB_NORMAL, "Built at %s - %s\r\n",
    app_build_info.date,  
    app_build_info.time);    

  
  const ACWG_build_info_t* acwg_build_info = ACWG_SecurityGetVersion();
  
  PLATFORM_LOG(VERB_NORMAL, "Security: %s v%d.%d.%d\r\n",
    acwg_build_info->fw_description,
    acwg_build_info->ver_major,
    acwg_build_info->ver_minor,
    acwg_build_info->ver_subminor);

  PLATFORM_LOG(VERB_NORMAL, "Built at %s - %s\r\n",
    acwg_build_info->date,  
    acwg_build_info->time);

  PLATFORM_LOG(VERB_NORMAL, "App Aliro spec.: v%d.%d.%d\r\n",
    app_build_info.aliro_spec_major,
    app_build_info.aliro_spec_minor,
    app_build_info.aliro_spec_subminor);

  PLATFORM_LOG(VERB_NORMAL, "Lib Aliro spec.: v%d.%d.%d\r\n",
    acwg_build_info->aliro_spec_major,
    acwg_build_info->aliro_spec_minor,
    acwg_build_info->aliro_spec_subminor);

  if(
     (acwg_build_info->aliro_spec_major != app_build_info.aliro_spec_major) ||
       (acwg_build_info->aliro_spec_minor != app_build_info.aliro_spec_minor) ||
         (acwg_build_info->aliro_spec_subminor != app_build_info.aliro_spec_subminor) )
  {
    PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_RED);
    PLATFORM_LOG(VERB_ERROR, "Misalignment Aliro spec in app and lib\r\n");
    PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
    Error_Handler();
  }

  if(
    (acwg_build_info->ver_major != app_build_info.acwg_ver_major) ||
      (acwg_build_info->ver_minor != app_build_info.acwg_ver_minor) ||
        (acwg_build_info->ver_subminor != app_build_info.acwg_ver_subminor) )
 {
   PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_RED);
   PLATFORM_LOG(VERB_ERROR, "Misalignment ACWG security app and lib\r\n");
   PLATFORM_LOG(VERB_ERROR, "ACWG security lib ver: %d.%d.%d required ver: %d.%d.%d\r\n",
                acwg_build_info->ver_major,
                acwg_build_info->ver_minor,
                acwg_build_info->ver_subminor,
                app_build_info.acwg_ver_major,
                app_build_info.acwg_ver_minor,
                app_build_info.acwg_ver_subminor);
   PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
   Error_Handler();
 }

  // PLATFORM_LOG(VERB_NORMAL, "Application start\r\n");

  PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
#if USE_GEN2
  PLATFORM_LOG(VERB_NORMAL, "Based on TruELock GEN2\r\n");
#else //USE_GEN2
  
#if defined(STM32WBA52xx)||defined(STM32WBA55xx)
  PLATFORM_LOG(VERB_NORMAL, "Based on STM32WBA + " X_NUCLEO_NFC_NAME);
#endif //defined(STM32WBA52xx)||defined(STM32WBA55xx)
#if defined(STM32U545xx) || defined(STM32U585xx)
  PLATFORM_LOG(VERB_NORMAL, "Based on STM32U5 + " X_NUCLEO_NFC_NAME);
#endif //defined(STM32U545xx) || defined(STM32U585xx)
#if defined(STM32U385xx)
  PLATFORM_LOG(VERB_NORMAL, "Based on STM32U3 + " X_NUCLEO_NFC_NAME);
#endif //defined(STM32U385xx)
#if defined(STM32WBA65xx)
  PLATFORM_LOG(VERB_NORMAL, "Based on STM32WBA65 + " X_NUCLEO_NFC_NAME);
#endif //STM32WBA65xx
#endif //USE_GEN2
  PLATFORM_LOG(VERB_NORMAL, "\r\n");
  PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);

  uint32_t UIDw[3];
  UIDw[0] = HAL_GetUIDw0();
  UIDw[1] = HAL_GetUIDw1();
  UIDw[2] = HAL_GetUIDw2();
  
  PLATFORM_LOG(VERB_NORMAL, "MCUID: %.08X%.08X%.08X\r\n", UIDw[0], UIDw[1], UIDw[2]);
  
#ifdef STM32WBA65xx
  PLATFORM_LOG(VERB_NORMAL, "HwCryptoInit()\r\n");
  HwCryptoInit();
#endif //STM32WBA65xx

#if defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)
  HAL_UART_Receive_IT(HH_ALIRO_UART, &tmpUartChar, 1);
#endif //defined(STM32U545xx) || defined(STM32U585xx) || defined(STM32U385xx)

  if(CryptoInit() != true)
  {
    PLATFORM_LOG(VERB_NORMAL, "CryptoInit() FAIL\r\n");
    isAliroRunning = false;
    return false;
  }
  PLATFORM_LOG(VERB_NORMAL, "CryptoInit() OK\r\n");

  // NFC init
//  spiInit(&H_NFC_SPI);
  
  PLATFORM_LOG(VERB_NORMAL, "Start AliroNfcHwInit()...\r\n");
  AliroNfcHwInit();
  
  if(AliroNfcIni(&context_nfc) != true)
  {
	PLATFORM_LOG(VERB_NORMAL, "NFC AliroNfcIni FAIL\r\n");
	isAliroRunning = false;
	return false;
  }
  PLATFORM_LOG(VERB_NORMAL, "NFC AliroNfcIni() OK\r\n");
  context_nfc.init.cmdParams = DEFAULT_CMD_PARAMS_VALUE;
  
  if(getProvisioningFromFlash()->certificate_len > 0)
  {
    context_nfc.init.useLoadCert = 1;
  }
  else
  {
    context_nfc.init.useLoadCert = 0;
  }
  memcpy(&context_ble, &context_nfc, sizeof(context_ble));
  
  // BLE init
  //uncomment the following line if you want to disable BLE by default
//  ble_inhibit = 1;

  // UWB init

  // Extra init

  PLATFORM_LOG(VERB_NORMAL, "aliro_init() OK\r\n");
  isAliroRunning = true;
  
  UWB_HW_init();

  DISPLAY_SPLASH();
  
  return true;
}

/**
 * @brief Main processing loop for the Aliro application.
 *
 * This function handles the main processing tasks for the Aliro application,
 * including NFC, BLE, and UWB processing, as well as handling extra processes
 * such as UART input.
 */
void aliro_process(){
   
  if(isAliroRunning == true)
  {
    // NFC process
    AliroNfcCycle(&context_nfc);
    
    // BLE process
#if ALIRO_BLE
    tlw_ble_SM_step();
#endif //ALIRO_BLE
    
    UWB_HW_loop();
  }
  // Extra process
  ProcessUartInput();
}

#if defined (ST25R200)
/**
 * @brief Gets the remaining time for a platform timer.
 * @param discTmr The timer identifier.
 * @return Remaining time in milliseconds.
 */
uint32_t platformTimerGetRemaining(uint32_t discTmr)
{
  //TODO: implement this function
  
  return 0;
}
#endif //ST25R200

#if ALIRO_BLE

/**
 * @brief Fills a buffer with random data.
 * @param p_data Pointer to the buffer to fill.
 * @param len Length of the buffer in bytes.
 */
void get_random(uint8_t * p_data, uint16_t len)
{
  uint16_t rd_len = len;

  while(rd_len >= sizeof(uint32_t))
  {
    HW_RNG_Get(1, (uint32_t*)&(p_data[len - rd_len]));
    rd_len -= sizeof(uint32_t);
  }

  if(rd_len > (uint16_t)0)
  {
    uint32_t rd_val;
    HW_RNG_Get(1, &rd_val);
    for(;rd_len > (uint16_t)0; rd_len --)
    {
      p_data[len-rd_len] = (uint8_t)(rd_val & 0xFF);
      rd_val = rd_val >> 8UL;
    }
  }
}

/**
 * @brief Generates a dynamic SPSM value different from the old one.
 * @param old_spsm The previous SPSM value to avoid.
 * @return A new dynamic SPSM value.
 */
uint16_t get_dynamic_spsm(uint8_t old_spsm)
{
  uint8_t spsm = MIN_DYNAMIC_SPSM_VALUE;
  uint8_t sec_loop = (uint8_t)GET_SPSM_LOOP_NBR;
  do
  {
    get_random((uint8_t*)&spsm, sizeof(uint8_t));
    
    //TESTHARNESS patch
//    spsm = MIN_DYNAMIC_SPSM_VALUE;

    if((spsm < MIN_DYNAMIC_SPSM_VALUE)
    || (spsm >= MAX_DYNAMIC_SPSM_VALUE))
    {
      /* continue loop */
      spsm = MIN_DYNAMIC_SPSM_VALUE;
    }
    else
    {
      if (spsm == old_spsm)
      {
        spsm = MIN_DYNAMIC_SPSM_VALUE;
        break;
      }
    }

    --sec_loop;

  }while((spsm == MIN_DYNAMIC_SPSM_VALUE) && (sec_loop > (uint8_t)0));

  return spsm;
}

/* AES key used for dynamic tag generation */
uint32_t dyn_pKeyAES[4] = {0x00000000,0x00000000,0x00000000,0x00000000};

/**
 * @brief Manages BLE advertising based on the current state.
 *
 * This function checks if BLE advertising is disabled or inhibited,
 * and starts advertising if allowed.
 */
void AliroManageBleAdvertising()
{
  if(!BLE_ADV_DISABLE)
  {
    if(!ble_inhibit)
    {
      Adv_Request_cb(APP_BLE_ADV_FAST);
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "BLE ADV not started. BLE is disabled.\r\n");
    }
  }
}

/**
 * @brief Callback function called when BLE initialization is complete.
 *
 * This function is invoked after the BLE stack has been successfully initialized.
 * It manages BLE advertising by calling AliroManageBleAdvertising().
 */
void AliroBLE_Init_complete_cb()
{
  AliroManageBleAdvertising();
}

/**
 * @brief Callback function called when a BLE disconnection is complete.
 *
 * This function is triggered when a BLE connection is terminated.
 * It manages BLE advertising by calling AliroManageBleAdvertising().
 */
void AliroBleDisconnection_complete_cb()
{
  AliroManageBleAdvertising();
}

/**
 * @brief Callback function called when a BLE CoC (Credit-based Connection) disconnection is complete.
 *
 * This function is triggered when a BLE CoC connection is terminated.
 * It manages BLE advertising by calling AliroManageBleAdvertising().
 */
void AliroBleCocDisconnection_complete_cb()
{
  AliroManageBleAdvertising();
}

/**
 * @brief Generates a dynamic tag using AES encryption.
 *
 * This function creates a dynamic tag by encrypting a plaintext block constructed from the provided MAC address
 * and expiry timestamp using AES-128 ECB mode. The resulting encrypted data is byte-swapped and the first 7 bytes
 * are copied to the output dynamic tag buffer.
 *
 * @param[out] dynTag         Pointer to the buffer where the generated dynamic tag (7 bytes) will be stored.
 * @param[in]  mac            Pointer to the MAC address (6 bytes) used in the plaintext construction.
 * @param[in]  dynTag_expiry  Expiry timestamp to be included in the plaintext block.
 *
 * @return Always returns 0.
 *
 * @note Uses HAL CRYP peripheral for AES encryption. Calls Error_Handler() on failure.
 */
uint8_t generateDynamicTag(uint8_t *dynTag, uint8_t *mac, uint32_t dynTag_expiry)
{
  ////  0080 e12a9d17  
  ////  66100fe0
  
  PLATFORM_LOG(VERB_NORMAL, "Dinamic Tag Expiry: 0x%08X \r\n", dynTag_expiry);
  
  uint32_t aPlaintextECB_1 = 0;
  uint32_t aPlaintextECB_2 = 0;
  
  
  //    aPlaintextECB_1 = ((uint32_t)mac[0] << 8) | (mac[1]); 
  //    aPlaintextECB_2 = ((uint32_t)mac[2] << 24) | ((uint32_t)mac[3] << 16) | ((uint32_t)mac[4] << 8) | (mac[5]);
  
  aPlaintextECB_1 = ((uint32_t)mac[5] << 8) | (mac[4]); 
  aPlaintextECB_2 = ((uint32_t)mac[3] << 24) | ((uint32_t)mac[2] << 16) | ((uint32_t)mac[1] << 8) | (mac[0]);
  
  uint32_t aPlaintextECB[4] = {0, 0, 0, 0};
  //                          { 0x00000000, 0x0000c4bb, 0x86c32710, 0x7a4b8500 };
  //                          { 0x00000000, 0x00000080, 0xe12a9d17, 0x66100fe0 };
  aPlaintextECB[0] = 0;
  aPlaintextECB[1] = aPlaintextECB_1;
  aPlaintextECB[2] = aPlaintextECB_2;
  aPlaintextECB[3] = dynTag_expiry;
  
  
  /* Used for storing the encrypted text */
  uint32_t aEncryptedtext[4];
  
  hcryp.Instance = AES;
  if (HAL_CRYP_DeInit(&hcryp) != HAL_OK)
  {
    Error_Handler();
  }
  /* Initialize the CRYP peripheral */
  hcryp.Init.DataType      = CRYP_NO_SWAP;
  hcryp.Init.KeySize       = CRYP_KEYSIZE_128B;
  hcryp.Init.Algorithm     = CRYP_AES_ECB;
  hcryp.Init.pKey          = (uint32_t *)dyn_pKeyAES;
  hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_WORD;
  
  
  if (HAL_CRYP_Init(&hcryp) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
  
//  uint16_t ffp = 1;
  if(HAL_CRYP_Encrypt(&hcryp, aPlaintextECB, 4, aEncryptedtext, 1000) == HAL_OK)
  {
    
    uint32_t little_aEncryptedtext[4]= {0};
    for (uint8_t i=0; i<4; i++)
    {
      little_aEncryptedtext[i] = __byteswap32(aEncryptedtext[i]);
      
    }
    memcpy(dynTag, little_aEncryptedtext, 7);
    
    PLATFORM_LOG(VERB_NORMAL, "Dinamic Tag: ");
    for (uint8_t i=0; i<7; i++)
    {
      PLATFORM_LOG(VERB_NORMAL, "0x%02X ", dynTag[i]);
    }
    PLATFORM_LOG(VERB_NORMAL, "\r\n");
  }
  else
  {
    Error_Handler();
  }
  return 0;
}

/**
 * @brief Fills the BLE advertising buffer with appropriate data.
 *
 * This function constructs the BLE advertising packet by populating the provided buffer
 * with flags, header, TX power, reader ID, and a dynamically generated tag based on
 * the device's Bluetooth address and an expiry timestamp.
 *
 * @param[out] advbuf Pointer to the buffer where the advertising data will be stored.
 * @param[out] psize Pointer to a variable where the size of the advertising data will be stored.
 */
void fillAdvBuffer(uint8_t *advbuf, uint8_t *psize)
{
  // uint32_t expirytimestamp = 0x76100fe0;
  uint8_t tempdyntag[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  
  ////  0080 e12a9d17
  
  // uint8_t *bdaddr;
  // bdaddr = BleGetBdAddress();
  // ble_glue_get_bdaddr(bdaddr, NULL);
  PLATFORM_LOG(VERB_NORMAL, "BLE public address: ");
  PLATFORM_LOG(VERB_NORMAL, "%02X:%02X:%02X:%02X:%02X:%02X\r\n", BleGetBdAddress()[5], BleGetBdAddress()[4], BleGetBdAddress()[3], BleGetBdAddress()[2], BleGetBdAddress()[1], BleGetBdAddress()[0]);
  // PLATFORM_LOG(VERB_NORMAL, "%02X:%02X:%02X:%02X:%02X:%02X\r\n", bdaddr[5], bdaddr[4], bdaddr[3], bdaddr[2], bdaddr[1], bdaddr[0]);
  //           uint8_t testMAC[6] = {0x17, 0x9d, 0x2a, 0xe1, 0x80, 0x00};
  ////  66100fe0
  
#if 0
          PLATFORM_LOG(VERB_NORMAL, "Test vector for dynamic tag computation:\r\n");

          //          uint8_t tmpgrpreskey[] = { 0xf5, 0xb1, 0x65, 0x22, 0x4a, 0x58, 0xb7, 0x91, 0xdf, 0x6a, 0xf1, 0xd8, 0x30, 0x3e, 0x61, 0xcd };
          uint8_t tmpgrpreskey[] = { 
            0x22, 0x65, 0xb1, 0xf5,  
            0x91, 0xb7, 0x58, 0x4a,  
            0xd8, 0xf1, 0x6a, 0xdf,  
            0xcd, 0x61, 0x3e, 0x30 };
  
          //test vectors
          uint32_t tmpexpirytimestamp = 0x7a4b8500;
          //          uint8_t tmpaddr[6] = { 0xc4, 0xbb, 0x86, 0xc3, 0x27, 0x10 };
          uint8_t tmpaddr[6] = { 0x10, 0x27, 0xc3, 0x86, 0xbb, 0xc4 };
          
          memcpy(dyn_pKeyAES, tmpgrpreskey, 16);
          
          generateDynamicTag(tempdyntag, tmpaddr, tmpexpirytimestamp);
#endif //0

  PLATFORM_LOG(VERB_NORMAL, "Dynamic tag computation\r\n");
  uint32_t dyn_pKeyAES1[4] = {0x00000000,0x00000000,0x00000000,0x00000000};
  memcpy(dyn_pKeyAES1, getProvisioningFromFlash()->group_resolving_key, 16);
  
  PLATFORM_LOG(VERB_NORMAL, "group_resolving_key: ");
  for (uint8_t i=0; i<16; i++)
  {
    PLATFORM_LOG(VERB_NORMAL, "0x%02X ", getProvisioningFromFlash()->group_resolving_key[i]);
  }
  PLATFORM_LOG(VERB_NORMAL, "\r\n");
    
  for (uint8_t i=0; i<4; i++)
  {
    dyn_pKeyAES[i] = __byteswap32(dyn_pKeyAES1[i]);
    
  }

  
  generateDynamicTag(tempdyntag, (uint8_t*)BleGetBdAddress(), expirytimestamp);
  // generateDynamicTag(tempdyntag, bdaddr, expirytimestamp);
  //          GetDynamicTag(tempdyntag);
  
  
  memcpy(&advbuf[0], advflags, 3);
  memcpy(&advbuf[3], aliroadvheader, 5);
  memcpy(&advbuf[8], txpower, 1);
  
  
  uint8_t tmpreaderID[AUTH_READER_GROUP_ID_LEN_BYTES + AUTH_READER_GROUP_SUB_ID_LEN_BYTES];
  
  memcpy(tmpreaderID, getProvisioningFromFlash()->readerID, sizeof(tmpreaderID));
  
  uint8_t reader_group_id[8] = {0};   /**< reader_group id part*/
  uint8_t reader_group_sub_id[2] = {0};   /**< reader_group sub_id part*/
  
  // extern csa_reader_conf_t s_csa_reader_conf;
  memcpy(reader_group_id, &tmpreaderID[0], sizeof(reader_group_id));
  memcpy(reader_group_sub_id, &tmpreaderID[AUTH_READER_GROUP_ID_LEN_BYTES], sizeof(reader_group_sub_id));
  
  PrintBufferHex("Reader ID: ", tmpreaderID, 32);
  
  memcpy(&advbuf[9], reader_group_id, 8);
  memcpy(&advbuf[17], reader_group_sub_id, 2);
  //          memcpy(&advbuf[19], expirytimestamp, 4);
  uint32_t littleexp = __byteswap32(expirytimestamp); 
  memcpy(&advbuf[19], (uint8_t*)&littleexp, 4);
  advbuf[23] = 0x00; //RFU
  memcpy(&advbuf[24], tempdyntag, 7);
  
  if(*psize > 31)
  {
    *psize = 31;
  }
  
  PrintBufferHexAlways("Adv buffer: ", &advbuf[0], 31);
}


/**
 * @brief Calculates and returns the initial length for Aliro service parameters.
 *
 * This function computes the initial length required for Aliro service parameters
 * and stores the result in the location pointed to by @p pLen.
 * The calculation is based on a fixed formula involving protocol version and other constants.
 *
 * @param[out] pLen Pointer to a variable where the calculated length will be stored.
 * @return Always returns 0.
 */
uint8_t AliroSvcParamGetInitLen(uint8_t *pLen)
{
  *pLen = 2+1+((1+ALIRO_HAS_SEC_PROT_VER)*2)+2;
  return 0;
}

/**
 * @brief Fills the provided buffer with initial Aliro service parameters.
 *
 * This function populates the buffer pointed to by @p DataBuf with the initial
 * Aliro service parameters, including proposed SPSM and supported protocol versions.
 * The SPSM is dynamically generated to ensure uniqueness.
 *
 * @param[out] DataBuf Pointer to a buffer where the service parameters will be stored.
 * @return Always returns 0.
 */
uint8_t AliroSvcParamGetInitBuf(uint8_t **DataBuf)
{
  // uint8_t spsmDataBuf[] = { 0x00, 0x8F, 0x02, 0x01, 0x00 };
  // uint8_t spsmDataBuf[] = { 0x00, 0x8F, 0x02, 0x01, 0x00, 0x01, 0x00};
  // alirosvc_param.proposedSPSM;
  // alirosvc_param.supportedProtocolVersion;

  alirosvc_param.proposedSPSM[1] = get_dynamic_spsm(alirosvc_param.proposedSPSM[1]);
  PLATFORM_LOG(VERB_NORMAL, "Dinamic SPSM: 0x%.2X%.2X\r\n", alirosvc_param.proposedSPSM[0], alirosvc_param.proposedSPSM[1]);

  memcpy(&spsmDataBuf[0], alirosvc_param.proposedSPSM, 2);

  spsmDataBuf[2] = ((1+ALIRO_HAS_SEC_PROT_VER)*2);
  alirosvc_param.supportedProtocolVersion[0] = HIGH_BYTE(ALIRO_MAIN_PROT_VER);
  alirosvc_param.supportedProtocolVersion[1] = LOW_BYTE(ALIRO_MAIN_PROT_VER);
  if(ALIRO_HAS_SEC_PROT_VER)
  {
    alirosvc_param.supportedProtocolVersion[2] = HIGH_BYTE(ALIRO_SEC_PROT_VER);
    alirosvc_param.supportedProtocolVersion[3] = LOW_BYTE(ALIRO_SEC_PROT_VER);
  }
  memcpy(&spsmDataBuf[3], alirosvc_param.supportedProtocolVersion, ((1+ALIRO_HAS_SEC_PROT_VER)*2));
  
  spsmDataBuf[((1+ALIRO_HAS_SEC_PROT_VER)*2)+3] = 0x01;
  spsmDataBuf[((1+ALIRO_HAS_SEC_PROT_VER)*2)+4] = 0x00;
//  spsmDataBuf[5] = 0x01;
//  spsmDataBuf[6] = 0x00;
  *DataBuf = spsmDataBuf;
  return 0;
}

/**
 * @brief Retrieves the saved selected protocol version.
 *
 * This function returns the value of the saved selected protocol version,
 * which is stored in the variable `savedselectedProtocolVersion_16`.
 *
 * @return The saved selected protocol version as a 16-bit unsigned integer.
 */
uint16_t AliroGetSavedSelectedProtocolVersion()
{
  return savedselectedProtocolVersion_16;
}

/**
 * @brief Verifies if the selected protocol version is supported.
 *
 * This function checks if the provided `selectedProtocolVersion_16` matches
 * any of the supported protocol versions defined in `alirosvc_param.supportedProtocolVersion`.
 * If a match is found, it logs a success message and updates the saved selected protocol version.
 * If no match is found, it logs an error message indicating the unsupported version.
 *
 * @param selectedProtocolVersion_16 The protocol version to verify.
 * @return 0 if the protocol version is supported, 1 otherwise.
 */
uint8_t AliroSvcVerifyProtVer(uint16_t selectedProtocolVersion_16)
{
  uint8_t nRet = 1;//fail
  uint16_t supportedProtocolVersion_16 = (uint16_t)alirosvc_param.supportedProtocolVersion[1] | (uint16_t)alirosvc_param.supportedProtocolVersion[0]<<8;
  if (selectedProtocolVersion_16 == supportedProtocolVersion_16)
  {
    PLATFORM_LOG(VERB_NORMAL, "Protocol Version Supported (0x%.4X)\r\n", selectedProtocolVersion_16);
    nRet = 0;
  }
  else 
  {
    if(ALIRO_HAS_SEC_PROT_VER)
    {
      supportedProtocolVersion_16 = (uint16_t)alirosvc_param.supportedProtocolVersion[3] | (uint16_t)alirosvc_param.supportedProtocolVersion[2]<<8;
      if (selectedProtocolVersion_16 == supportedProtocolVersion_16)
      {
        PLATFORM_LOG(VERB_NORMAL, "Protocol Version Supported (0x%.4X)\r\n", selectedProtocolVersion_16);
        nRet = 0;
      }
      else
      {
        PLATFORM_LOG(VERB_NORMAL, "ERROR: Protocol Version Not Supported\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tProtocol Version Supported (0x%.4X) selected (0x%.4X)\r\n", supportedProtocolVersion_16, selectedProtocolVersion_16);
      }
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "ERROR: Protocol Version Not Supported\r\n");
      PLATFORM_LOG(VERB_NORMAL, "\tProtocol Version Supported (0x%.4X) selected (0x%.4X)\r\n", supportedProtocolVersion_16, selectedProtocolVersion_16);
    }
  }
  
  if(nRet == 0)
  {
    savedselectedProtocolVersion_16 = selectedProtocolVersion_16;
  }
  return nRet;
}

/**
 * @brief Validates the selected SPSM against the proposed SPSM.
 *
 * This function checks if the provided `selected` SPSM matches the proposed SPSM
 * stored in `alirosvc_param.proposedSPSM`. It logs the proposed and selected SPSM values,
 * and indicates whether the validation was successful or not.
 *
 * @param selected The SPSM value to validate.
 * @return 1 if the SPSM is valid (matches the proposed SPSM), 0 otherwise.
 */
uint8_t isValidSPSM(uint16_t selected)
{
  uint8_t validSPSM_res;
  uint16_t supported_16 = (uint16_t)alirosvc_param.proposedSPSM[1] | (uint16_t)alirosvc_param.proposedSPSM[0]<<8;

  PLATFORM_LOG(VERB_NORMAL, "\tSPSM proposed (0x%.2X%.2X) selected (0x%.4X)\n", alirosvc_param.proposedSPSM[0], alirosvc_param.proposedSPSM[1], selected);
  if (supported_16 == selected)
  {
    PLATFORM_LOG(VERB_NORMAL, "SPSM Verified (0x%.4X)\r\n", selected);
    validSPSM_res = 1;
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "SPSM did not match\r\n");
    validSPSM_res = 0;
  }

  // uint8_t selectedBytes[2] = {(selected & 0xFF00)>>8 , (selected & 0xFF)};
  // if (memcmp(supported, selectedBytes, 2))
  // {
  //   validSPSM_res = 0;
  // }
  // else
  // {
  //   validSPSM_res = 1;
  // }
  return validSPSM_res;
}

#else //ALIRO_BLE

uint16_t AliroGetSavedSelectedProtocolVersion()
{
  return savedselectedProtocolVersion_16;
}

#endif //ALIRO_BLE

/**
 * @brief Initializes the proprietary NFC functionality for the demo application.
 *
 * This function performs necessary initialization steps for the proprietary NFC
 * features by marking the NFC_PROP_BUF as unused and calling the INIT_NFC_PROP macro.
 *
 * @return ERR_NONE on successful initialization.
 */
ReturnCode demoPropNfcInitialize( void )
{
  UNUSED(NFC_PROP_BUF);
  INIT_NFC_PROP();
  return ERR_NONE;
}

/**
 * @brief Demonstrates detection of proprietary NFC technology.
 *
 * This function triggers the detection process for proprietary NFC technology
 * by calling DET_NFC_PROP(). It always returns ERR_TIMEOUT.
 *
 * @return ReturnCode Returns ERR_TIMEOUT after attempting detection.
 */
ReturnCode demoPropNfcTechnologyDetection( void )
{
  DET_NFC_PROP();
  return ERR_TIMEOUT;
}


/**
 * @brief Fills the NFC technologies to be discovered.
 *
 * This function sets the bits in the provided pointer to indicate which NFC technologies
 * should be searched for during the discovery process. The selection of technologies is
 * determined by compile-time feature flags.
 *
 * @param[out] techs2find Pointer to a variable where the discovered technologies bitmask will be stored.
 *
 * @note The actual technologies included depend on the enabled RFAL feature macros.
 */
void AliroFilltechs2Find(uint16_t *techs2find)
{
  uint16_t discParamtechs2Find = 0;
#if RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP
//  discParamtechs2Find |= RFAL_NFC_POLL_TECH_AP2P;
#endif /* RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP */
  
#if RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP && RFAL_FEATURE_LISTEN_MODE
//  discParamtechs2Find |= RFAL_NFC_LISTEN_TECH_AP2P;
#endif /* RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P && RFAL_FEATURE_NFC_DEP && RFAL_FEATURE_LISTEN_MODE */
  
#if RFAL_FEATURE_NFCA
  discParamtechs2Find          |= RFAL_NFC_POLL_TECH_A;
#endif /* RFAL_FEATURE_NFCA */
  
#if RFAL_FEATURE_ST25TB
//  discParamtechs2Find          |= RFAL_NFC_POLL_TECH_ST25TB;
#endif /* RFAL_FEATURE_ST25TB */
  
  LOAD_NFC_PROP();
  
  *techs2find = discParamtechs2Find;
}

/**
 * @brief Parses and processes the Reader Public Key (RDpubk) command from the input buffer.
 *
 * This function checks if the input buffer contains a valid RDpubk command, which should start with "04"
 * followed by a 64-byte public key in hexadecimal format (128 hex characters). If the format is correct,
 * the public key is converted from hex string to binary and saved to flash memory.
 *
 * @param cmdBuffer Pointer to the command buffer (null-terminated string).
 * @param processBuffer Pointer to the buffer containing the data to be processed.
 * @param size Size of the processBuffer in bytes.
 *
 * @note The function expects the processBuffer to start with "04" followed by the public key in hex format.
 *       If the format or size is incorrect, appropriate log messages are generated.
 */
void parseRDpubkCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer += strlen(cmdBuffer);
  size -= strlen(cmdBuffer);
  PLATFORM_LOG(VERB_NORMAL, "Processing input buffer\r\n");
  if(processBuffer[0]=='0' && processBuffer[1]=='4')
  {
    processBuffer += 2;
    size -= 2;
    if(size == 2*64)
    {
      uint8_t tempBuf[64] = {0};
      hexStrToHex((char*)processBuffer, 2*64, tempBuf);
      saveRDPubKToFlash(tempBuf, 64);
      PLATFORM_LOG(VERB_NORMAL, "Saving Reader Public key to flash\r\n");
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "Wrong size - expected 64 bytes in hex format, got %d bytes\r\n", size);
    }
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Missing 04 at the beginning\r\n");
  }
}

/**
 * @brief Parses and processes the RDprvk command from the input buffer.
 *
 * This function advances the processBuffer pointer past the command string,
 * checks if the remaining buffer size matches the expected length for a
 * 32-byte private key in hexadecimal format (64 characters), and if so,
 * converts the hex string to binary and saves it to flash memory.
 *
 * @param cmdBuffer Pointer to the command string.
 * @param processBuffer Pointer to the buffer containing the command and data.
 * @param size Size of the processBuffer in bytes.
 */
void parseRDprvkCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer += strlen(cmdBuffer);
  size -= strlen(cmdBuffer);
  PLATFORM_LOG(VERB_NORMAL, "Processing input buffer\r\n");
  if(size == 2*32)
  {
    uint8_t tempBuf[32] = {0};
    hexStrToHex((char*)processBuffer, 2*32, tempBuf);
    saveRDPrvKToFlash(tempBuf, 32);
    PLATFORM_LOG(VERB_NORMAL, "Saving Reader Private key to flash\r\n");
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Wrong size - expected 32 bytes in hex format, got %d bytes\r\n", size);
  }
}

/**
 * @brief Parses the RDiden command from the input buffer and saves the Reader identifier to flash.
 *
 * This function processes the input command buffer, extracts the Reader identifier in hexadecimal format,
 * converts it to binary, and saves it to flash memory if the size is correct.
 *
 * @param cmdBuffer Pointer to the command buffer containing the command string.
 * @param processBuffer Pointer to the buffer containing the data to be processed.
 * @param size Size of the data in the process buffer.
 *
 * The function expects the Reader identifier to be provided as a 32-byte value in hexadecimal format (64 characters).
 * If the size does not match the expected value, an error message is logged.
 */
void parseRDidenCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer += strlen(cmdBuffer);
  size -= strlen(cmdBuffer);
  PLATFORM_LOG(VERB_NORMAL, "Processing input buffer\r\n");
  if(size == 2*32)
  {
    uint8_t tempBuf[32] = {0};
    hexStrToHex((char*)processBuffer, 2*32, tempBuf);
    saveRDidenToFlash(tempBuf, 32);
    PLATFORM_LOG(VERB_NORMAL, "Saving Reader identifier to flash\r\n");
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Wrong size - expected 32 bytes in hex format, got %d bytes\r\n", size);
  }
}

/**
 * @brief Parses the RDgrrk command from the input buffer and saves the Group Resolving Key to flash.
 *
 * This function processes the input command buffer, extracts the Group Resolving Key in hexadecimal format,
 * converts it to binary, and saves it to flash memory if the size is correct.
 *
 * @param cmdBuffer Pointer to the command buffer containing the command string.
 * @param processBuffer Pointer to the buffer containing the data to be processed.
 * @param size Size of the data in the process buffer.
 *
 * The function expects the Group Resolving Key to be provided as a 16-byte value in hexadecimal format (32 characters).
 * If the size does not match the expected value, an error message is logged.
 */
void parseRDgrrkCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer += strlen(cmdBuffer);
  size -= strlen(cmdBuffer);
  PLATFORM_LOG(VERB_NORMAL, "Processing input buffer\r\n");
  if(size == 2*16)
  {
    uint8_t tempBuf[16] = {0};
    hexStrToHex((char*)processBuffer, 2*16, tempBuf);
    saveRDgrrkToFlash(tempBuf, 16);
    PLATFORM_LOG(VERB_NORMAL, "Saving group resolving key to flash\r\n");
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Wrong size - expected 16 bytes in hex format, got %d bytes\r\n", size);
  }
}

/**
 * @brief Parses the Credential Issuer Public Key (CRispk) command from the input buffer and saves it to flash.
 *
 * This function processes the input buffer to extract a Credential Issuer public key in hexadecimal format,
 * converts it to binary, and saves it to flash memory if the size is correct.
 *
 * @param cmdBuffer Pointer to the command buffer containing the command string.
 * @param processBuffer Pointer to the buffer containing the data to be processed.
 * @param size Size of the data in the process buffer.
 *
 * The function expects the Credential Issuer public key to be provided as a 64-byte value in hexadecimal format (128 characters).
 * If the size does not match the expected value, an error message is logged.
 */
void parseCredIsPkCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer += strlen(cmdBuffer);
  size -= strlen(cmdBuffer);
  PLATFORM_LOG(VERB_NORMAL, "Processing input buffer\r\n");
  if(processBuffer[0]=='0' && processBuffer[1]=='4')
  {
    processBuffer += 2;
    size -= 2;
    if(size == 2*64)
    {
      uint8_t tempBuf[64] = {0};
      hexStrToHex((char*)processBuffer, 2*64, tempBuf);
      saveCredIsPkToFlash(tempBuf, 64);
      PLATFORM_LOG(VERB_NORMAL, "Saving Credential Issuer Public key to flash\r\n");
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "Wrong size - expected 64 bytes in hex format, got %d bytes\r\n", size);
    }
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Missing 04 at the beginning\r\n");
  }
}

/**
 * @brief Parses and logs an echo command.
 *
 * This function takes a command buffer and a process buffer, appends a null terminator
 * at the specified size, and logs the received echo command along with the current time and date.
 *
 * @param cmdBuffer Pointer to the command buffer containing the command string.
 * @param processBuffer Pointer to the buffer to be processed and logged.
 * @param size The size of the process buffer to be considered for logging.
 */
void parseEchoCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer[size] = 0;
  PLATFORM_LOG(VERB_NORMAL, "Echo command %s received at ", processBuffer);
  ShowTimeDate();
  PLATFORM_LOG(VERB_NORMAL, "\r\n");
}

/**
 * @brief Parses and processes a DEVpbk command from the input buffer.
 *
 * This function advances the process buffer past the command string, checks for the
 * expected '04' prefix, and verifies the size of the remaining data. If the data is
 * valid, it converts the hex string to a binary public key and saves it to flash.
 * Logs are generated for each step and error condition.
 *
 * @param cmdBuffer Pointer to the command string buffer.
 * @param processBuffer Pointer to the buffer containing the data to process.
 * @param size Size of the process buffer in bytes.
 */
void parseDEVpbkCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer += strlen(cmdBuffer);
  size -= strlen(cmdBuffer);
  PLATFORM_LOG(VERB_NORMAL, "Processing input buffer\r\n");
  if(processBuffer[0]=='0' && processBuffer[1]=='4')
  {
    processBuffer += 2;
    size -= 2;
    if(size == 2*64)
    {
      uint8_t tempBuf[64] = {0};
      hexStrToHex((char*)processBuffer, 2*64, tempBuf);
      saveAccessCredentialPuBK(tempBuf, 64, 0xff, (uint8_t*)"NewKey");
      PLATFORM_LOG(VERB_NORMAL, "Saving Access credential public key to flash\r\n");
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "Wrong size - expected 64 bytes in hex format, got %d bytes\r\n", size);
    }
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Missing 04 at the beginning\r\n");
  }
}

/**
 * @brief
 *
 * @param cmdBuffer Pointer to the command string buffer.
 * @param processBuffer Pointer to the buffer containing the data to process.
 * @param size Size of the process buffer in bytes.
 */
void parseAccElemCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  processBuffer += strlen(cmdBuffer);
  size -= strlen(cmdBuffer);
  PLATFORM_LOG(VERB_NORMAL, "Processing input buffer\r\n");
  
  processBuffer[size] = 0;
  tlw_SetAccessElementString(processBuffer);
  
  PLATFORM_LOG(VERB_NORMAL, "Access element set to: %s\r\n", processBuffer);
}

#if USE_RTC

/**
 * @brief Parses a date command from the input buffer and sets the RTC date.
 *
 * This function extracts a date in the format "DD/MM/YYYY" from the processBuffer,
 * validates the date values, and sets the RTC date accordingly. It expects the
 * processBuffer to point to the date string after the command prefix.
 *
 * @param[in]  cmdBuffer      Pointer to the command buffer containing the command prefix.
 * @param[in]  processBuffer  Pointer to the buffer containing the date string to parse.
 * @param[in]  size           Size of the processBuffer.
 *
 * @note The function logs the result of the operation using PLATFORM_LOG.
 *       The year is expected in four digits and will be converted to RTC format (offset from 2000).
 *       The date is validated for correct format and valid calendar values.
 *
 * @retval None
 */
void parseDateCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  RTC_DateTypeDef tempDate = {0};
  processBuffer += strlen(cmdBuffer);
  int day, month, year;
  
  if (sscanf((char const*)processBuffer, " %d/%d/%d", &day, &month, &year) == 3)
  {
    if(year >= 2000)
      year -= 2000;
    
    if (month >= 1 && month <= 12 && day >= 1 && year >= 0 && year <= 99 && day <= DaysInMonth((uint16_t)year, (uint8_t)month))
    {
      tempDate.Date = day;
      tempDate.Month = month;
      tempDate.Year = year;
      if (HAL_RTC_SetDate(&ALIRO_HRTC, &tempDate, RTC_FORMAT_BIN) == HAL_OK)
      {
        PLATFORM_LOG(VERB_NORMAL, "Date set to %02d/%02d/%04d\r\n", tempDate.Date, tempDate.Month, tempDate.Year + 2000);
      }
      else
      {
        PLATFORM_LOG(VERB_NORMAL, "Failed to set date\r\n");
      }
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "Invalid date values\r\n");
    }
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Invalid date format. Use: date: DD/MM/YYYY\r\n");
  }
}

/**
 * @brief Parses a time command from the input buffer and sets the RTC time.
 *
 * This function extracts a time in the format "HH:MM[:SS]" from the processBuffer,
 * validates the time values, and sets the RTC time accordingly. It expects the
 * processBuffer to point to the time string after the command prefix.
 *
 * @param[in]  cmdBuffer      Pointer to the command buffer containing the command prefix.
 * @param[in]  processBuffer  Pointer to the buffer containing the time string to parse.
 * @param[in]  size           Size of the processBuffer.
 *
 * @note The function logs the result of the operation using PLATFORM_LOG.
 *       The seconds component is optional and defaults to 0 if not provided.
 *       The time is validated for correct format and valid clock values.
 *
 * @retval None
 */
void parseTimeCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  RTC_TimeTypeDef tempTime = {0};
  processBuffer += strlen(cmdBuffer);
  int hour, minute, second = 0;
  int parsed = sscanf((char const*)processBuffer, " %d:%d:%d", &hour, &minute, &second);
  if (parsed < 2)
  {
    PLATFORM_LOG(VERB_NORMAL, "Invalid time format. Use: time: HH:MM[:SS]\r\n");
    return;
  }
  if (hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 && second >= 0 && second <= 59)
  {
    tempTime.Hours = hour;
    tempTime.Minutes = minute;
    tempTime.Seconds = second;
    tempTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    tempTime.StoreOperation = RTC_STOREOPERATION_RESET;
    if (HAL_RTC_SetTime(&ALIRO_HRTC, &tempTime, RTC_FORMAT_BIN) == HAL_OK)
    {
      PLATFORM_LOG(VERB_NORMAL, "Time set to %02d:%02d:%02d\r\n", hour, minute, second);
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "Failed to set time\r\n");
    }
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Invalid time values\r\n");
  }
}

/**
 * @brief Parses a date and time command from the input buffer and sets the RTC date and time.
 *
 * This function extracts a date and time in the format "YYYY/MM/DD HH:MM:SS" from the processBuffer,
 * validates the date and time values, and sets the RTC date and time accordingly. It expects the
 * processBuffer to point to the date and time string after the command prefix.
 *
 * @param[in]  cmdBuffer      Pointer to the command buffer containing the command prefix.
 * @param[in]  processBuffer  Pointer to the buffer containing the date and time string to parse.
 * @param[in]  size           Size of the processBuffer.
 *
 * @note The function logs the result of the operation using PLATFORM_LOG.
 *       The year is expected in four digits and will be converted to RTC format (offset from 2000).
 *       Both date and time are validated for correct format and valid calendar/clock values.
 *
 * @retval None
 */
void parseDateTimeCommand(char *cmdBuffer, uint8_t *processBuffer, uint32_t size)
{
  //settime-%Y/%m/%d %H:%M:%S
  
  RTC_TimeTypeDef tempTime = {0};
  RTC_DateTypeDef tempDate = {0};
  int hour, minute, second = 0;
  int day, month, year;
  
  processBuffer += strlen(cmdBuffer);
  
  if (sscanf((char const*)processBuffer, " %d/%d/%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6)
  {
    //time check
    if (hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 && second >= 0 && second <= 59)
    {
      tempTime.Hours = hour;
      tempTime.Minutes = minute;
      tempTime.Seconds = second;
      tempTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
      tempTime.StoreOperation = RTC_STOREOPERATION_RESET;
      if (HAL_RTC_SetTime(&ALIRO_HRTC, &tempTime, RTC_FORMAT_BIN) == HAL_OK)
      {
        PLATFORM_LOG(VERB_NORMAL, "Time set to %02d:%02d:%02d\r\n", hour, minute, second);
      }
      else
      {
        PLATFORM_LOG(VERB_NORMAL, "Failed to set time\r\n");
      }
    }
    
    //date check
    if(year >= 2000)
    {
      year -= 2000;
    }
    
    if (month >= 1 && month <= 12 && year >= 0 && year <= 99 && day >= 1 && day <= DaysInMonth((uint16_t)year, (uint8_t)month))
    {
      tempDate.Date = day;
      tempDate.Month = month;
      tempDate.Year = year;
      if (HAL_RTC_SetDate(&ALIRO_HRTC, &tempDate, RTC_FORMAT_BIN) == HAL_OK)
      {
        PLATFORM_LOG(VERB_NORMAL, "Date set to %02d/%02d/%04d\r\n", tempDate.Date, tempDate.Month, tempDate.Year + 2000);
        SetRtcSettingStatus(ALIRO_RTC_SET_STATUS_GOOD);
      }
      else
      {
        PLATFORM_LOG(VERB_NORMAL, "Failed to set date\r\n");
      }
    }
    else
    {
      PLATFORM_LOG(VERB_NORMAL, "Invalid date values\r\n");
    }
  }
  else
  {
    PLATFORM_LOG(VERB_NORMAL, "Invalid date format. Use: date: DD/MM/YYYY\r\n");
  }
}
#endif //USE_RTC

/**
 * @brief Resets the board after a delay.
 *
 * This function introduces a delay of 2000 milliseconds (2 seconds)
 * before triggering a system reset using HAL_NVIC_SystemReset().
 */
void board_reset()
{
  HAL_Delay(2000);
  HAL_NVIC_SystemReset();
}

/**
 * @brief Processes UART input commands.
 *
 * This function reads a single character command from the UART input buffer
 * and performs actions based on the received command. It supports setting
 * verbosity levels and displaying system status information.
 */
void ProcessUartInput()
{
  uint8_t CommandChars[128 + 1] = {0};
  uint16_t size = UsartGetChar(CommandChars);
  if (size == 1)
  {
    ShowTimeDate();
    uint8_t cmdChar = CommandChars[0];
    PLATFORM_LOG(VERB_VERBOSE, "Received char %c\r\n", cmdChar);
    switch(cmdChar)
    {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
        PLATFORM_LOG(VERB_NORMAL, "Set verbosity level to %c\r\n", cmdChar);
        SetVerbosityLevel(cmdChar);
        break;
      case '?':
    
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_CYAN);
        PLATFORM_LOG(VERB_NORMAL, "\r\n\r\n********************** STATUS ***********************\r\n");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
    
        // PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        // PLATFORM_LOG(VERB_NORMAL, "\tStatus: NFC system running\r\n");
        // PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
    
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tKey Slot: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.useKeyslot == 1)
        {
          PLATFORM_LOG(VERB_NORMAL, "active\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "not active\r\n");
        }
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tTransaction: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.cmdParams & CMD_PARAMS_EXP_FAST_MASK)
        {
          PLATFORM_LOG(VERB_NORMAL, "FAST\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "STANDARD\r\n");
        }
    
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tExchange: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if ((context_nfc.init.useExchange > 0) && (context_nfc.init.useExchange < 0x80))  
        {
          PLATFORM_LOG(VERB_NORMAL, "active %d\r\n", context_nfc.init.useExchange);
        }
        else if (context_nfc.init.useExchange >= 0x80)
        {
          PLATFORM_LOG(VERB_NORMAL, "auto - based on signaling bitmap\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "not active\r\n");
        }
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tStepup: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.useStepUp == 1)
        {
          PLATFORM_LOG(VERB_NORMAL, "auto - based on signaling bitmap\r\n");
        }
        else if (context_nfc.init.useStepUp == 2)
        {
          PLATFORM_LOG(VERB_NORMAL, "always active\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "disabled\r\n");
        }
    
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tLoad Cert: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.useLoadCert == 0)
        {
          PLATFORM_LOG(VERB_NORMAL, "certificate not available\r\n");
        }
        else if (context_nfc.init.useLoadCert == 1)
        {
          PLATFORM_LOG(VERB_NORMAL, "active in LOAD CERT\r\n");
        }
        else if (context_nfc.init.useLoadCert == 2)
        {
          PLATFORM_LOG(VERB_NORMAL, " active in LOAD CERT Extended length\r\n");
        }
        else if (context_nfc.init.useLoadCert == 3)
        {
          PLATFORM_LOG(VERB_NORMAL, " active in AUTH1 Extended length\r\n");
        }
        else if (context_nfc.init.useLoadCert == 4)
        {
          PLATFORM_LOG(VERB_NORMAL, " active in AUTH1 Auto\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
          PLATFORM_LOG(VERB_NORMAL, "not valid\r\n");
          PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        }
    
#if 0
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tReserved3: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.RESERVED3 == 1)
        {
          PLATFORM_LOG(VERB_NORMAL, "active\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "not active\r\n");
        }
#endif //0    
        
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tNot Verify Key: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.NotVerifyKey == 1)
        {
          PLATFORM_LOG(VERB_NORMAL, "active\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "not active\r\n");
        }
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tAuthentication Policy: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.authPolicy == 0x01)
        {
          PLATFORM_LOG(VERB_NORMAL, "User device setting\r\n");
        }
        else if(context_nfc.init.authPolicy == 0x02)
        {
          PLATFORM_LOG(VERB_NORMAL, "User device setting - secure action\r\n");
        }
        else if(context_nfc.init.authPolicy == 0x03)
        {
          PLATFORM_LOG(VERB_NORMAL, "Force user authentication\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "NOT VALID\r\n");
        }
    
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "\tVendor Specific Extension: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        if (context_nfc.init.RESERVED2 == 1)
        {
          PLATFORM_LOG(VERB_NORMAL, "active\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "not active\r\n");
        }
    
        PLATFORM_LOG(VERB_NORMAL, "\tProtocol version: 0x%.4X\r\n", ALIRO_MAIN_PROT_VER);
        if(ALIRO_HAS_SEC_PROT_VER)
        {
          PLATFORM_LOG(VERB_NORMAL, "\tProtocol version: 0x%.4X\r\n", ALIRO_SEC_PROT_VER);
        }
        PLATFORM_LOG(VERB_NORMAL, "\tNFC FLOW: ENABLED\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tBLE FLOW: %s\r\n", (BLE_ADV_DISABLE?"DISABLED":"ENABLED"));
        if(!BLE_ADV_DISABLE)
        {
          PLATFORM_LOG(VERB_NORMAL, "\tBLE-RKE FLOW: %s\r\n", (BLE_ALIRO_FLOWS_SUPPORTED&BLE_RKE_ALIRO_FLOW_SUPPORTED?"ENABLED":"DISABLED"));
          PLATFORM_LOG(VERB_NORMAL, "\tBLE-UWB FLOW: %s\r\n", (BLE_ALIRO_FLOWS_SUPPORTED&BLE_UWB_ALIRO_FLOW_SUPPORTED?"ENABLED":"DISABLED"));
        }
        PLATFORM_LOG(VERB_NORMAL, "\tAccess element is set to: %s\r\n", tlw_GetAccessElementString());
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_CYAN);
        PLATFORM_LOG(VERB_NORMAL, "*****************************************************\r\n");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "PROVISIONING_JSON_START_ADDRESS: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        PLATFORM_LOG(VERB_NORMAL, "0x%08X\r\n", (PROVISIONING_JSON_START_ADDRESS));
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "PROVISIONING_START_ADDRESS: ");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        PLATFORM_LOG(VERB_NORMAL, "0x%08X\r\n", (PROVISIONING_START_ADDRESS));
        
        PLATFORM_LOG(VERB_NORMAL, "\tverbosity level set to: %d", nVerbosityLevel);
        
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_CYAN);
        PLATFORM_LOG(VERB_NORMAL, "\r\n********************* COMMANDS **********************\r\n");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
    
        PLATFORM_LOG(VERB_NORMAL, "\t0-6 Set verbosity level\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t? - System status and commands list\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\ta - Set Authentication Policy\r\n");  
        PLATFORM_LOG(VERB_NORMAL, "\tb - Toggle Vendor Specific Extension\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tf - Toggle Fast transaction\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tk - Toggle Key Slot activation\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\te - Toggle EXCHANGE\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tl - Set LOAD_CERT\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tu - Toggle STEPUP\r\n");
#if 0
        PLATFORM_LOG(VERB_NORMAL, "\tt - Toggle RESERVED3\r\n");
#endif //0
        PLATFORM_LOG(VERB_NORMAL, "\tv - Toggle Not Verify Key\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\td - Ranging Suspend/Resume Action (not active work in progress)\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tp - Provisioning status\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\ts - Save default provisioning info\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tc - Convert provisioning info from json\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tw - NFC wakeup mode (LPCD)\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tn - Switch NFC only / NFC+BLE / NFC+BLE+UWB\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tz - Save last access credential PuBK\r\n");
        
        PLATFORM_LOG(VERB_NORMAL, "\t'date:' - Set system date\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'time:' - Set system time\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'settime-' - Set system date and time\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'setRDiden-' - Set reader group identifier and sub identifier\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'setRDprvk-' - Set reader private key\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'setRDpubk-' - Set reader public key\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'setRDgrrk-' - Set reader group resolving key\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'setCrIspk-' - Set Credential issuer public key\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'setDEVpbk-' - Set reader access credential public key\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t'setAccElm-' - Set access element name for stepup (session)\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\tr - system restart\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t! - system pause\r\n");   

    #if TEST_HARNESS_SIMULATION
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "x   Test AccDoc Process\r\n");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
    #endif
    
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_CYAN);
        PLATFORM_LOG(VERB_NORMAL, "*****************************************************\r\n");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);

        break;
    
    #if TEST_HARNESS_SIMULATION
      case 'X':
      case 'x':
        {
          uint8_t stepup_process(ACWG_SessionContext_t *acwgctx);
          stepup_process(&context_nfc);
        }
        break;
    #endif
    
      case 'P':
      case 'p':
        // showProvisioningStatus();
        printDeviceJson();  
        break;
      case '!':
        {
          PLATFORM_LOG(VERB_NORMAL, "System pause toggle\r\n");
          PLATFORM_LOG(VERB_NORMAL, "Press any key to continue\r\n");
          if(isAliroRunning)
          {
            isAliroRunning = 0;
            PLATFORM_LOG(VERB_NORMAL, "System paused. Serial commands are accepted. Press ! again to restart\r\n");
          }
          else
          {
            isAliroRunning = 1;
            PLATFORM_LOG(VERB_NORMAL, "System running. Press ! again to pause\r\n");
          }
        }
        break;
    
      case 'A':
      case 'a':
        PLATFORM_LOG(VERB_NORMAL, "\r\nAuthentication Policy options:\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t1: User device setting\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t2: User device setting - secure action\r\n");  
        PLATFORM_LOG(VERB_NORMAL, "\t3: Force user authentication\r\n");  
    
        while (UsartGetCharBlocking(CommandChars) != 1)
        {
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
          PLATFORM_LOG(VERB_NORMAL, "Plese insert only one character\r\n");
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
        }
        cmdChar = CommandChars[0];

        switch (cmdChar)
        {
          case '1':
            PLATFORM_LOG(VERB_NORMAL, "Set User device setting\r\n");
            context_nfc.init.authPolicy = 0x01;
            break;
            
          case '2':
            PLATFORM_LOG(VERB_NORMAL, "Set User device setting - secure action\r\n");
            context_nfc.init.authPolicy = 0x02;
            break;
    
          case '3':
            PLATFORM_LOG(VERB_NORMAL, "Set Force user authentication\r\n");
            context_nfc.init.authPolicy = 0x03;
            break;
    
          default:
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_RED);
            PLATFORM_LOG(VERB_NORMAL, "No choice selected\r\n");
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
            // saveProvisioningToFlash(1);
            break;
        }
        break;
    
      case 'B':
      case 'b':
        if (context_nfc.init.RESERVED2 == 1)
        {
          context_nfc.init.RESERVED2 = 0;
          PLATFORM_LOG(VERB_NORMAL, "Vendor Specific Extension Not Active\r\n");
        }
        else
        {
          context_nfc.init.RESERVED2 = 1;
          PLATFORM_LOG(VERB_NORMAL, "Vendor Specific Extension Active\r\n");
        }
        break;
    
      case 'R':
      case 'r':
        PLATFORM_LOG(VERB_NORMAL, "System restart\r\n");
        AliroPort_Delay(1000);
        AliroPort_NVIC_SystemReset();
        break;
      case 'S':
      case 's':
          PLATFORM_LOG(VERB_NORMAL, "\r\nSave provisioning options:\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t1: Save provisioning WITHOUT certificate\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t2: Save provisioning WITH certificate\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t3: Save provisioning WITH LONG certificate\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t4: Save provisioning WITH SHORT certificate\r\n");
          
        while (UsartGetCharBlocking(CommandChars) != 1)
        {
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
          PLATFORM_LOG(VERB_NORMAL, "Plese insert only one character\r\n");
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
        }
        cmdChar = CommandChars[0];

        switch (cmdChar)
        {
          case '1':
            PLATFORM_LOG(VERB_NORMAL, "Saving without certificate...\r\n");
            if (context_nfc.init.useLoadCert != 0x00)
            {
              context_nfc.init.useLoadCert = 0x00;
            }
            saveProvisioningToFlash(1);
            break;
    
          case '2':
            PLATFORM_LOG(VERB_NORMAL, "Saving with certificate...\r\n");
            if (context_nfc.init.useLoadCert == 0x00)
            {
              context_nfc.init.useLoadCert = 0x01;
            }
            
            saveProvisioningToFlash(2);
            break;
    
          case '3':
            PLATFORM_LOG(VERB_NORMAL, "Saving with long certificate...\r\n");
            if (context_nfc.init.useLoadCert == 0x00)
            {
              context_nfc.init.useLoadCert = 0x01;
            }
            saveProvisioningToFlash(3);
            break;
    
          case '4':
            PLATFORM_LOG(VERB_NORMAL, "Saving with short certificate...\r\n");
            if (context_nfc.init.useLoadCert == 0x00)
            {
              context_nfc.init.useLoadCert = 0x01;
            }
            saveProvisioningToFlash(4);
            break;
            
    
          case 'M':
          case 'm':
            PLATFORM_LOG(VERB_NORMAL, "RFU\r\n");
            break;
    
          default:
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
            PLATFORM_LOG(VERB_NORMAL, "No choice selected\r\n");
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
            break;
        }
        break;
      case 'C':
      case 'c':
        convertDefaultProvisioningFromJson();
        break;
      case 'K':
      case 'k':
        if (context_nfc.init.useKeyslot == 1)
        {
          context_nfc.init.useKeyslot = 0;
          PLATFORM_LOG(VERB_NORMAL, "Key Slot not active\r\n");
        }
        else
        {
          context_nfc.init.useKeyslot = 1;
          PLATFORM_LOG(VERB_NORMAL, "Key Slot active\r\n");
        }
        break;
      
      case 'E':
      case 'e':
        if( context_nfc.init.useExchange )
        {
          context_nfc.init.useExchange = 0;
          PLATFORM_LOG(VERB_NORMAL, "EXCHANGE not active\r\n");
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "\r\nExchange list:\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t1: Set exchange frame READ\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t2: Set exchange frame WRITE\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t3: Set exchange frame SET\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t4: Set exchange frame ALL\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\ta: Set exchange Auto\r\n");

          while (UsartGetCharBlocking(CommandChars) != 1)
          {
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
            PLATFORM_LOG(VERB_NORMAL, "Plese insert only one character\r\n");
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
          }
          cmdChar = CommandChars[0];
          
          switch (cmdChar)
          {
          case '1':
            {
              context_nfc.init.useExchange = 1;
              PLATFORM_LOG(VERB_NORMAL, "Set exchange frame to 1\r\n");
              PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_READ\r\n");
            }
            break;
          case '2':
            {
              context_nfc.init.useExchange = 2;
              PLATFORM_LOG(VERB_NORMAL, "Set exchange frame to 2\r\n");
              PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_WRITE\r\n");
            }
            break;
          case '3':
            {
              context_nfc.init.useExchange = 3;
              PLATFORM_LOG(VERB_NORMAL, "Set exchange frame to 3\r\n");
              PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_SET\r\n");
            }
            break;
          case '4':
            {
              context_nfc.init.useExchange = 4;
              PLATFORM_LOG(VERB_NORMAL, "Set exchange frame to 4\r\n");
              PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_ALL\r\n");
            }
            break;
          case 'A':
          case 'a':
            {
              context_nfc.init.useExchange = 0x80;
              PLATFORM_LOG(VERB_NORMAL, "Exchange set to auto - based on signaling bitmap\r\n");
            }
            break;
          default:
            {
              context_nfc.init.useExchange = 0x80;
              PLATFORM_LOG(VERB_NORMAL, "Exchange set to auto - based on signaling bitmap\r\n");
            }
            break;
          }
          PLATFORM_LOG(VERB_NORMAL, "EXCHANGE active:(%d)\r\n", context_nfc.init.useExchange);
        }
        break;  
      
      case 'L':
      case 'l':
        if(getProvisioningFromFlash()->certificate_len > 0)
        {
          PLATFORM_LOG(VERB_NORMAL, "\r\nSet Load CERT mode:\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t1: in LOAD CERT\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t2: In LOAD CERT Extended length\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t3: In AUTH1 Extended length\r\n");
          PLATFORM_LOG(VERB_NORMAL, "\t4: In AUTH1\r\n");  
          PLATFORM_LOG(VERB_NORMAL, "\t5: In AUTH1 Auto (to do)\r\n");
    
          while (UsartGetCharBlocking(CommandChars) != 1)
          {
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
            PLATFORM_LOG(VERB_NORMAL, "Plese insert only one character\r\n");
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
          }
          cmdChar = CommandChars[0];
          
          switch (cmdChar)
          {
            case '1':
              PLATFORM_LOG(VERB_NORMAL, "In LOAD CERT Selected\r\n");
              context_nfc.init.useLoadCert = 0x01;
              break;

            case '2':
              PLATFORM_LOG(VERB_NORMAL, "In LOAD CERT Extended length Selected\r\n");
              context_nfc.init.useLoadCert = 0x02;
              break;
              
            case '3':
              PLATFORM_LOG(VERB_NORMAL, "In AUTH1 Extended length Selected\r\n");
              context_nfc.init.useLoadCert = 0x03;
              break;
    
            case '4':
              PLATFORM_LOG(VERB_NORMAL, "In AUTH1 Selected (chaining)\r\n");
              context_nfc.init.useLoadCert = 0x04;
              break;
    
            case '5':
              PLATFORM_LOG(VERB_NORMAL, "In AUTH1 Auto (to do) Selected\r\n");
              // context_nfc.init.useLoadCert = 0x05;
    
    
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_RED);
              PLATFORM_LOG(VERB_NORMAL, "This choice is work in progress\r\n");
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
              PLATFORM_LOG(VERB_NORMAL, "In LOAD CERT by default\r\n");
              context_nfc.init.useLoadCert = 0x01;
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
    
              break;
    
            default:
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_RED);
              PLATFORM_LOG(VERB_NORMAL, "No choice selected\r\n");
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
              PLATFORM_LOG(VERB_NORMAL, "In LOAD CERT by default\r\n");
              context_nfc.init.useLoadCert = 0x01;
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
              // saveProvisioningToFlash(1);
              break;
          }
    
        }
        else
        {
          context_nfc.init.useLoadCert = 0;
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
          PLATFORM_LOG(VERB_NORMAL, "Certificate not available\r\n");
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
        }
        break;  
    
      case 'T':
      case 't':
        if (context_nfc.init.RESERVED3 == 1)
        {
          context_nfc.init.RESERVED3 = 0;
          PLATFORM_LOG(VERB_NORMAL, "RESERVED3 not active\r\n");
        }
        else
        {
          context_nfc.init.RESERVED3 = 1;
          PLATFORM_LOG(VERB_NORMAL, "RESERVED3 active\r\n");
        }
        break;
        
#if ALIRO_BLE
    case 'N':
    case 'n':
      {
        if (ble_inhibit == 1)
        {
          PLATFORM_LOG(VERB_NORMAL, "BLE is disabled\r\n ENABLING BLE...");
          ble_inhibit = 0;
          Adv_Request_cb(APP_BLE_ADV_FAST);
        }
        else
        {
          PLATFORM_LOG(VERB_NORMAL, "BLE is enabled\r\n DISABLING BLE...");
          ble_inhibit = 1;
          Adv_Stop_cb(APP_BLE_IDLE);
          
        }
        
//        if (context_nfc.init.NFC_BLE_UWB == 0)
//        {
//          //NFC + BLE RKE
//          context_nfc.init.NFC_BLE_UWB = 1;
//          PLATFORM_LOG(VERB_NORMAL, "NFC + BLE RKE flow\r\n");
//        }
//        else if (context_nfc.init.NFC_BLE_UWB == 1)
//        {
//          //NFC + BLE UWB
//          context_nfc.init.NFC_BLE_UWB = 2;
//          PLATFORM_LOG(VERB_NORMAL, "NFC + BLE UWB flow\r\n");
//        }
//        else if (context_nfc.init.NFC_BLE_UWB == 2)
//        {
//          //NFC + BLE RKE & UWB
//          context_nfc.init.NFC_BLE_UWB = 3;
//          PLATFORM_LOG(VERB_NORMAL, "NFC + BLE RKE & UWB flow\r\n");
//        }
//        else
//        {
//          //NFC only
//          context_nfc.init.NFC_BLE_UWB = 0;
//          PLATFORM_LOG(VERB_NORMAL, "NFC only flow\r\n");
//        }
      }
        break;
#endif //ALIRO_BLE
    case 'U':
    case 'u':
        if (context_nfc.init.useStepUp == 2)
        {
          context_nfc.init.useStepUp = 1;
          PLATFORM_LOG(VERB_NORMAL, "STEPUP auto - based on signaling bitmap\r\n");
        }
        else if(context_nfc.init.useStepUp == 1)
        {
          context_nfc.init.useStepUp = 0;
          PLATFORM_LOG(VERB_NORMAL, "STEPUP disabled\r\n");
        }
        else
        {
          context_nfc.init.useStepUp = 2;
          PLATFORM_LOG(VERB_NORMAL, "STEPUP always active\r\n");
        }
        break;  
    
      case 'V':
      case 'v':
        if (context_nfc.init.NotVerifyKey == 1)
        {
          context_nfc.init.NotVerifyKey = 0;
          PLATFORM_LOG(VERB_NORMAL, "NOT VERIFY KEY not active\r\n");
        }
        else
        {
          context_nfc.init.NotVerifyKey = 1;
          PLATFORM_LOG(VERB_NORMAL, "NOT VERIFY KEY active\r\n");
        }
        break; 
    
      case 'Z':
      case 'z':
        PrintBufferHexAlways("Last Access Credential PuBK: ", tlw_GetLastEndpointKey(), 64);
        PLATFORM_LOG(VERB_NORMAL, "\r\n");
        
        if(is_zeros(tlw_GetLastEndpointKey(), 64))
        {
          PLATFORM_LOG(VERB_NORMAL, "\r\nLast Access Credential PuBK is empty\r\n");
        }
        else if ( ProvisioningCheckEndpointPubK(tlw_GetLastEndpointKey()) == 0 )
        {
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_GREEN);
          PLATFORM_LOG(VERB_NORMAL, "\r\nLast Access Credential PuBK is already saved\r\n");
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
        }
        else
        {
          do 
          {
            PLATFORM_LOG(VERB_NORMAL, "Do you want save last Access Credential PuBK? (y-yes/n-no/s-show)\r\n");
            while (UsartGetCharBlocking(CommandChars) != 1)
            {
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
              PLATFORM_LOG(VERB_NORMAL, "Plese insert only 'y' or 'n'\r\n");
              PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
            }
          }
          while((CommandChars[0] != 'y') && (CommandChars[0] != 'n') && (CommandChars[0] != 's'));
          
          
          if (CommandChars[0] == 'y')
          {
            saveAccessCredentialPuBK(tlw_GetLastEndpointKey(), 64, 0xff, (uint8_t*)"LastKey");
          }
          else if (CommandChars[0] == 's')
          {
            PrintBufferHexAlways("temp_last_pubk: ", tlw_GetLastEndpointKey(), 64);
          }
          else
          {
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
            PLATFORM_LOG(VERB_NORMAL, "Last Access Credential PuBK NOT saved\r\n");
            PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
          }
        }
        break;
      case 'W':
      case 'w':
            PLATFORM_LOG(VERB_NORMAL, "Reserved for NFC wakeup (coming soon)\r\n");
        break;
      case 'D':
      case 'd':
        PLATFORM_LOG(VERB_NORMAL, "\r\nRanging Suspend/Resume Actions list:\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t1: Suspend Request\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t2: Force Suspend\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t3: Wait Suspend\r\n");
        PLATFORM_LOG(VERB_NORMAL, "\t4: Resume Request\r\n");

        while (UsartGetCharBlocking(CommandChars) != 1)
        {
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_YELLOW);
          PLATFORM_LOG(VERB_NORMAL, "Please insert only one character\r\n");
          PLATFORM_LOG(VERB_ERROR, TEXT_COLOR_DEFAULT);
        }
        cmdChar = CommandChars[0];

        switch (cmdChar)
        {
        case '1':
          {
            // context_nfc.init.useExchange = 1;
            PLATFORM_LOG(VERB_NORMAL, "Suspend request\r\n");
            // PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_READ\r\n");
          }
          break;
        case '2':
          {
            // context_nfc.init.useExchange = 2;
            PLATFORM_LOG(VERB_NORMAL, "Force Suspend\r\n");
            // PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_WRITE\r\n");
          }
          break;
        case '3':
          {
            // context_nfc.init.useExchange = 2;
            PLATFORM_LOG(VERB_NORMAL, "Wait Suspend\r\n");
            // PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_WRITE\r\n");
          }
          break;
        case '4':
          {
            // context_nfc.init.useExchange = 3;
            PLATFORM_LOG(VERB_NORMAL, "Resume Request\r\n");
            // PLATFORM_LOG(VERB_NORMAL, "Exchange set to EXCHANGE_STOP_SET\r\n");
          }
          break;
        default:
          {
            PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
            PLATFORM_LOG(VERB_NORMAL, "No choice selected\r\n");
            PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
          }
          break;
        }
        // PLATFORM_LOG(VERB_NORMAL, "EXCHANGE active:(%d)\r\n", context_nfc.init.useExchange);
        break;  
      case 'F':
      case 'f':
        if (context_nfc.init.cmdParams & CMD_PARAMS_EXP_FAST_MASK)
        {
          context_nfc.init.cmdParams &= ~(CMD_PARAMS_EXP_FAST_MASK);
          PLATFORM_LOG(VERB_NORMAL, "Transaction: STANDARD\r\n");
        }
        else
        {
          context_nfc.init.cmdParams |= CMD_PARAMS_EXP_FAST_MASK;
          PLATFORM_LOG(VERB_NORMAL, "Transaction: FAST\r\n");
        }
        break;
      default:
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_YELLOW);
        PLATFORM_LOG(VERB_NORMAL, "No valid choice\r\n");
        PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
        break;
    }

    memcpy(&context_ble, &context_nfc, sizeof(context_ble));
  }
  else if (size > 1)
  {
#if USE_RTC
    ShowTimeDate();
#endif //USE_RTC

    PLATFORM_LOG(VERB_NORMAL, "Received (%d bytes): ", size);
    for (size_t i = 0; i < size; i++)
    {
      PLATFORM_LOG(VERB_NORMAL, "%c", CommandChars[i]);
    }
    PLATFORM_LOG(VERB_NORMAL, "\r\n");
   
#if USE_RTC
    if (strncmp((char const*)CommandChars, "date:", 5) == 0)
    {
      parseDateCommand("date:", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "time:", 5) == 0)
    {
      parseTimeCommand("time:", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "settime-", 8) == 0)
    {
      parseDateTimeCommand("settime-", &CommandChars[0], size);
    }
    else 
#endif //USE_RTC
    if (strncmp((char const*)CommandChars, "setRDpubk-", 10) == 0)
    {
      parseRDpubkCommand("setRDpubk-", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "setRDprvk-", 10) == 0)
    {
      parseRDprvkCommand("setRDprvk-", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "setRDiden-", 10) == 0)
    {
      parseRDidenCommand("setRDiden-", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "setRDgrrk-", 10) == 0)
    {
      parseRDgrrkCommand("setRDgrrk-", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "setCrIspk-", 10) == 0)
    {
      parseCredIsPkCommand("setCrIspk-", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "setDEVpbk-", 10) == 0)
    {
      parseDEVpbkCommand("setDEVpbk-", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "setAccElm-", 10) == 0)
    {
      parseAccElemCommand("setAccElm-", &CommandChars[0], size);
    }
    else if (strncmp((char const*)CommandChars, "echo-", 5) == 0)
    {
      parseEchoCommand("echo-", &CommandChars[0], size);
    }
    else if (size == 32 || size == 64 || size == 128 || size == 130)
    {
      PLATFORM_LOG(VERB_NORMAL, "Translating in hex buffer...\r\n");
      
      PLATFORM_LOG(VERB_NORMAL, " { ");
      for(uint32_t i = 0; i < size; i+=2)
      {
        if(size == 130 && i == 0)
        {
          i=2;
        }
        PLATFORM_LOG(VERB_NORMAL, "0x%c%c, ", CommandChars[i], CommandChars[i+1]);
      }
      PLATFORM_LOG(VERB_NORMAL, "}\r\n");
    }
    memcpy(&context_ble, &context_nfc, sizeof(context_ble));
  }
}

void Set_timeout_prepare_ranging(uint32_t timeout_ms)
{
  timeoutPrepareRanging = timeout_ms;
}

void Set_timeout_execute_ranging(uint32_t timeout_ms)
{
  timeoutExecuteRanging = timeout_ms;
}

void Set_timeout_suspend_ranging(uint32_t timeout_ms)
{
  timeoutSuspendRanging = timeout_ms;
}

void Set_timeout_resume_ranging(uint32_t timeout_ms)
{
  timeoutResumeRanging = timeout_ms;
}

void Set_timeout_door_operation(uint32_t timeout_ms)
{
  timeoutDoorOperation = timeout_ms;
}
