// Copyright 2020 The Tint 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
//
//     http://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 "src/writer/text_generator.h"

#include <algorithm>
#include <limits>

#include "src/utils/map.h"

namespace tint {
namespace writer {

TextGenerator::TextGenerator(const Program* program)
    : program_(program), builder_(ProgramBuilder::Wrap(program)) {}

TextGenerator::~TextGenerator() = default;

std::string TextGenerator::UniqueIdentifier(const std::string& prefix) {
  return builder_.Symbols().NameFor(builder_.Symbols().New(prefix));
}

std::string TextGenerator::StructName(const sem::Struct* s) {
  auto name = builder_.Symbols().NameFor(s->Name());
  if (name.size() > 1 && name[0] == '_' && name[1] == '_') {
    name = utils::GetOrCreate(builtin_struct_names_, s,
                              [&] { return UniqueIdentifier(name.substr(2)); });
  }
  return name;
}

std::string TextGenerator::TrimSuffix(std::string str,
                                      const std::string& suffix) {
  if (str.size() >= suffix.size()) {
    if (str.substr(str.size() - suffix.size(), suffix.size()) == suffix) {
      return str.substr(0, str.size() - suffix.size());
    }
  }
  return str;
}

TextGenerator::LineWriter::LineWriter(TextBuffer* buf) : buffer(buf) {}

TextGenerator::LineWriter::LineWriter(LineWriter&& other) {
  buffer = other.buffer;
  other.buffer = nullptr;
}

TextGenerator::LineWriter::~LineWriter() {
  if (buffer) {
    buffer->Append(os.str());
  }
}

TextGenerator::TextBuffer::TextBuffer() = default;
TextGenerator::TextBuffer::~TextBuffer() = default;

void TextGenerator::TextBuffer::IncrementIndent() {
  current_indent += 2;
}

void TextGenerator::TextBuffer::DecrementIndent() {
  current_indent = std::max(2u, current_indent) - 2u;
}

void TextGenerator::TextBuffer::Append(const std::string& line) {
  lines.emplace_back(Line{current_indent, line});
}

void TextGenerator::TextBuffer::Insert(const std::string& line,
                                       size_t before,
                                       uint32_t indent) {
  if (before >= lines.size()) {
    diag::List d;
    TINT_ICE(Writer, d)
        << "TextBuffer::Insert() called with before >= lines.size()\n"
        << "  before:" << before << "\n"
        << "  lines.size(): " << lines.size();
    return;
  }
  lines.insert(lines.begin() + before, Line{indent, line});
}

void TextGenerator::TextBuffer::Append(const TextBuffer& tb) {
  for (auto& line : tb.lines) {
    // TODO(bclayton): inefficent, consider optimizing
    lines.emplace_back(Line{current_indent + line.indent, line.content});
  }
}

void TextGenerator::TextBuffer::Insert(const TextBuffer& tb,
                                       size_t before,
                                       uint32_t indent) {
  if (before >= lines.size()) {
    diag::List d;
    TINT_ICE(Writer, d)
        << "TextBuffer::Insert() called with before >= lines.size()\n"
        << "  before:" << before << "\n"
        << "  lines.size(): " << lines.size();
    return;
  }
  size_t idx = 0;
  for (auto& line : tb.lines) {
    // TODO(bclayton): inefficent, consider optimizing
    lines.insert(lines.begin() + before + idx,
                 Line{indent + line.indent, line.content});
    idx++;
  }
}

std::string TextGenerator::TextBuffer::String(uint32_t indent /* = 0 */) const {
  std::stringstream ss;
  for (auto& line : lines) {
    if (!line.content.empty()) {
      for (uint32_t i = 0; i < indent + line.indent; i++) {
        ss << " ";
      }
      ss << line.content;
    }
    ss << std::endl;
  }
  return ss.str();
}

TextGenerator::ScopedParen::ScopedParen(std::ostream& stream) : s(stream) {
  s << "(";
}
TextGenerator::ScopedParen::~ScopedParen() {
  s << ")";
}

TextGenerator::ScopedIndent::ScopedIndent(TextGenerator* generator)
    : ScopedIndent(generator->current_buffer_) {}

TextGenerator::ScopedIndent::ScopedIndent(TextBuffer* buffer)
    : buffer_(buffer) {
  buffer_->IncrementIndent();
}
TextGenerator::ScopedIndent::~ScopedIndent() {
  buffer_->DecrementIndent();
}

}  // namespace writer
}  // namespace tint
