// 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

#ifndef THIRD_PARTY_PROTOBUF_COMPILER_PERL_GENERATOR_H__
#define THIRD_PARTY_PROTOBUF_COMPILER_PERL_GENERATOR_H__

#ifndef GOOGLE3
// Alias proto2_ symbols to google_protobuf_ for local builds using vendored upb
#define proto2_DescriptorProto google_protobuf_DescriptorProto
#define proto2_DescriptorProto_enum_type google_protobuf_DescriptorProto_enum_type
#define proto2_DescriptorProto_field google_protobuf_DescriptorProto_field
#define proto2_DescriptorProto_name google_protobuf_DescriptorProto_name
#define proto2_DescriptorProto_nested_type google_protobuf_DescriptorProto_nested_type
#define proto2_EnumDescriptorProto google_protobuf_EnumDescriptorProto
#define proto2_EnumDescriptorProto_name google_protobuf_EnumDescriptorProto_name
#define proto2_EnumDescriptorProto_value google_protobuf_EnumDescriptorProto_value
#define proto2_EnumValueDescriptorProto google_protobuf_EnumValueDescriptorProto
#define proto2_EnumValueDescriptorProto_name google_protobuf_EnumValueDescriptorProto_name
#define proto2_EnumValueDescriptorProto_number google_protobuf_EnumValueDescriptorProto_number
#define proto2_FieldDescriptorProto google_protobuf_FieldDescriptorProto
#define proto2_FieldDescriptorProto_name google_protobuf_FieldDescriptorProto_name
#define proto2_FieldDescriptorProto_type google_protobuf_FieldDescriptorProto_type
#define proto2_FieldDescriptorProto_type_name google_protobuf_FieldDescriptorProto_type_name
#define proto2_FileDescriptorProto google_protobuf_FileDescriptorProto
#define proto2_FileDescriptorProto_dependency google_protobuf_FileDescriptorProto_dependency
#define proto2_FileDescriptorProto_enum_type google_protobuf_FileDescriptorProto_enum_type
#define proto2_FileDescriptorProto_message_type google_protobuf_FileDescriptorProto_message_type
#define proto2_FileDescriptorProto_name google_protobuf_FileDescriptorProto_name
#define proto2_FileDescriptorProto_package google_protobuf_FileDescriptorProto_package
#define proto2_FileDescriptorProto_serialize google_protobuf_FileDescriptorProto_serialize
#define proto2_FileDescriptorProto_service google_protobuf_FileDescriptorProto_service
#define proto2_MethodDescriptorProto google_protobuf_MethodDescriptorProto
#define proto2_MethodDescriptorProto_input_type google_protobuf_MethodDescriptorProto_input_type
#define proto2_MethodDescriptorProto_name google_protobuf_MethodDescriptorProto_name
#define proto2_MethodDescriptorProto_output_type google_protobuf_MethodDescriptorProto_output_type
#define proto2_ServiceDescriptorProto google_protobuf_ServiceDescriptorProto
#define proto2_ServiceDescriptorProto_method google_protobuf_ServiceDescriptorProto_method
#define proto2_ServiceDescriptorProto_name google_protobuf_ServiceDescriptorProto_name

// Alias proto2_compiler_ symbols to google_protobuf_compiler_ symbols
#define proto2_compiler_CodeGeneratorRequest google_protobuf_compiler_CodeGeneratorRequest
#define proto2_compiler_CodeGeneratorResponse google_protobuf_compiler_CodeGeneratorResponse
#define proto2_compiler_CodeGeneratorResponse_File google_protobuf_compiler_CodeGeneratorResponse_File

#define proto2__compiler__CodeGeneratorRequest_msg_init google__protobuf__compiler__CodeGeneratorRequest_msg_init
#define proto2__compiler__CodeGeneratorResponse_msg_init google__protobuf__compiler__CodeGeneratorResponse_msg_init
#define proto2__compiler__CodeGeneratorResponse__File_msg_init google__protobuf__compiler__CodeGeneratorResponse__File_msg_init

