text: // Copyright (c) 2018 Doyub Kim
  //
  // I am making my contributions/submissions to this project solely in my
  // personal capacity and am not conveying any rights to any intellectual
  // property of any third parties.
  
  #include "common.h"
  
  #include "logging.h"
  #include "macros.h"
  
  #include <chrono>
  #include <cstdlib>
  #include <exception>
  #include <fstream>
  #include <iostream>
  #include <mutex>
  #include <string>
  
  namespace vox {
  namespace geometry {
  
  static std::mutex critical;
  
  static std::ostream *infoOutStream = &std::cout;
  static std::ostream *warnOutStream = &std::cout;
  static std::ostream *errorOutStream = &std::cerr;
  static std::ostream *debugOutStream = &std::cout;
  static LoggingLevel sLoggingLevel = LoggingLevel::All;
  
  inline std::ostream *levelToStream(LoggingLevel level) {
    switch (level) {
    case LoggingLevel::All:
    case LoggingLevel::Info:
      return infoOutStream;
    case LoggingLevel::Warn:
      return warnOutStream;
    case LoggingLevel::Error:
      return errorOutStream;
    case LoggingLevel::Debug:
      return debugOutStream;
    case LoggingLevel::Off:
      return nullptr;
    }
  
    return nullptr;
  }
  
  inline std::string levelToString(LoggingLevel level) {
    switch (level) {
    case LoggingLevel::All:
      return "";
    case LoggingLevel::Info:
      return "INFO";
    case LoggingLevel::Warn:
      return "WARN";
    case LoggingLevel::Error:
      return "ERROR";
    case LoggingLevel::Debug:
      return "DEBUG";
    case LoggingLevel::Off:
      return "";
    }
  
    return "";
  }
  
  inline bool isLeq(LoggingLevel a, LoggingLevel b) { return (uint8_t)a <= (uint8_t)b; }
  
  Logger::Logger(LoggingLevel level) : _level(level) {}
  
  Logger::~Logger() {
    std::lock_guard<std::mutex> lock(critical);
    bool shouldWrite = true;
  #ifndef JET_DEBUG_MODE
    shouldWrite = _level != LoggingLevel::Debug;
  #endif
    if (shouldWrite && isLeq(sLoggingLevel, _level)) {
      auto strm = levelToStream(_level);
      (*strm) << _buffer.str() << std::endl;
      strm->flush();
    }
  }
  
  void Logging::setInfoStream(std::ostream *strm) {
    std::lock_guard<std::mutex> lock(critical);
    infoOutStream = strm;
  }
  
  void Logging::setWarnStream(std::ostream *strm) {
    std::lock_guard<std::mutex> lock(critical);
    warnOutStream = strm;
  }
  
  void Logging::setErrorStream(std::ostream *strm) {
    std::lock_guard<std::mutex> lock(critical);
    errorOutStream = strm;
  }
  
  void Logging::setDebugStream(std::ostream *strm) {
    std::lock_guard<std::mutex> lock(critical);
    debugOutStream = strm;
  }
  
  void Logging::setAllStream(std::ostream *strm) {
    setInfoStream(strm);
    setWarnStream(strm);
    setErrorStream(strm);
    setDebugStream(strm);
  }
  
  std::string Logging::getHeader(LoggingLevel level) {
    auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    char timeStr[20];
  #ifdef JET_WINDOWS
    tm time;
    localtime_s(&time, &now);
    strftime(timeStr, sizeof(timeStr), "%F %T", &time);
  #else
    strftime(timeStr, sizeof(timeStr), "%F %T", std::localtime(&now));
  #endif
    char header[256];
    snprintf(header, sizeof(header), "[%s] %s ", levelToString(level).c_str(), timeStr);
    return header;
  }
  
  void Logging::setLevel(LoggingLevel level) {
    std::lock_guard<std::mutex> lock(critical);
    sLoggingLevel = level;
  }
  
  void Logging::mute() { setLevel(LoggingLevel::Off); }
  
  void Logging::unmute() { setLevel(LoggingLevel::All); }
  
  } // namespace vox
  } // namespace geometry
  