//----------------------------------------------------------------------------
///
/// \file   log.cpp
///
/// \brief  Logging
///
/// \date   20221222
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


//---Documentation------------------------------------------------------------


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


//---Own------------------------------

#include <lepto/log.h>
#include <lepto/lepto.h>         // IS_ENABLED
#include <biwak/log_private.h>
#include <biwak/biwak.h>         // BIWAK_CODE
#include <lepto/bufferRing.hpp>
#include <stdarg.h>
#include <stdio.h>

#if IS_ENABLED( CONFIG_BIWAK_LOG_HEARTBEAT )
   #include <biwak/heartbeat.hpp>
#endif

#if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT ) || IS_ENABLED( CONFIG_LEPTO_LOG_USE_ANSI )
   #include <lepto/ansi.h>
#endif

#if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT ) && IS_ENABLED( CONFIG_LEPTO_LOG_DOWNSIZE )
   #error Having LEPTO_LOG_DOWNSIZE and LEPTO_LOG_USE_PRETTY_PRINT enabled at \
          the same time does not make sense.
#endif

#if IS_ENABLED( CONFIG_BIWAK_LOG_DOWNSIZE )
   #error Config entry BIWAK_LOG_DOWNSIZE does not exist.
#endif

#if defined( CONFIG_BIWAK_LOG_CODE )
   #error BIWAK_LOG_CODE is obsolete
#endif


//---Implementation-----------------------------------------------------------


static CRing<SLogEntry> logs( MAX_LOG_ENTRIES );
static bool logOverflow=false;

#if IS_ENABLED( CONFIG_BIWAK_LOG_SUPPORT_AUTOFLUSH )
   #if IS_ENABLED2( CONFIG_BIWAK_LOG_AUTOFLUSH )
      static bool biwakLogAutoFlush=true;
   #else
      static bool biwakLogAutoFlush=false;
   #endif
#endif

#if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT )
void bVLogPP(const char*file, int line, ELogBlended code, const char *format, va_list &list )
#else
void bVLog(ELogBlended code, const char *format, va_list &list )
#endif
{
   // The buffer is imediately available and blocked. But string will be
   // created before jumping back to mainloop.
   // Semaphore is needed here around "pushBuffer" because additional ISR could
   // produce log.
   if(!logs.pushable())
   {
      // Can not produce any more messages
      logOverflow=true;
      return;
   }
   SLogEntry *le=(SLogEntry *)logs.topEntry();
   logs.pushEntry();
   if(!le)
   {
      // Avoid exception
      return;
   }

   #if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT )
      le->file=file;
      le->line=line;
      #if IS_ENABLED( CONFIG_BIWAK_LOG_USE_TIMESTAMPS )
         le->timeStamp=lrNow();
      #endif
   #endif
      
   #if IS_ENABLED( CONFIG_BIWAK_LOG_HEARTBEAT )
   le->code=code;
   #endif

   #if ! IS_ENABLED( CONFIG_LEPTO_LOG_SILENT )
      // strcpy (le->logString, categoryMessages[ (int)category & 0xF ] );
      //int categoryLength=strlen(le->logString);
      int categoryLength=0;
   
      vsnprintf( &le->logString[categoryLength], MAX_LOG_STRING - categoryLength, format, list);
   #endif

   #if IS_ENABLED( CONFIG_BIWAK_LOG_SUPPORT_AUTOFLUSH )
   if ( biwakLogAutoFlush )
   {
      logEventLoop();
   }
   #endif

   if( toCategory(code) == ELogCategory::Fatal )
   {
      #if defined ( STM32 )
         #if IS_ENABLED( CONFIG_BIWAK_LOG_HEARTBEAT )
            // Let the LED switch on. Hope the call will survive, we are already
            // in fatal situation.
            logHeartBeat( ELogCategory::Fatal | 0 );
         #endif
         abort();
      #else
         printf("FATAL: %s\n", le->logString);
         *((int*)0)=1;
         throw("Assertion wrong");
      #endif
   }

   return;
}

#if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT )

