#include "google/protobuf/compiler/j2objc/j2objc_extension_lite.h"

#include <google/protobuf/compiler/j2objc/j2objc_field.h>
#include <google/protobuf/compiler/j2objc/j2objc_helpers.h>

namespace google {
namespace protobuf {
namespace compiler {
namespace j2objc {

namespace {
std::string ContainingClassName(const FieldDescriptor* descriptor) {
  const Descriptor* scope = descriptor->extension_scope();
  if (scope != nullptr) {
    return ClassName(scope);
  } else {
    return ClassName(descriptor->file());
  }
}
}  // namespace

ExtensionLiteGenerator::ExtensionLiteGenerator(
    const FieldDescriptor* descriptor)
    : ExtensionGenerator(descriptor), descriptor_(descriptor) {}

ExtensionLiteGenerator::~ExtensionLiteGenerator() = default;

void ExtensionLiteGenerator::CollectSourceImports(
    std::set<std::string>* imports) const {
  imports->insert("com/google/protobuf/GeneratedMessage_PackagePrivate.h");
  // Imports needed for metadata of enum and message types.
  CollectSourceImportsForField(imports, descriptor_);
}

void ExtensionLiteGenerator::GenerateMembersHeader(io::Printer* printer) const {
  printer->Print(
      "\n"
      // TODO(anjulij): remove when lite is supported
      "// in j2objc_extension_lite.cc \n"
      "inline ComGoogleProtobufGeneratedMessage_GeneratedExtension"
      " *$classname$_get_$name$(void);\n"
      "/*! INTERNAL ONLY - Use accessor function from above. */\n"
      "FOUNDATION_EXPORT ComGoogleProtobufGeneratedMessage_GeneratedExtension"
      " *$classname$_$name$;\n"
      "J2OBJC_STATIC_FIELD_OBJ_FINAL($classname$, $name$, "
      "ComGoogleProtobufGeneratedMessage_GeneratedExtension *)\n",
      "name", UnderscoresToCamelCase(descriptor_), "classname",
      ContainingClassName(descriptor_));
}

void ExtensionLiteGenerator::GenerateSourceDefinition(
    io::Printer* printer) const {
  printer->Print(
      "ComGoogleProtobufGeneratedMessage_GeneratedExtension"
      " *$classname$_$name$;\n",
      "name", UnderscoresToCamelCase(descriptor_), "classname",
      ContainingClassName(descriptor_));
}

void ExtensionLiteGenerator::GenerateFieldData(io::Printer* printer) const {
  std::map<std::string, std::string> vars;
  vars["field_name"] = descriptor_->name();
  vars["capitalized_name"] = UnderscoresToCapitalizedCamelCase(descriptor_);
  vars["field_number"] = SimpleItoa(descriptor_->number());
  vars["flags"] = GetFieldFlags(descriptor_);
  vars["field_type"] = GetFieldTypeEnumValue(descriptor_);
  vars["default_value_type"] = GetDefaultValueTypeName(descriptor_);
  vars["default_value"] = DefaultValue(descriptor_);
  vars["containing_type_name"] = ClassName(descriptor_->containing_type());
  vars["options_data"] = GetFieldOptionsData(descriptor_);
  printer->Print(
      vars,
      "{\n"
      "  .name = \"$field_name$\",\n"
      "  .number = $field_number$,\n"
      "  .flags = $flags$,\n"
      "  .type = ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_"
      "$field_type$,\n"
      "  .defaultValue.value$default_value_type$ = $default_value$,\n"
      "  .hasBitIndex = 0,\n"
      "  .offset = 0,\n");
  GenerateClassReference(printer);
  printer->Print(vars,
                 "  .containingType = \"$containing_type_name$\",\n"
                 "  .optionsData = $options_data$,\n"
                 "},\n");
}

void ExtensionLiteGenerator::GenerateClassReference(
    io::Printer* printer) const {
  GenerateObjcClassRef(printer, descriptor_);
}

void ExtensionLiteGenerator::GenerateSourceInitializer(
    io::Printer* printer) const {
  printer->Print(
      "$classname$_$name$ = "
      "[[ComGoogleProtobufGeneratedMessage_GeneratedExtension alloc] "
      "initWithFieldData:&extensionFields[$num$]];\n",
      "classname", ContainingClassName(descriptor_), "name",
      UnderscoresToCamelCase(descriptor_), "num",
      SimpleItoa(descriptor_->index()));
}

void ExtensionLiteGenerator::GenerateRegistrationCode(
    io::Printer* printer) const {
  printer->Print(
      "CGPExtensionRegistryAdd(extensionRegistry, $classname$_get_$name$());\n",
      "classname", ContainingClassName(descriptor_), "name",
      UnderscoresToCamelCase(descriptor_));
}

}  // namespace j2objc
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
