content: package im.actor.generator.generators;
  
  import im.actor.generator.FileGenerator;
  import im.actor.generator.scheme.*;
  
  import java.io.IOException;
  
  /**
   * Created by ex3ndr on 15.11.14.
   */
  public class ProtoBufGenerator {
      public static void generate(SchemeDefinition definition, String fileName) throws IOException {
          FileGenerator generator = new FileGenerator(fileName);
          for (SchemeSection section : definition.getSections()) {
              generator.appendLn("//////////////////////////////////////////////////////");
              generator.appendLn("// " + section.getName());
              generator.appendLn("//////////////////////////////////////////////////////");
              for (String s : section.getDocs()) {
                  generator.appendLn("//" + s);
              }
              generator.appendLn();
              for (SchemeRecord record : section.getRecords()) {
                  if (record instanceof SchemeEnum) {
                      SchemeEnum sEnum = (SchemeEnum) record;
                      generator.appendLn("enum " + sEnum.getName() + " {");
                      generator.increaseDepth();
                      for (SchemeEnum.Record enrec : sEnum.getRecord()) {
                          generator.appendLn(enrec.getName().toUpperCase() + " = " + enrec.getId() + ";");
                      }
                      generator.decreaseDepth();
                      generator.appendLn("}");
                      generator.appendLn();
                  } else if (record instanceof SchemeStruct) {
                      SchemeStruct sEnum = (SchemeStruct) record;
                      for (SchemeDoc d : sEnum.getDocs()) {
                          if (d instanceof SchemeDocComment) {
                              generator.appendLn("// " + ((SchemeDocComment) d).getText());
                          } else if (d instanceof SchemeDocParameter) {
                              generator.appendLn("// " + ((SchemeDocParameter) d).getArgument() + " " + ((SchemeDocParameter) d).getDescription());
                          }
                      }
                      generator.appendLn("message " + sEnum.getName() + " {");
                      generator.increaseDepth();
                      appendContents(definition, sEnum, generator);
                      generator.decreaseDepth();
                      generator.appendLn("}");
                      generator.appendLn();
                  } else if (record instanceof SchemeUpdate) {
                      SchemeUpdate update = (SchemeUpdate) record;
                      generator.appendLn("// API#0x" + toHex(update.getHeader()));
                      for (SchemeDoc d : update.getDocs()) {
                          if (d instanceof SchemeDocComment) {
                              generator.appendLn("// " + ((SchemeDocComment) d).getText());
                          } else if (d instanceof SchemeDocParameter) {
                              generator.appendLn("// " + ((SchemeDocParameter) d).getArgument() + " " + ((SchemeDocParameter) d).getDescription());
                          }
                      }
                      generator.appendLn("message Update" + update.getName() + " {");
                      generator.increaseDepth();
                      appendContents(definition, update, generator);
                      generator.decreaseDepth();
                      generator.appendLn("}");
                      generator.appendLn();
                  } else if (record instanceof SchemeUpdateBox) {
                      SchemeUpdateBox update = (SchemeUpdateBox) record;
                      generator.appendLn("// API#0x" + toHex(update.getHeader()));
                      for (SchemeDoc d : update.getDocs()) {
                          if (d instanceof SchemeDocComment) {
                              generator.appendLn("// " + ((SchemeDocComment) d).getText());
                          } else if (d instanceof SchemeDocParameter) {
                              generator.appendLn("// " + ((SchemeDocParameter) d).getArgument() + " " + ((SchemeDocParameter) d).getDescription());
                          }
                      }
                      generator.appendLn("message Update" + update.getName() + " {");
                      generator.increaseDepth();
                      appendContents(definition, update, generator);
                      generator.decreaseDepth();
                      generator.appendLn("}");
                      generator.appendLn();
                  } else if (record instanceof SchemeResponse) {
                      SchemeResponse update = (SchemeResponse) record;
                      generator.appendLn("// API#0x" + toHex(update.getHeader()));
                      for (SchemeDoc d : update.getDocs()) {
                          if (d instanceof SchemeDocComment) {
                              generator.appendLn("// " + ((SchemeDocComment) d).getText());
                          } else if (d instanceof SchemeDocParameter) {
                              generator.appendLn("// " + ((SchemeDocParameter) d).getArgument() + " " + ((SchemeDocParameter) d).getDescription());
                          }
                      }
                      generator.appendLn("message Response" + update.getName() + " {");
                      generator.increaseDepth();
                      appendContents(definition, update, generator);
                      generator.decreaseDepth();
                      generator.appendLn("}");
                      generator.appendLn();
                  } else if (record instanceof SchemeRpc) {
                      SchemeRpc rpc = (SchemeRpc) record;
                      generator.appendLn("// API#0x" + toHex(rpc.getHeader()));
                      if (rpc.getResponse() instanceof SchemeRpc.RefResponse) {
                          SchemeRpc.RefResponse resp = (SchemeRpc.RefResponse) rpc.getResponse();
                          generator.appendLn("// Response" + resp.getName());
                      } else {
                          generator.appendLn("// " + responseName(rpc.getName()));
                      }
                      for (SchemeDoc d : rpc.getDocs()) {
                          if (d instanceof SchemeDocComment) {
                              generator.appendLn("// " + ((SchemeDocComment) d).getText());
                          } else if (d instanceof SchemeDocParameter) {
                              generator.appendLn("// " + ((SchemeDocParameter) d).getArgument() + " " + ((SchemeDocParameter) d).getDescription());
                          }
                      }
                      generator.appendLn("message " + requestName(rpc.getName()) + " {");
                      generator.increaseDepth();
                      appendContents(definition, rpc, generator);
                      generator.decreaseDepth();
                      generator.appendLn("}");
                      generator.appendLn();
  
                      if (rpc.getResponse() instanceof SchemeRpc.AnonymousResponse) {
                          SchemeRpc.AnonymousResponse resp = (SchemeRpc.AnonymousResponse) rpc.getResponse();
                          generator.appendLn("// API#0x" + toHex(resp.getResponse().getHeader()));
                          generator.appendLn("message " + responseName(rpc.getName()) + " {");
                          generator.increaseDepth();
                          appendContents(definition, resp.getResponse(), generator);
                          generator.decreaseDepth();
                          generator.appendLn("}");
                          generator.appendLn();
                      }
                  }
              }
          }
          generator.close();
      }
  
  
      private static void appendContents(SchemeDefinition definition, SchemeContainer container, FileGenerator generator) throws IOException {
          for (SchemeAttribute enrec : container.getAttributes()) {
              generator.appendLn(getType(definition, enrec.getType(), true) + " " + enrec.getName() + " = " + enrec.getId() + ";");
          }
      }
  
      private static String getType(SchemeDefinition definition, SchemeType attribute, boolean isFirst) throws IOException {
          if (attribute instanceof SchemePrimitiveType) {
              return (isFirst ? "required " : "") + ((SchemePrimitiveType) attribute).getName();
          } else if (attribute instanceof SchemeOptionalType) {
              return "optional " + getType(definition, ((SchemeOptionalType) attribute).getType(), false);
          } else if (attribute instanceof SchemeListType) {
              return "repeated " + getType(definition, ((SchemeListType) attribute).getType(), false);
          } else if (attribute instanceof SchemeStructType) {
              return (isFirst ? "required " : "") + ((SchemeStructType) attribute).getType();
          } else if (attribute instanceof SchemeEnumType) {
              return (isFirst ? "required " : "") + ((SchemeEnumType) attribute).getName();
          } else if (attribute instanceof SchemeAliasType) {
              return getType(definition, definition.getAliases().get(((SchemeAliasType) attribute).getName()), isFirst);
          } else if (attribute instanceof SchemeTraitType) {
              return (isFirst ? "required bytes" : "bytes");
          } else {
              throw new IOException();
          }
      }
  
      private static String toHex(int val) {
          String res = Integer.toHexString(val);
          if (res.length() < 2) {
              res = "0" + res;
          }
          return res.toUpperCase();
      }
  
      private static String requestName(String name) {
          if (name.startsWith("Request")) {
              return name;
          } else {
              return "Request" + name;
          }
      }
  
      private static String responseName(String name) {
          if (name.startsWith("Request")) {
              return "Response" + name.substring("Request".length());
          } else {
              return "Response" + name;
          }
      }
  }
  