// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/webui/i18n_source_stream.h"

#include <algorithm>
#include <utility>

#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "net/base/io_buffer.h"

namespace ui {

I18nSourceStream::~I18nSourceStream() {}

std::unique_ptr<I18nSourceStream> I18nSourceStream::Create(
    std::unique_ptr<SourceStream> upstream,
    SourceStream::SourceType type,
    const TemplateReplacements* replacements) {
  DCHECK(replacements);
  std::unique_ptr<I18nSourceStream> source(
      new I18nSourceStream(std::move(upstream), type, replacements));
  return source;
}

I18nSourceStream::I18nSourceStream(std::unique_ptr<SourceStream> upstream,
                                   SourceStream::SourceType type,
                                   const TemplateReplacements* replacements)
    : FilterSourceStream(type, std::move(upstream)),
      replacements_(replacements) {}

std::string I18nSourceStream::GetTypeAsString() const {
  return "i18n";
}

int I18nSourceStream::FilterData(net::IOBuffer* output_buffer,
                                 int output_buffer_size,
                                 net::IOBuffer* input_buffer,
                                 int input_buffer_size,
                                 int* consumed_bytes,
                                 bool upstream_end_reached) {
  // |input_| is often empty (or it may have something from the prior call).
  input_.append(input_buffer->data(), input_buffer_size);
  *consumed_bytes = input_buffer_size;

  // The replacement tag starts with '$' and ends with '}'. The white-space
  // characters are an optimization that looks for characters that are invalid
  // within $i18n{} tags.
  size_t pos = input_.find_last_of("$} \t\r\n");
  std::string to_process;
  if (!upstream_end_reached && pos != std::string::npos && input_[pos] == '$') {
    // If there is a trailing '$' then split the |input_| at that point. Process
    // the first part; save the second part for the next call to FilterData().
    to_process.assign(input_, 0, pos);
    input_.erase(0, pos);
  } else {
    // There is no risk of a split key, process the whole input.
    to_process.swap(input_);
  }

  output_.append(ReplaceTemplateExpressions(to_process, *replacements_));
  int bytes_out =
      std::min(output_.size(), static_cast<size_t>(output_buffer_size));
  output_.copy(output_buffer->data(), bytes_out);
  output_.erase(0, bytes_out);
  return bytes_out;
}

}  // namespace ui
