// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//==============================================================================
//
#include "pw_trace_tokenized/trace_buffer_log.h"

#include <span>

#include "pw_base64/base64.h"
#include "pw_log/log.h"
#include "pw_string/string_builder.h"
#include "pw_trace_tokenized/trace_buffer.h"

namespace pw {
namespace trace {
namespace {

constexpr int kMaxEntrySize = PW_TRACE_BUFFER_MAX_BLOCK_SIZE_BYTES;
constexpr int kMaxEntrySizeBase64 = pw::base64::EncodedSize(kMaxEntrySize);
constexpr int kLineLength = 80;

class ScopedTracePause {
 public:
  ScopedTracePause() : was_enabled_(pw_trace_IsEnabled()) {
    PW_TRACE_SET_ENABLED(false);
  }
  ~ScopedTracePause() { PW_TRACE_SET_ENABLED(was_enabled_); }

 private:
  bool was_enabled_;
};

}  // namespace

pw::Status DumpTraceBufferToLog() {
  std::byte line_buffer[kLineLength] = {};
  std::byte entry_buffer[kMaxEntrySize + 1] = {};
  char entry_base64_buffer[kMaxEntrySizeBase64] = {};
  pw::StringBuilder line_builder(line_buffer);
  ScopedTracePause pause_trace;
  pw::ring_buffer::PrefixedEntryRingBuffer* trace_buffer =
      pw::trace::GetBuffer();
  size_t bytes_read = 0;
  PW_LOG_INFO("[TRACE] begin");
  while (trace_buffer->PeekFront(std::span(entry_buffer).subspan(1),
                                 &bytes_read) != pw::Status::OutOfRange()) {
    trace_buffer->PopFront();
    entry_buffer[0] = static_cast<std::byte>(bytes_read);
    // The entry buffer is formatted as (size, entry) with an extra byte as
    // a header to the entry. The calcuation of bytes_read + 1 represents
    // the extra size header.
    size_t to_write =
        pw::base64::Encode(std::span(entry_buffer, bytes_read + 1),
                           std::span(entry_base64_buffer));
    size_t space_left = line_builder.max_size() - line_builder.size();
    size_t written = 0;
    while (to_write - written >= space_left) {
      line_builder.append(entry_base64_buffer + written, space_left);
      PW_LOG_INFO("[TRACE] data: %s", line_builder.c_str());
      line_builder.clear();
      written += space_left;
      space_left = line_builder.max_size();
    }
    line_builder.append(entry_base64_buffer + written, to_write - written);
  }
  if (line_builder.size() > 0) {
    PW_LOG_INFO("[TRACE] data: %s", line_builder.c_str());
  }
  PW_LOG_INFO("[TRACE] end");
  return pw::OkStatus();
}

}  // namespace trace
}  // namespace pw