private static void processAccessors(
      JFieldVar fieldVar, JDefinedClass packetClass, boolean fromClient) {
    String name = WordUtils.capitalize(fieldVar.name());

    if (fromClient) {
      String methodName = "get" + name;
      JMethod getter = packetClass.method(JMod.PUBLIC, fieldVar.type(), methodName);
      getter.body()._return(fieldVar);
    } else {
      String methodName = "set" + name;
      JMethod setter = packetClass.method(JMod.PUBLIC, Void.TYPE, methodName);
      setter.param(fieldVar.type(), fieldVar.name());
      setter.body().assign(JExpr._this().ref(fieldVar.name()), JExpr.ref(fieldVar.name()));
    }
  }
  private static void processToString(JDefinedClass packetClass, JCodeModel codeModel) {
    JClass string = codeModel.ref(String.class);
    JClass stringBuilder = codeModel.ref(StringBuilder.class);
    JClass arrays = codeModel.ref(Arrays.class);

    JMethod toStringMeth = packetClass.method(JMod.PUBLIC, String.class, "toString");
    toStringMeth.annotate(Override.class);
    JBlock body = toStringMeth.body();

    JVar stringBuilderVar = body.decl(stringBuilder, "sb");
    stringBuilderVar =
        stringBuilderVar.init(JExpr._new(stringBuilder).arg(packetClass.name() + "["));

    JInvocation appendChain = null;

    for (JFieldVar fieldVar : packetClass.fields().values()) {
      if (appendChain != null) {
        // a comma is needed
        appendChain = appendChain.invoke("append").arg("," + fieldVar.name() + "=");
      } else {
        appendChain = stringBuilderVar.invoke("append").arg(fieldVar.name() + "=");
      }

      // now add the field to the toString output
      JExpression expression =
          fieldVar.type().isArray()
              ? arrays.staticInvoke("toString").arg(JExpr._this().ref(fieldVar.name()))
              : fieldVar.type().isReference()
                  ? JExpr._this().ref(fieldVar.name()).invoke("toString")
                  : JExpr._this().ref(fieldVar.name());

      appendChain = appendChain.invoke("append").arg(expression);
    }

    if (appendChain != null) {
      appendChain = appendChain.invoke("append").arg("]");
    } else {
      appendChain = stringBuilderVar.invoke("append").arg("]");
    }

    body.add(appendChain);
    body._return(stringBuilderVar.invoke("toString"));
  }
  private static void processPacket(
      PacketType packet, JPackage dirPackage, JCodeModel codeModel, boolean fromClient)
      throws JClassAlreadyExistsException {
    // get the packet info
    String packetName =
        (packet.getInfo() == null || packet.getInfo().getName() == null)
            ? "Unknown"
            : packet.getInfo().getName();
    String packetNamePrefixed = "P" + String.format("%03d", packet.getHeader()) + "_" + packetName;
    String packetDescription =
        (packet.getInfo() == null
                || packet.getInfo().getDescription() == null
                || packet.getInfo().getDescription().isEmpty())
            ? ""
            : "\n" + WordUtils.wrap(packet.getInfo().getDescription(), /* maximumLength */ 50);

    JDefinedClass packetClass =
        dirPackage._class(JMod.FINAL | JMod.PUBLIC, packetNamePrefixed)._extends(GWMessage.class);

    LOGGER.info("+-Processing packet: {}", packetNamePrefixed);
    LOGGER.debug("|+-Packet description: {}", packetDescription);

    StringBuilder packetJavadoc =
        new StringBuilder("Auto-generated by PacketCodeGen.").append(packetDescription);

    JDocComment jDocComment = packetClass.javadoc();
    jDocComment.add(packetJavadoc.toString());

    AtomicInteger numberOfUnknowns = new AtomicInteger(); // unknown field number

    // get all fields in this packet
    for (FieldType field : packet.getField()) {
      processField(field, packetClass, codeModel, numberOfUnknowns, fromClient);
    }

    // generate the header method
    JMethod headerMeth = packetClass.method(JMod.PUBLIC, short.class, "getHeader");
    headerMeth.annotate(Override.class);
    headerMeth.body()._return(JExpr.lit(packet.getHeader().intValue()));

    // generate getters, setters
    for (JFieldVar fieldVar : packetClass.fields().values()) {
      processAccessors(fieldVar, packetClass, fromClient);
    }

    // generate the toString method
    processToString(packetClass, codeModel);
  }