/** ==========================================================================
 * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
 * with no warranties. This code is yours to share, use and modify with no
 * strings attached and no restrictions or obligations.
 *
 * For more information see g3log/LICENSE or refer refer to http://unlicense.org
 * ============================================================================*/

#include "g3log/logcapture.hpp"
#include "g3log/crashhandler.hpp"

// For Windows we need force a thread_local install per thread of three
// signals that must have a signal handler instealled per thread-basis
// It is really a royal pain. Seriously Microsoft? Seriously?
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread()
#else
// Does nothing  --- enforces that semicolon must be written
#define SIGNAL_HANDLER_VERIFY() do {} while(0)
#endif



/** logCapture is a simple struct for capturing log/fatal entries. At destruction the
* captured message is forwarded to background worker.
* As a safety precaution: No memory allocated here will be moved into the background
* worker in case of dynamic loaded library reasons instead the arguments are copied
* inside of g3log.cpp::saveMessage*/
LogCapture::~LogCapture() {
   using namespace g3::internal;
   SIGNAL_HANDLER_VERIFY();
   saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str());
}


/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
LogCapture::LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const char *dump) : LogCapture("", 0, "", level, "", fatal_signal, dump) {
}

/**
 * @file, line, function are given in g3log.hpp from macros
 * @level INFO/DEBUG/WARNING/FATAL
 * @expression for CHECK calls
 * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
 */
LogCapture::LogCapture(const char *file, const int line, const char *function, const LEVELS &level,
                       const char *expression, g3::SignalType fatal_signal, const char *dump)
   : _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) {

   if (g3::internal::wasFatal(level)) {
      _stack_trace = {"\n*******\tSTACKDUMP *******\n"};
      _stack_trace.append(g3::internal::stackdump(dump));
   }
}



/**
* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF
* See also for the attribute formatting ref:  http://www.codemaestro.com/reviews/18
*/
void LogCapture::capturef(const char *printf_like_message, ...) {
   static const int kMaxMessageSize = 2048;
   static const std::string kTruncatedWarningText = "[...truncated...]";
   char finished_message[kMaxMessageSize];
   va_list arglist;
   va_start(arglist, printf_like_message);

#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
   const int nbrcharacters = vsnprintf_s(finished_message, _countof(finished_message), _TRUNCATE, printf_like_message, arglist);
#else
   const int nbrcharacters = vsnprintf(finished_message, sizeof (finished_message), printf_like_message, arglist);
#endif
   va_end(arglist);

   if (nbrcharacters <= 0) {
      stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
      stream() << '"' << printf_like_message << '"' << std::endl;
   } else if (nbrcharacters > kMaxMessageSize) {
      stream() << finished_message << kTruncatedWarningText;
   } else {
      stream() << finished_message;
   }
}


