//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).

#include "util/log_buffer.h"

#include "port/sys_time.h"
#include "port/port.h"

namespace rocksdb {

LogBuffer::LogBuffer(const InfoLogLevel log_level,
                     Logger*info_log)
    : log_level_(log_level), info_log_(info_log) {}

void LogBuffer::AddLogToBuffer(size_t max_log_size, const char* format,
                               va_list ap) {
  if (!info_log_ || log_level_ < info_log_->GetInfoLogLevel()) {
    // Skip the level because of its level.
    return;
  }

  char* alloc_mem = arena_.AllocateAligned(max_log_size);
  BufferedLog* buffered_log = new (alloc_mem) BufferedLog();
  char* p = buffered_log->message;
  char* limit = alloc_mem + max_log_size - 1;

  // store the time
  gettimeofday(&(buffered_log->now_tv), nullptr);

  // Print the message
  if (p < limit) {
    va_list backup_ap;
    va_copy(backup_ap, ap);
    auto n = vsnprintf(p, limit - p, format, backup_ap);
#ifndef OS_WIN
    // MS reports -1 when the buffer is too short
    assert(n >= 0);
#endif
    if (n > 0) {
      p += n;
    } else {
      p = limit;
    }
    va_end(backup_ap);
  }

  if (p > limit) {
    p = limit;
  }

  // Add '\0' to the end
  *p = '\0';

  logs_.push_back(buffered_log);
}

void LogBuffer::FlushBufferToLog() {
  for (BufferedLog* log : logs_) {
    const time_t seconds = log->now_tv.tv_sec;
    struct tm t;
    if (localtime_r(&seconds, &t) != nullptr) {
      Log(log_level_, info_log_,
          "(Original Log Time %04d/%02d/%02d-%02d:%02d:%02d.%06d) %s",
          t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min,
          t.tm_sec, static_cast<int>(log->now_tv.tv_usec), log->message);
    }
  }
  logs_.clear();
}

void LogToBuffer(LogBuffer* log_buffer, size_t max_log_size, const char* format,
                 ...) {
  if (log_buffer != nullptr) {
    va_list ap;
    va_start(ap, format);
    log_buffer->AddLogToBuffer(max_log_size, format, ap);
    va_end(ap);
  }
}

void LogToBuffer(LogBuffer* log_buffer, const char* format, ...) {
  const size_t kDefaultMaxLogSize = 512;
  if (log_buffer != nullptr) {
    va_list ap;
    va_start(ap, format);
    log_buffer->AddLogToBuffer(kDefaultMaxLogSize, format, ap);
    va_end(ap);
  }
}

}  // namespace rocksdb