#define proto2_compiler_CodeGeneratorRequest_proto_file google_protobuf_compiler_CodeGeneratorRequest_proto_file
#define proto2_compiler_CodeGeneratorRequest_file_to_generate google_protobuf_compiler_CodeGeneratorRequest_file_to_generate
#define proto2_compiler_CodeGeneratorResponse_new google_protobuf_compiler_CodeGeneratorResponse_new
#define proto2_compiler_CodeGeneratorResponse_set_supported_features google_protobuf_compiler_CodeGeneratorResponse_set_supported_features
#define proto2_compiler_CodeGeneratorResponse_set_minimum_edition google_protobuf_compiler_CodeGeneratorResponse_set_minimum_edition
#define proto2_compiler_CodeGeneratorResponse_set_maximum_edition google_protobuf_compiler_CodeGeneratorResponse_set_maximum_edition
#define proto2_compiler_CodeGeneratorResponse_add_file google_protobuf_compiler_CodeGeneratorResponse_add_file
#define proto2_compiler_CodeGeneratorResponse_File_set_name google_protobuf_compiler_CodeGeneratorResponse_File_set_name
#define proto2_compiler_CodeGeneratorResponse_File_set_content google_protobuf_compiler_CodeGeneratorResponse_File_set_content

#define proto2_compiler_CodeGeneratorRequest_parse google_protobuf_compiler_CodeGeneratorRequest_parse
#define proto2_compiler_CodeGeneratorResponse_serialize google_protobuf_compiler_CodeGeneratorResponse_serialize
#define proto2_compiler_CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL google_protobuf_compiler_CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL
#define proto2_compiler_CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS google_protobuf_compiler_CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS
#endif

#include <sstream>
#include <string>
#include <unordered_map>
#include <string_view>
#include "upb/mem/arena.h"

#ifdef GOOGLE3
#include "net/proto2/proto/descriptor.upb.h"
#include "third_party/protobuf/compiler/perl/plugin.upb.h"
#else
#include "google/protobuf/compiler/plugin.upb.h"
#include "google/protobuf/descriptor.upb.h"
#endif

namespace google {
namespace protobuf {
namespace compiler {
namespace perl {

class PerlCodeGenerator {
 public:
  PerlCodeGenerator(const proto2_compiler_CodeGeneratorRequest* request,
                    upb_Arena* arena)
      : request_(request), arena_(arena) {
    BuildPackageMap();
    BuildTypeMap();
  }

  proto2_compiler_CodeGeneratorResponse* Generate();

 private:
  void BuildPackageMap();
  void BuildTypeMap();
  void RegisterTypesRecursively(const proto2_DescriptorProto* msg_proto,
                                std::string_view proto_prefix,
                                std::string_view perl_prefix);
  std::string ResolvePerlClass(std::string_view proto_type);
  std::string GetPerlPackage(const proto2_FileDescriptorProto* file_proto);

  std::string GenerateModule(const proto2_FileDescriptorProto* file_proto);
  std::string GenerateTypes(const proto2_FileDescriptorProto* file_proto);

  void PrintTypesRecursively(const proto2_DescriptorProto* msg_proto,
                             std::string_view current_package,
                             std::stringstream& ss);
  void PrintMessageTypes(const proto2_DescriptorProto* msg_proto,
                         std::string_view current_package,
                         std::stringstream& ss);
  void PrintEnumTypes(const proto2_EnumDescriptorProto* enum_proto,
                      std::string_view current_package, std::stringstream& ss);

  upb_StringView ArenaCopy(std::string_view s) {
    char* buf = (char*)upb_Arena_Malloc(arena_, s.length());
    memcpy(buf, s.data(), s.length());
    return upb_StringView_FromDataAndSize(buf, s.length());
  }

  const proto2_compiler_CodeGeneratorRequest* request_;
  upb_Arena* arena_;
  std::unordered_map<std::string, std::string> file_to_package_;
  std::unordered_map<std::string, std::string> type_to_perl_class_;
  std::string package_name_;
};

}  // namespace perl
}  // namespace compiler
}  // namespace protobuf
}  // namespace google

#endif  // THIRD_PARTY_PROTOBUF_COMPILER_PERL_GENERATOR_H__