void bLogPP( const char*file, int line, ELogBlended code, const char *format, ... )
{
   va_list list;
   va_start(list, format);
   bVLogPP( file, line, code, format, list);
   return;
}

#else

void bLog( ELogBlended code, const char *format, ... )
{
   va_list list;
   va_start(list, format);
   bVLog( code, format, list);
   return;
}

#endif

// Overload leptos logging functions
#if IS_ENABLED( CONFIG_BIWAK_LOG )
   #if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT )
      void lVLogPP(const char*file, int line, ELogBlended code, const char *format, va_list &list ) __attribute__ (( alias("bVLogPP") ) );
   #else
      void lVLog(ELogBlended code, const char *format, va_list &list ) __attribute__ (( alias("bVLog") ) );
   #endif
      int lLogInfo() __attribute__ (( alias("bLogInfo") ) );
#endif

void logEventLoop()
{
   const SLogEntry* le;
   while( ( le = (const SLogEntry*)logs.bottomEntry() ) )
   {
      #if IS_ENABLED( CONFIG_BIWAK_LOG_USE_TIMESTAMPS )
         lrtimer_t timeStamp=le->timeStamp;
      #endif
         
      #if ! defined LEPTO_LOG_SILENT
         //fputs("\r", stdout);
         if(logOverflow)
         {
            fputs("OF ", stdout);
         }
      #endif
      
      #if IS_ENABLED( CONFIG_BIWAK_LOG_USE_TIMESTAMPS )
         printf("[%08d] ",  (int)lrtValueToMsec( timeStamp ) );
      #endif

      #if ! IS_ENABLED( CONFIG_LEPTO_LOG_SILENT )
         #if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT )
            int len=printf("B %s(%d)" , le->file, le->line);
            for(int i1=0; i1<22-len; i1++)
            {
               fputs(".", stdout);
            }
            fputs( ": ", stdout );
            fputs( categoryMessages[ (int)toCategory(le->code) ], stdout );
            fputs( ANSI_NORMAL,  stdout );
            fputs( le->logString, stdout );
            fputs( "\n", stdout );
         #else
            fputs( categoryMessages[ (int)toCategory(le->code) ], stdout );
            // fputs does not print an newline
            puts(le->logString);
         #endif
      #endif

      #if IS_ENABLED( CONFIG_LEPTO_LOG_USE_PRETTY_PRINT ) || IS_ENABLED( CONFIG_LEPTO_LOG_USE_ANSI )
      #endif
      
      #if IS_ENABLED( CONFIG_BIWAK_LOG_HEARTBEAT )
         logHeartBeat( le->code );
      #endif

      #if IS_ENABLED( CONFIG_BIWAK_LOG_CUSTOM_CHANNEL )
         logCustomChannel( le->code, le->logString );
      #endif

      logs.dropEntry();
   }
}

int logPendingCount()
{
   return( logs.getEntryCount() );
}

const char *logPending()
{
   #if ! IS_ENABLED( CONFIG_LEPTO_LOG_SILENT )
      const SLogEntry* se=(const SLogEntry*) logs.bottomEntry();
      if(se)
      {
        return( (const char *)
              ( ( (const SLogEntry*) logs.bottomEntry() )->logString ) );
      }
      else
      {
          return( nullptr );
      }
   #else
      return( nullptr );
   #endif
}

#if IS_ENABLED( CONFIG_BIWAK_LOG_SUPPORT_AUTOFLUSH )

void logAutoFlush()
{
   biwakLogAutoFlush=true;
}

#endif

#if IS_ENABLED( CONFIG_BIWAK_LOG_CUSTOM_CHANNEL )
void logCustomChannel( ELogBlended code, const char* message )  __attribute__ ( ( weak ) );
void logCustomChannel( ELogBlended code, const char* message )
{
   printf("Log CC NI\n");
}
#endif // ? BIWAK_LOG_CUSTOM_CHANNEL

int bLogInfo()
{
   return( BIWAK_CODE );
}


//---fin----------------------------------------------------------------------
