#ifndef LEPTO_LOG_H
#define LEPTO_LOG_H
//----------------------------------------------------------------------------
//
// \brief   Logging with lepto
//
//----------------------------------------------------------------------------


//---Includes-----------------------------------------------------------------


#include <stdarg.h>
#include <lepto/lepto.h>      // IS_ENABLED( )


//---Declaration--------------------------------------------------------------


// #if ! defined LEPTO_LOG_NO_USE_ANSI
#if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT )
   #if ! IS_ENABLED( CONFIG_LEPTO_LOG_USE_ANSI )
      #define CONFIG_LEPTO_LOG_USE_ANSI
   #endif
#endif

#if defined LEPTO_LOG_DEBUG
   #define lDebug( ... ) lDebugReal( __VA_ARGS__ )
#else
   #define lDebug(a,...)
#endif

#if defined LEPTO_LOG_DEBUG || defined LEPTO_LOG_DEBUG_ASSERT
   #define lDebugAssert(a, ...) if(! (a)){ lFatal("Assertion false: '" #a "'. " __VA_ARGS__ ); }
#else
   #define lDebugAssert(a, ...)
#endif

#define lAssert( assertion, ... ) if ( ! (assertion) ) \
         { lFatal( "ASRT" /*SW " __VA_ARGS__*/ ); };

// { exception(); };



enum class ELogCategory
{
   Debug       = 0,
   Good        = 0,
   Info        = 0,
   Caution     = 1,
   Warning     = 2,
   Critical    = 3,
   Fatal       = 4,

   Function    = 8,    // Function area
   Beckon      = 8,    //
   Calm        = 9,
   Loop        = 10,
};

enum class ELogCode: int;
enum class ELogBlended: int;

inline constexpr ELogBlended operator| (ELogCategory cat, int i)
{
   return( (ELogBlended)( (int) cat | ( i << 4 ) ) );
}

inline constexpr ELogCategory toCategory(ELogBlended code)
{
   return( (ELogCategory)( (int) code & 0xF ) );
}


#if ! IS_ENABLED( CONFIG_LEPTO_LOG_SILENT )
   #define lDebugReal(msg, ...) lLogChooser(ELogCategory::Debug|0, msg, ##__VA_ARGS__ )
   #define lInfo(msg, ...) lLogChooser(ELogCategory::Info|0, msg, ##__VA_ARGS__ )
   #define lCaution(msg, ... ) lLogChooser(ELogCategory::Caution|0, msg, ##__VA_ARGS__ )
   #define lWarning(msg, ... ) lLogChooser(ELogCategory::Warning|0, msg, ##__VA_ARGS__ )
   #define lCritical(msg, ... ) lLogChooser(ELogCategory::Critical|0, msg, ##__VA_ARGS__ )
   #define lFatal(msg, ... ) lLogChooser(ELogCategory::Fatal|0, msg, ##__VA_ARGS__ )
#else
   #define lDebugReal(msg, ...) lLogChooser(ELogCategory::Debug, "" )
   #define lInfo(msg, ...) lLogChooser(ELogCategory::Info, "" )
   #define lCaution(msg, ... ) lLogChooser(ELogCategory::Caution, "" )
   #define lWarning(msg, ... ) lLogChooser(ELogCategory::Warning, "" )
   #define lCritical(msg, ... ) lLogChooser(ELogCategory::Critical, "" )
   #define lFatal(msg, ... ) lLogChooser(ELogCategory::Fatal, "" )
#endif

#define EV_STR                   "%X"
// #define EV( code, format)        EV_STR format, code
#define EV( code, format)        format
#define lDebugRealE(code, msg, ...) lLogChooser(ELogCategory::Debug | code, EV(code, msg), ##__VA_ARGS__ )
#define lInfoE(code, msg, ...) lLogChooser(ELogCategory::Info | code, EV(code, msg), ##__VA_ARGS__ )
#define lCautionE(code, msg, ... ) lLogChooser(ELogCategory::Caution | code, EV(code, msg), ##__VA_ARGS__ )
#define lWarningE(code, msg, ... ) lLogChooser(ELogCategory::Warning | code, EV(code, msg), ##__VA_ARGS__ )
#define lCriticalE(code, msg, ... ) lLogChooser(ELogCategory::Critical | code, EV(code, msg), ##__VA_ARGS__ )
#define lFatalE(code, msg, ... ) lLogChooser(ELogCategory::Fatal | code, EV(code, msg), ##__VA_ARGS__ )


// You can use the LDS macro to give two different messages depending on the
// flash size. Example: lCritical( LDS ("DSBL DEV 0x%X", "Disabling device 0x%X"), dev);
#if IS_ENABLED( CONFIG_LEPTO_LOG_DOWNSIZE )
   #define LDS( msg1, msg2 ) msg1
#else
   #define LDS( msg1, msg2 ) msg2
#endif

// The CMake helpers only shows the compilation unit. This is confusing. The
// problem only exists on old distributions. Containing full pathes is not
// very critical as long as STM32 toolchains have proper __FILE_NAME__.
#if 0
inline constexpr
const char* shrinkFileName( const char* const filename )
{
   return( __builtin_strrchr(filename, '/') + 1 );
};
#endif

#if ! defined __FILE_NAME__
   #if defined ( STM32 )
      #warning "__FILE_NAME__" is not defined. Implement it in CMake to save memory.
   #endif
   // Use full size absolut path:
   inline constexpr
   const char* shrinkFileName( const char* const filename )
   {
      return( __builtin_strrchr(filename, '/') + 1 );
   };
   #define __FILE_NAME__ shrinkFileName( __FILE__ )
#endif

#if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT )
   #define lLogChooser( ... ) lLogPP( __FILE_NAME__, __LINE__, __VA_ARGS__ )
#else
   #define lLogChooser( ... ) lLog( __VA_ARGS__ )
#endif

extern "C"
{
   #if ! IS_ENABLED( CONFIG_LEPTO_LOG_SILENT )
      extern const char *categoryMessages[];
      void lVLog(ELogBlended code, const char *format, va_list &list );
      void lVLogPP(const char*file, int line, ELogBlended code, const char *format, va_list &list );
      void lLog(ELogBlended code, const char *format, ... );
      void lLogPP(const char*file, int line, ELogBlended code, const char *format, ... );
      /** \brief Check which implementation is used
       */
      int lLogInfo();
   #else
      void lVLog( ELogCategory category, const char *format, ... );
      void lLog( ELogCategory category, const char *format, ... );
   #endif
}


//---fin----------------------------------------------------------------------
#endif // ? ! LEPTO_LOG_H
