// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#include "google/protobuf/compiler/kotlin/file.h"

#include <memory>
#include <string>
#include <vector>

#include "absl/strings/str_cat.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/java/context.h"
#include "google/protobuf/compiler/java/helpers.h"
#include "google/protobuf/compiler/java/name_resolver.h"
#include "google/protobuf/compiler/java/names.h"
#include "google/protobuf/compiler/java/options.h"
#include "google/protobuf/compiler/kotlin/message.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"

namespace google {
namespace protobuf {
namespace compiler {
namespace kotlin {

using google::protobuf::compiler::java::Context;
using google::protobuf::compiler::java::Options;

FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options)
    : file_(file),
      java_package_(java::FileJavaPackage(file, options)),
      message_generators_(file->message_type_count()),
      context_(new Context(file, options)),
      name_resolver_(context_->GetNameResolver()),
      options_(options) {
  for (int i = 0; i < file_->message_type_count(); ++i) {
    message_generators_[i] = std::make_unique<MessageGenerator>(
        file_->message_type(i), context_.get());
  }
}

std::string FileGenerator::GetKotlinClassname() {
  return absl::StrCat(name_resolver_->GetFileImmutableClassName(file_), "Kt");
}

void FileGenerator::Generate(io::Printer* printer) {
  printer->Print(
      "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
      "// NO CHECKED-IN PROTOBUF "
      // Intentional line breaker
      "GENCODE\n"
      "// source: $filename$\n"
      "\n",
      "filename", file_->name());
  printer->Print(
      "// Generated files should ignore deprecation warnings\n"
      "@file:Suppress(\"DEPRECATION\")\n");
  if (!java_package_.empty()) {
    printer->Print(
        "package $package$;\n"
        "\n",
        "package", java::EscapeKotlinKeywords(java_package_));
  }
}

void FileGenerator::GenerateSiblings(
    const std::string& package_dir, GeneratorContext* context,
    std::vector<std::string>* file_list,
    std::vector<std::string>* annotation_list) {
  for (int i = 0; i < file_->message_type_count(); i++) {
    const Descriptor* descriptor = file_->message_type(i);
    MessageGenerator* generator = message_generators_[i].get();
    auto open_file = [context](const std::string& filename) {
      return std::unique_ptr<io::ZeroCopyOutputStream>(context->Open(filename));
    };
    std::string filename =
        absl::StrCat(package_dir, descriptor->name(), "Kt.kt");
    file_list->push_back(filename);
    std::string info_full_path = absl::StrCat(filename, ".pb.meta");
    GeneratedCodeInfo annotations;
    io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
        &annotations);
    auto output = open_file(filename);
    io::Printer printer(
        output.get(), '$',
        options_.annotate_code ? &annotation_collector : nullptr);

    printer.Print(
        "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
        "// NO CHECKED-IN PROTOBUF "
        // Intentional line breaker
        "GENCODE\n"
        "// source: $filename$\n"
        "\n",
        "filename", descriptor->file()->name());
    printer.Print(
        "// Generated files should ignore deprecation warnings\n"
        "@file:Suppress(\"DEPRECATION\")\n");
    if (!java_package_.empty()) {
      printer.Print(
          "package $package$;\n"
          "\n",
          "package", java::EscapeKotlinKeywords(java_package_));
    }

    generator->GenerateMembers(&printer);
    generator->GenerateTopLevelMembers(&printer);

    if (options_.annotate_code) {
      auto info_output = open_file(info_full_path);
      annotations.SerializeToZeroCopyStream(info_output.get());
      annotation_list->push_back(info_full_path);
    }
  }
}

}  // namespace kotlin
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
