// File: /workspace/github_cpp_makefile_star2_O2/hzeller[P]jcxxgen/schema-compiler.cc
// Function: GenerateCode(char const*, char const*, std::vector<ObjectType*, std::allocator<ObjectType*>> const&, _IO_FILE*)
void GenerateCode(const char *filename,
                  const char *nlohmann_json_include,
                  const ObjectTypeVector &objects,
                  FILE *out) {
  fprintf(out, "// Don't modify. Generated from %s\n", filename);
  fprintf(out, "#pragma once\n"
          "#include <string>\n"
          "#include <vector>\n");
  fprintf(out, "#include %s\n\n", nlohmann_json_include);
  for (const auto& o : objects) {
    fprintf(out, "struct %s", o->name.c_str());
    bool is_first = true;
    for (const auto &e : o->extends) {
      fprintf(out, "%s", is_first ? " :" : ",");
      fprintf(out, " public %s", e.c_str());
      is_first = false;
    }
    fprintf(out, " {\n");
    for (const auto&p : o->properties) {
      std::string type;
      if (p.object_type) type = p.object_type->name;
      else if (p.type == "string") type = "std::string";
      else if (p.type == "integer") type = "int";
      else if (p.type == "object") type = "nlohmann::json";
      else if (p.type == "boolean") type = "bool";
      // TODO: optional and array
      if (type.empty()) {
        std::cerr << p.location << "Not supported yet '" << p.type << "'\n";
        continue;
      }
      if (p.is_array) {
        fprintf(out, "  std::vector<%s> %s", type.c_str(), p.name.c_str());
      } else {
        fprintf(out, "  %s %s", type.c_str(), p.name.c_str());
      }
      if (!p.default_value.empty())
        fprintf(out, " = %s", p.default_value.c_str());
      fprintf(out, ";\n");
      if (p.is_optional) {
        fprintf(out, "  bool has_%s = false;  // optional property\n",
                p.name.c_str());
      }
    }

    // nlohmann::json serialization
    fprintf(out, "\n");
    fprintf(out, "  void Deserialize(const nlohmann::json &j) {\n");
    for (const auto &e : o->extends) {
      fprintf(out, "    %s::Deserialize(j);\n",
              e.c_str());
    }
    for (const auto&p : o->properties) {
      int indent = 4;
      std::string access_call = "j.at(\"" + p.name + "\")";
      std::string access_deref =  access_call + ".";
      if (p.is_optional) {
        fprintf(out, "%*sif (auto found = j.find(\"%s\"); found != j.end()) {\n",
                indent, "", p.name.c_str());
        indent += 4;
        fprintf(out, "%*shas_%s = true;\n", indent, "", p.name.c_str());
        access_call = "*found";
        access_deref = "found->";
      }
      if (p.object_type == nullptr || p.is_array) {
        fprintf(out, "%*s%sget_to(%s);\n",
                indent, "", access_deref.c_str(), p.name.c_str());
      } else {
        fprintf(out, "%*s%s.Deserialize(%s);\n",
                indent, "", p.name.c_str(), access_call.c_str());
      }
      if (p.is_optional) {
        fprintf(out, "%*s}\n", indent-4, "");
      }
    }
    fprintf(out, "  }\n");

    fprintf(out, "  void Serialize(nlohmann::json *j) const {\n");
    for (const auto &e : o->extends) {
      fprintf(out, "    %s::Serialize(j);\n", e.c_str());
    }
    for (const auto&p : o->properties) {
      int indent = 4;
      if (p.is_optional) {
        fprintf(out, "%*sif (has_%s)", indent, "", p.name.c_str());
        indent = 1;
      }
      if (p.object_type == nullptr || p.is_array) {
        fprintf(out, "%*s(*j)[\"%s\"] = %s;\n",
                indent, "", p.name.c_str(), p.name.c_str());
      } else {
        fprintf(out, "%*s%s.Serialize(&(*j)[\"%s\"]);\n",
                indent, "", p.name.c_str(), p.name.c_str());
      }
    }
    fprintf(out, "  }\n");

    fprintf(out, "};\n"); // End of struct

    // functions that are picked up by the nlohmann::json serializer
    // We could generate template code once for all to_json/from_json that take
    // a T obj, but to limit method lookup confusion for other objects that
    // might interact with the json library, let's be explicit for each struct
    fprintf(out, "inline void to_json(nlohmann::json &j, const %s &obj) "
            "{ obj.Serialize(&j); }\n", o->name.c_str());
    fprintf(out, "inline void from_json(const nlohmann::json &j, %s &obj) "
            "{ obj.Deserialize(j); }\n\n", o->name.c_str());
  }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x58, %rsp
movq %rcx, %rbx
movq %rdx, %r14
movq %rsi, %r15
movq %rdi, %rdx
leaq 0xedde(%rip), %rsi      # 0x140f8
movq %rcx, %rdi
xorl %eax, %eax
callq 0x4400
leaq 0xedf1(%rip), %rdi      # 0x1411c
pushq $0x31
popq %rsi
pushq $0x1
popq %r12
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
leaq 0xee0a(%rip), %rsi      # 0x1414e
movq %rbx, %rdi
movq %r15, %rdx
xorl %eax, %eax
callq 0x4400
movq (%r14), %rax
movq 0x8(%r14), %rcx
movq %rcx, -0x58(%rbp)
movq %rax, %r14
leaq 0xee05(%rip), %r13      # 0x1416b
cmpq -0x58(%rbp), %r14
je 0x5858
movq (%r14), %rax
movq 0x10(%rax), %rdx
movq %rbx, %rdi
leaq 0xeddb(%rip), %rsi      # 0x1415c
xorl %eax, %eax
callq 0x4400
movq %r14, -0x30(%rbp)
movq (%r14), %rax
movq 0x30(%rax), %r14
movq 0x38(%rax), %r15
leaq 0xedc8(%rip), %rdi      # 0x14166
cmpq %r15, %r14
je 0x53c8
movq %rbx, %rsi
callq 0x4310
movq (%r14), %rdx
movq %rbx, %rdi
movq %r13, %rsi
xorl %eax, %eax
callq 0x4400
addq $0x20, %r14
leaq 0xeda3(%rip), %rdi      # 0x14169
jmp 0x539e
leaq 0xeec8(%rip), %rdi      # 0x14297
pushq $0x3
popq %rsi
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
movq -0x30(%rbp), %rax
movq (%rax), %rax
movq 0x48(%rax), %r13
movq 0x50(%rax), %r14
addq $0x60, %r13
leaq -0x60(%r13), %r15
cmpq %r14, %r15
je 0x5563
leaq -0x40(%rbp), %rax
movq %rax, -0x50(%rbp)
andq $0x0, -0x48(%rbp)
movb $0x0, -0x40(%rbp)
movq 0x20(%r13), %rsi
testq %rsi, %rsi
je 0x5426
addq $0x10, %rsi
leaq -0x50(%rbp), %rdi
callq 0x41b0
jmp 0x5497
movq %r13, %rdi
leaq 0xed4b(%rip), %rsi      # 0x1417b
callq 0x5a3c
leaq 0xed3a(%rip), %rsi      # 0x14176
testb %al, %al
jne 0x548e
movq %r13, %rdi
leaq 0xed38(%rip), %rsi      # 0x14182
callq 0x5a3c
leaq 0xf5e3(%rip), %rsi      # 0x14a39
testb %al, %al
jne 0x548e
movq %r13, %rdi
leaq 0xed26(%rip), %rsi      # 0x1418a
callq 0x5a3c
leaq 0xed21(%rip), %rsi      # 0x14191
testb %al, %al
jne 0x548e
movq %r13, %rdi
leaq 0xed22(%rip), %rsi      # 0x141a0
callq 0x5a3c
leaq 0xed1e(%rip), %rsi      # 0x141a8
testb %al, %al
je 0x5497
leaq -0x50(%rbp), %rdi
callq 0x4520
cmpq $0x0, -0x48(%rbp)
je 0x5516
movq -0x50(%rbp), %rdx
cmpb $0x0, -0x27(%r13)
movq -0x48(%r13), %rcx
leaq 0xed24(%rip), %rsi      # 0x141d6
leaq 0xed08(%rip), %rax      # 0x141c1
cmovneq %rax, %rsi
movq %rbx, %rdi
xorl %eax, %eax
callq 0x4400
cmpq $0x0, -0x18(%r13)
je 0x54e3
movq -0x20(%r13), %rdx
movq %rbx, %rdi
leaq 0xed02(%rip), %rsi      # 0x141de
xorl %eax, %eax
callq 0x4400
leaq 0xee8c(%rip), %rdi      # 0x14376
pushq $0x2
popq %rsi
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
cmpb $0x1, -0x28(%r13)
jne 0x554e
movq -0x48(%r13), %rdx
movq %rbx, %rdi
leaq 0xecd7(%rip), %rsi      # 0x141e4
xorl %eax, %eax
callq 0x4400
jmp 0x554e
movq 0x19ad3(%rip), %rdi     # 0x1eff0
movq %r15, %rsi
callq 0x5a4c
movq %rax, %rdi
leaq 0xec7e(%rip), %rsi      # 0x141ad
callq 0x42a0
movq %rax, %rdi
movq %r13, %rsi
callq 0x4280
movq %rax, %rdi
leaq 0xefbe(%rip), %rsi      # 0x14507
callq 0x42a0
leaq -0x50(%rbp), %rdi
callq 0x4160
addq $0x88, %r13
jmp 0x53f0
pushq $0xa
popq %rdi
movq %rbx, %rsi
callq 0x4370
leaq 0xec9d(%rip), %rdi      # 0x14212
pushq $0x2e
popq %rsi
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
movq -0x30(%rbp), %rax
movq (%rax), %rax
movq 0x30(%rax), %r14
movq 0x38(%rax), %r15
leaq 0xedac(%rip), %r13      # 0x14345
cmpq %r15, %r14
je 0x55b8
movq (%r14), %rdx
movq %rbx, %rdi
leaq 0xec96(%rip), %rsi      # 0x14241
xorl %eax, %eax
callq 0x4400
addq $0x20, %r14
jmp 0x5599
movq -0x30(%rbp), %rax
movq (%rax), %rax
movq 0x48(%rax), %r14
movq 0x50(%rax), %rax
movq %rax, -0x60(%rbp)
cmpq -0x60(%rbp), %r14
je 0x571a
leaq 0x18(%r14), %r15
leaq -0x80(%rbp), %r13
movq %r13, %rdi
leaq 0xec73(%rip), %rsi      # 0x1425a
movq %r15, %rdx
callq 0x5ab5
leaq -0x50(%rbp), %r12
movq %r12, %rdi
movq %r13, %rsi
leaq 0xec61(%rip), %rdx      # 0x14261
callq 0x5a8c
movq %r13, %rdi
callq 0x4160
movq %r13, %rdi
movq %r12, %rsi
leaq 0xf2ff(%rip), %rdx      # 0x14919
callq 0x5b2b
cmpb $0x1, 0x38(%r14)
pushq $0x4
popq %r13
jne 0x5689
movq (%r15), %r8
movq %rbx, %rdi
leaq 0xec2d(%rip), %rsi      # 0x14264
pushq $0x4
popq %rdx
leaq 0xeb1a(%rip), %r13      # 0x1415b
movq %r13, %rcx
xorl %eax, %eax
callq 0x4400
movq (%r15), %r8
movq %rbx, %rdi
leaq 0xec43(%rip), %rsi      # 0x1429b
pushq $0x8
popq %rdx
movq %r13, %rcx
xorl %eax, %eax
callq 0x4400
leaq -0x50(%rbp), %rdi
leaq 0xec3e(%rip), %rsi      # 0x142ae
callq 0x4520
leaq -0x80(%rbp), %rdi
leaq 0xec35(%rip), %rsi      # 0x142b5
callq 0x4520
pushq $0x8
popq %r13
cmpq $0x0, 0x80(%r14)
je 0x569a
cmpb $0x1, 0x39(%r14)
jne 0x56ad
movq -0x80(%rbp), %r8
movq (%r15), %r9
movq %rbx, %rdi
leaq 0xec12(%rip), %rsi      # 0x142bd
jmp 0x56be
movq (%r15), %r8
movq -0x50(%rbp), %r9
movq %rbx, %rdi
leaq 0xec11(%rip), %rsi      # 0x142cf
movl %r13d, %edx
leaq 0xea93(%rip), %rcx      # 0x1415b
xorl %eax, %eax
callq 0x4400
cmpb $0x1, 0x38(%r14)
jne 0x56f5
addl $-0x4, %r13d
movq %rbx, %rdi
leaq 0xec03(%rip), %rsi      # 0x142e7
movl %r13d, %edx
leaq 0xea6d(%rip), %rcx      # 0x1415b
xorl %eax, %eax
callq 0x4400
leaq -0x80(%rbp), %rdi
callq 0x4160
leaq -0x50(%rbp), %rdi
callq 0x4160
addq $0x88, %r14
leaq 0xec30(%rip), %r13      # 0x14345
jmp 0x55cb
leaq 0xebcc(%rip), %rdi      # 0x142ed
pushq $0x4
popq %rsi
pushq $0x1
popq %r12
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
leaq 0xebb8(%rip), %rdi      # 0x142f2
pushq $0x2c
popq %rsi
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
movq -0x30(%rbp), %rax
movq (%rax), %rax
movq 0x30(%rax), %r14
movq 0x38(%rax), %r15
cmpq %r15, %r14
je 0x5776
movq (%r14), %rdx
movq %rbx, %rdi
leaq 0xebb6(%rip), %rsi      # 0x1431f
xorl %eax, %eax
callq 0x4400
addq $0x20, %r14
jmp 0x5757
movq -0x30(%rbp), %rax
movq (%rax), %rax
movq 0x48(%rax), %r14
movq 0x50(%rax), %r15
cmpq %r15, %r14
je 0x57f1
cmpb $0x1, 0x38(%r14)
pushq $0x4
popq %rdx
jne 0x57b3
movq 0x18(%r14), %r8
movq %rbx, %rdi
leaq 0xeb94(%rip), %rsi      # 0x14336
leaq 0xe9b2(%rip), %rcx      # 0x1415b
xorl %eax, %eax
callq 0x4400
pushq $0x1
popq %rdx
cmpq $0x0, 0x80(%r14)
movq %r13, %rsi
je 0x57d0
cmpb $0x0, 0x39(%r14)
leaq 0xeb8e(%rip), %rsi      # 0x1435a
cmovneq %r13, %rsi
movq 0x18(%r14), %r8
movq %rbx, %rdi
leaq 0xe97d(%rip), %rcx      # 0x1415b
movq %r8, %r9
xorl %eax, %eax
callq 0x4400
addq $0x88, %r14
jmp 0x5785
leaq 0xeaf5(%rip), %rdi      # 0x142ed
pushq $0x4
popq %rsi
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
leaq 0xeb6c(%rip), %rdi      # 0x14379
pushq $0x3
popq %rsi
movq %r12, %rdx
movq %rbx, %rcx
callq 0x4530
movq -0x30(%rbp), %r14
movq (%r14), %rax
movq 0x10(%rax), %rdx
movq %rbx, %rdi
leaq 0xeb4d(%rip), %rsi      # 0x1437d
xorl %eax, %eax
callq 0x4400
movq (%r14), %rax
movq 0x10(%rax), %rdx
movq %rbx, %rdi
leaq 0xeb83(%rip), %rsi      # 0x143cb
xorl %eax, %eax
callq 0x4400
addq $0x8, %r14
jmp 0x535f
addq $0x58, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
leaq -0x80(%rbp), %rdi
jmp 0x5887
jmp 0x5880
movq %rax, %rbx
leaq -0x80(%rbp), %rdi
callq 0x4160
jmp 0x5883
movq %rax, %rbx
leaq -0x50(%rbp), %rdi
callq 0x4160
movq %rbx, %rdi
callq 0x44a0