// Source code is decompiled from a .class file using FernFlower decompiler.
package re3lib;

import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class DataTypeWriter {
  public HashSet<String> blacklistedTypes;

  private static String[] INTEGRAL_TYPES = new String[] { "char", "short", "int", "long", "long long", "__int64",
      "float", "double", "long double", "void" };
  private static String[] INTEGRAL_MODIFIERS = new String[] { "signed", "unsigned", "const", "static", "volatile",
      "mutable" };
  private static String EOL = System.getProperty("line.separator");
  private Set<DataType> resolved;
  private Map<String, DataType> resolvedTypeMap;
  private Set<Composite> deferredCompositeDeclarations;
  private ArrayDeque<DataType> deferredTypeFIFO;
  private Set<DataType> deferredTypes;
  private int writerDepth;
  private Writer writer;
  private DataTypeManager dtm;
  private DataOrganization dataOrganization;
  private AnnotationHandler annotator;
  private boolean cppStyleComments;

  public DataTypeWriter(DataTypeManager dtm, Writer writer) throws IOException {
    this(dtm, writer, new DefaultAnnotationHandler());
  }

  public DataTypeWriter(DataTypeManager dtm, Writer writer, boolean cppStyleComments) throws IOException {
    this(dtm, writer, new DefaultAnnotationHandler(), cppStyleComments);
  }

  public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator) throws IOException {
    this(dtm, writer, annotator, false);
  }

  public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator, boolean cppStyleComments)
      throws IOException {
    this.blacklistedTypes = new HashSet<>();
    this.resolved = new HashSet();
    this.resolvedTypeMap = new HashMap();
    this.deferredCompositeDeclarations = new HashSet();
    this.deferredTypeFIFO = new ArrayDeque();
    this.deferredTypes = new HashSet();
    this.writerDepth = 0;
    this.cppStyleComments = false;
    this.dtm = dtm;
    if (dtm != null) {
      this.dataOrganization = dtm.getDataOrganization();
    }

    if (this.dataOrganization == null) {
      this.dataOrganization = DataOrganizationImpl.getDefaultOrganization();
    }

    this.writer = writer;
    this.annotator = annotator;
    this.cppStyleComments = cppStyleComments;
    if (dtm != null) {
      // this.writeBuiltInDeclarations(dtm);
    }
  }

  private String comment(String text) {
    if (text == null) {
      return "";
    } else {
      return this.cppStyleComments ? "// " + text : "/* " + text + " */";
    }
  }

  public void write(DataTypeManager dataTypeManager, TaskMonitor monitor) throws IOException, CancelledException {
    this.write(dataTypeManager.getRootCategory(), monitor);
  }

  public void write(Category category, TaskMonitor monitor) throws IOException, CancelledException {
    DataType[] dataTypes = category.getDataTypes();
    this.write(dataTypes, monitor);
    Category[] subCategories = category.getCategories();
    Category[] var5 = subCategories;
    int var6 = subCategories.length;

    for (int var7 = 0; var7 < var6; ++var7) {
      Category subCategory = var5[var7];
      if (monitor.isCancelled()) {
        return;
      }

      this.write(subCategory, monitor);
    }

  }

  public void write(DataType[] dataTypes, TaskMonitor monitor) throws IOException, CancelledException {
    monitor.initialize((long) dataTypes.length);
    int cnt = 0;
    DataType[] var4 = dataTypes;
    int var5 = dataTypes.length;

    for (int var6 = 0; var6 < var5; ++var6) {
      DataType dataType = var4[var6];
      monitor.checkCancelled();
      this.write(dataType, monitor);
      ++cnt;
      monitor.setProgress((long) cnt);
    }

  }

  public void write(List<DataType> dataTypes, TaskMonitor monitor) throws IOException, CancelledException {
    this.write(dataTypes, monitor, true);
  }

  public void write(List<DataType> dataTypes, TaskMonitor monitor, boolean throwExceptionOnInvalidType)
      throws IOException, CancelledException {
    monitor.initialize((long) dataTypes.size());
    int cnt = 0;
    Iterator var5 = dataTypes.iterator();

    while (var5.hasNext()) {
      DataType dataType = (DataType) var5.next();
      monitor.checkCancelled();
      this.write(dataType, monitor, throwExceptionOnInvalidType);
      ++cnt;
      monitor.setProgress((long) cnt);
    }

  }

  private void deferWrite(DataType dt) {
    if (!this.resolved.contains(dt) && !this.deferredTypes.contains(dt)) {
      this.deferredTypes.add(dt);
      this.deferredTypeFIFO.addLast(dt);
    }

  }

  void write(DataType dt, TaskMonitor monitor) throws IOException, CancelledException {
    this.doWrite(dt, monitor, true);
  }

  void write(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType)
      throws IOException, CancelledException {
    this.doWrite(dt, monitor, throwExceptionOnInvalidType);
  }

  private void doWrite(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType)
      throws IOException, CancelledException {
    if (dt != null) {
      if (blacklistedTypes.contains(dt.getDisplayName()))
        return;
      if (!(dt instanceof FunctionDefinition)) {
        if (dt instanceof FactoryDataType) {
          IllegalArgumentException iae = new IllegalArgumentException("Factory data types may not be written");
          if (throwExceptionOnInvalidType) {
            throw iae;
          }

          Msg.error(this, "Factory data types may not be written - type: " + dt);
        }

        if (!(dt instanceof Pointer) && !(dt instanceof Array) && !(dt instanceof BitFieldDataType)) {
          dt = dt.clone(this.dtm);
          if (!this.resolved.contains(dt)) {
            this.resolved.add(dt);
            DataType resolvedType = (DataType) this.resolvedTypeMap.get(dt.getName());
            if (resolvedType != null) {
              if (!resolvedType.isEquivalent(dt)) {
                if (dt instanceof TypeDef) {
                  DataType baseType = ((TypeDef) dt).getBaseDataType();
                  if ((resolvedType instanceof Composite || resolvedType instanceof Enum)
                      && baseType.isEquivalent(resolvedType)) {
                    return;
                  }
                }

                this.writer.write(EOL);
                Writer var10000 = this.writer;
                String var10002 = dt.getPathName();
                var10000.write(this
                    .comment("WARNING! conflicting data type names: " + var10002 + " - " + resolvedType.getPathName()));
                this.writer.write(EOL);
                this.writer.write(EOL);
              }
            } else {
              this.resolvedTypeMap.put(dt.getName(), dt);
              ++this.writerDepth;
              if (dt.equals(DataType.DEFAULT)) {
                this.writer.write("typedef unsigned char   " + DataType.DEFAULT.getName() + ";");
                this.writer.write(EOL);
                this.writer.write(EOL);
              } else if (dt instanceof Dynamic) {
                this.writeDynamicBuiltIn((Dynamic) dt, monitor);
              } else if (dt instanceof Structure) {
                Structure struct = (Structure) dt;
                this.writeCompositePreDeclaration(struct, monitor);
                this.deferredCompositeDeclarations.add(struct);
              } else if (dt instanceof Union) {
                Union union = (Union) dt;
                this.writeCompositePreDeclaration(union, monitor);
                this.deferredCompositeDeclarations.add(union);
              } else if (dt instanceof Enum) {
                this.writeEnum((Enum) dt, monitor);
              } else if (dt instanceof TypeDef) {
                this.writeTypeDef((TypeDef) dt, monitor);
              } else if (dt instanceof BuiltInDataType) {
                this.writeBuiltIn((BuiltInDataType) dt, monitor);
              } else if (!(dt instanceof BitFieldDataType)) {
                this.writer.write(EOL);
                this.writer.write(EOL);
                this.writer.write(this.comment("Unable to write datatype. Type unrecognized: " + dt.getClass()));
                this.writer.write(EOL);
                this.writer.write(EOL);
              }

              if (this.writerDepth == 1) {
                this.writeDeferredDeclarations(monitor);
              }

              --this.writerDepth;
            }
          }
        } else {
          this.write(this.getBaseDataType(dt), monitor);
        }
      }
    }
  }

  private void writeDeferredDeclarations(TaskMonitor monitor) throws IOException, CancelledException {
    while (!this.deferredTypes.isEmpty()) {
      DataType dt = (DataType) this.deferredTypeFIFO.removeFirst();
      this.deferredTypes.remove(dt);
      this.write(dt, monitor);
    }

    this.writeDeferredCompositeDeclarations(monitor);
    this.deferredCompositeDeclarations.clear();
  }

  private DataType getBaseArrayTypedefType(DataType dt) {
    while (true) {
      if (dt != null) {
        if (dt instanceof TypeDef) {
          dt = ((TypeDef) dt).getBaseDataType();
          continue;
        }

        if (dt instanceof Array) {
          dt = ((Array) dt).getDataType();
          continue;
        }
      }

      return dt;
    }
  }

  private boolean containsComposite(Composite container, Composite contained) {
    DataTypeComponent[] var3 = container.getDefinedComponents();
    int var4 = var3.length;

    for (int var5 = 0; var5 < var4; ++var5) {
      DataTypeComponent component = var3[var5];
      DataType dt = this.getBaseArrayTypedefType(component.getDataType());
      if (dt instanceof Composite && dt.getName().equals(contained.getName()) && dt.isEquivalent(contained)) {
        return true;
      }
    }

    return false;
  }

  private void writeDeferredCompositeDeclarations(TaskMonitor monitor) throws IOException, CancelledException {
    int cnt = this.deferredCompositeDeclarations.size();
    if (cnt != 0) {
      LinkedList<Composite> list = new LinkedList(this.deferredCompositeDeclarations);
      if (list.size() > 1) {
        int sortChange = 1;

        while (sortChange != 0) {
          sortChange = 0;

          for (int i = cnt - 1; i > 0; --i) {
            if (this.resortComposites(list, i)) {
              ++sortChange;
            }
          }
        }
      }

      Iterator var6 = list.iterator();

      while (var6.hasNext()) {
        Composite composite = (Composite) var6.next();
        this.writeCompositeBody(composite, monitor);
      }

    }
  }

  private boolean resortComposites(List<Composite> list, int index) {
    int listSize = list.size();
    if (listSize <= 0) {
      return false;
    } else {
      Composite composite = (Composite) list.get(index);

      for (int i = 0; i < index; ++i) {
        Composite other = (Composite) list.get(i);
        if (this.containsComposite(other, composite)) {
          list.remove(index);
          list.add(i, composite);
          composite = null;
          return true;
        }
      }

      return false;
    }
  }

  private String getDynamicComponentString(Dynamic dynamicType, String fieldName, int length) {
    if (dynamicType.canSpecifyLength()) {
      DataType replacementBaseType = dynamicType.getReplacementBaseType();
      if (replacementBaseType != null) {
        replacementBaseType = replacementBaseType.clone(this.dtm);
        int elementLen = replacementBaseType.getLength();
        if (elementLen > 0) {
          int elementCnt = (length + elementLen - 1) / elementLen;
          return replacementBaseType.getDisplayName() + " " + fieldName + "[" + elementCnt + "]";
        }

        String var10001 = dynamicType.getClass().getSimpleName();
        Msg.error(this,
            var10001 + " returned bad replacementBaseType: " + replacementBaseType.getClass().getSimpleName());
      }
    }

    return null;
  }

  private void writeCompositePreDeclaration(Composite composite, TaskMonitor monitor)
      throws IOException, CancelledException {
    String compositeType = composite instanceof Structure ? "struct" : "union";
    this.writer.write("typedef " + compositeType + " " + composite.getDisplayName() + " " + composite.getDisplayName()
        + ", *P" + composite.getDisplayName() + ";");
    this.writer.write(EOL);
    this.writer.write(EOL);
    DataTypeComponent[] var4 = composite.getComponents();
    int var5 = var4.length;

    for (int var6 = 0; var6 < var5; ++var6) {
      DataTypeComponent component = var4[var6];
      if (monitor.isCancelled()) {
        break;
      }

      DataType componentType = component.getDataType();
      this.deferWrite(componentType);
      this.getTypeDeclaration((String) null, componentType, component.getLength(), true, monitor);
    }

  }

  private void writeCompositeBody(Composite composite, TaskMonitor monitor) throws IOException, CancelledException {
    String compositeType = composite instanceof Structure ? "struct" : "union";
    StringBuilder sb = new StringBuilder();
    sb.append(compositeType + " " + composite.getDisplayName() + " {");
    String descrip = composite.getDescription();
    if (descrip != null && descrip.length() > 0) {
      String var10001 = this.comment(descrip);
      sb.append(" " + var10001);
    }

    sb.append(EOL);
    DataTypeComponent[] var6 = composite.getComponents();
    int var7 = var6.length;

    for (int var8 = 0; var8 < var7; ++var8) {
      DataTypeComponent component = var6[var8];
      monitor.checkCancelled();
      this.writeComponent(component, composite, sb, monitor);
    }

    sb.append(this.annotator.getSuffix(composite, (DataTypeComponent) null));
    sb.append("};");
    this.writer.write(sb.toString());
    this.writer.write(EOL);
    this.writer.write(EOL);
  }

  private void writeComponent(DataTypeComponent component, Composite composite, StringBuilder sb, TaskMonitor monitor)
      throws IOException, CancelledException {
    sb.append("    ");
    sb.append(this.annotator.getPrefix(composite, component));
    String fieldName = component.getFieldName();
    if (fieldName == null || fieldName.length() == 0) {
      fieldName = component.getDefaultFieldName();
    }

    DataType componentDataType = component.getDataType();
    sb.append(this.getTypeDeclaration(fieldName, componentDataType, component.getLength(), false, monitor));
    sb.append(";");
    sb.append(this.annotator.getSuffix(composite, component));
    String comment = component.getComment();
    if (comment != null && comment.length() > 0) {
      String var10001 = this.comment(comment);
      sb.append(" " + var10001);
    }

    sb.append(EOL);
  }

  private String getTypeDeclaration(String name, DataType dataType, int instanceLength, boolean writeEnabled,
      TaskMonitor monitor) throws IOException, CancelledException {
    if (name == null) {
      name = "";
    }

    StringBuilder sb = new StringBuilder();
    String componentString = null;
    if (dataType instanceof Dynamic) {
      componentString = this.getDynamicComponentString((Dynamic) dataType, name, instanceLength);
      if (componentString != null) {
        sb.append(componentString);
      } else {
        sb.append(this.comment("ignoring dynamic datatype inside composite: " + dataType.getDisplayName()));
        sb.append(EOL);
      }
    }

    if (componentString == null) {
      if (dataType instanceof BitFieldDataType) {
        BitFieldDataType bfDt = (BitFieldDataType) dataType;
        name = name + ":" + bfDt.getDeclaredBitSize();
        dataType = bfDt.getBaseDataType();
      }

      label44: while (true) {
        while (!(dataType instanceof Array)) {
          if (!(dataType instanceof Pointer)) {
            break label44;
          }

          Pointer pointer = (Pointer) dataType;
          DataType elem = pointer.getDataType();
          if (elem == null) {
            break label44;
          }

          name = "*" + name;
          dataType = elem;
          if (elem instanceof Array) {
            name = "(" + name + ")";
          }
        }

        Array array = (Array) dataType;
        name = name + "[" + array.getNumElements() + "]";
        dataType = array.getDataType();
      }

      DataType baseDataType = this.getBaseDataType(dataType);
      if (baseDataType instanceof FunctionDefinition) {
        componentString = this.getFunctionPointerString((FunctionDefinition) baseDataType, name, dataType, writeEnabled,
            monitor);
      } else {
        String var10000 = this.getDataTypePrefix(dataType);
        componentString = var10000 + dataType.getDisplayName();
        if (name.length() != 0) {
          componentString = componentString + " " + name;
        }
      }

      sb.append(componentString);
    }

    return sb.toString();
  }

  private String getDataTypePrefix(DataType dataType) {
    dataType = this.getBaseDataType(dataType);
    if (dataType instanceof Structure) {
      return "struct ";
    } else if (dataType instanceof Union) {
      return "union ";
    } else {
      return dataType instanceof Enum ? "enum " : "";
    }
  }

  private void writeEnum(Enum enumm, TaskMonitor monitor) throws IOException {
    String enumName = enumm.getDisplayName();
    Writer var10000;
    String var10001;
    if (enumName.startsWith("define_") && enumName.length() > 7 && enumm.getCount() == 1) {
      long val = enumm.getValues()[0];
      var10000 = this.writer;
      var10001 = enumName.substring(7);
      var10000.append("#define " + var10001 + " " + Long.toString(val));
      this.writer.write(EOL);
      this.writer.write(EOL);
    } else {
      this.writer.write("enum " + enumName + " {");
      String description = enumm.getDescription();
      if (description != null && description.length() != 0) {
        var10000 = this.writer;
        var10001 = this.comment(description);
        var10000.write(" " + var10001);
      }

      this.writer.write(EOL);
      String[] names = enumm.getNames();

      for (int j = 0; j < names.length; ++j) {
        this.writer.write("    ");
        this.writer.write(this.annotator.getPrefix(enumm, names[j]));
        this.writer.write(names[j]);
        this.writer.write("=");
        this.writer.write(Long.toString(enumm.getValue(names[j])));
        String comment = enumm.getComment(names[j]);
        if (!comment.isBlank()) {
          var10000 = this.writer;
          var10001 = this.comment(comment);
          var10000.write(" " + var10001);
        }

        this.writer.write(this.annotator.getSuffix(enumm, names[j]));
        if (j < names.length - 1) {
          this.writer.write(",");
        }

        this.writer.write(EOL);
      }

      this.writer.write("};");
      this.writer.write(EOL);
      this.writer.write(EOL);
    }
  }

  private void writeTypeDef(TypeDef typeDef, TaskMonitor monitor) throws IOException, CancelledException {
    String typedefName = typeDef.getDisplayName();
    DataType dataType = typeDef.getDataType();
    String dataTypeName = dataType.getDisplayName();
    if (!this.isIntegral(typedefName, dataTypeName)) {
      DataType baseType = typeDef.getBaseDataType();

      label125: {
        try {
          if (!(baseType instanceof Composite) && !(baseType instanceof Enum)) {
            if (!(baseType instanceof Pointer) || !typedefName.startsWith("P")) {
              break label125;
            }

            DataType dt = ((Pointer) baseType).getDataType();
            if (dt instanceof TypeDef) {
              dt = ((TypeDef) dt).getBaseDataType();
            }

            if (!(dt instanceof Composite) || !dt.getName().equals(typedefName.substring(1))) {
              break label125;
            }

            this.resolvedTypeMap.remove(typedefName);
            return;
          }

          if (!typedefName.equals(baseType.getName())) {
            break label125;
          }

          this.resolvedTypeMap.remove(typedefName);
        } finally {
          this.write(dataType, monitor);
        }

        return;
      }

      if (baseType instanceof Array && this.getBaseArrayTypedefType(baseType) instanceof Composite) {
        this.writeDeferredDeclarations(monitor);
      }

      String typedefString = this.getTypeDeclaration(typedefName, dataType, -1, true, monitor);
      this.writer.write("typedef " + typedefString + ";");
      this.writer.write(EOL);
      this.writer.write(EOL);
    }
  }

  private boolean isIntegral(String typedefName, String basetypeName) {
    String[] var3 = INTEGRAL_TYPES;
    int var4 = var3.length;

    int var5;
    for (var5 = 0; var5 < var4; ++var5) {
      String type = var3[var5];
      if (typedefName.equals(type)) {
        return true;
      }
    }

    boolean endsWithIntegralType = false;
    String[] var10 = INTEGRAL_TYPES;
    var5 = var10.length;

    int var13;
    for (var13 = 0; var13 < var5; ++var13) {
      String type = var10[var13];
      if (typedefName.endsWith(" " + type)) {
        endsWithIntegralType = true;
        break;
      }
    }

    boolean containsIntegralModifier = false;
    String[] var12 = INTEGRAL_MODIFIERS;
    var13 = var12.length;

    for (int var14 = 0; var14 < var13; ++var14) {
      String modifier = var12[var14];
      if (typedefName.indexOf(modifier + " ") >= 0 || typedefName.indexOf(" " + modifier) >= 0) {
        return true;
      }
    }

    if (endsWithIntegralType && containsIntegralModifier) {
      return true;
    } else if (typedefName.endsWith(" " + basetypeName)) {
      return containsIntegralModifier;
    } else {
      return false;
    }
  }

  private void writeDynamicBuiltIn(Dynamic dt, TaskMonitor monitor) throws IOException, CancelledException {
    DataType baseDt = dt.getReplacementBaseType();
    if (baseDt != null) {
      this.write(baseDt, monitor);
    }

  }

  private void writeBuiltIn(BuiltInDataType dt, TaskMonitor monitor) throws IOException {
    String declaration = dt.getCTypeDeclaration(this.dataOrganization);
    if (declaration != null) {
      if (dt.getDisplayName() == "bool")
        return;
      this.writer.write(declaration);
      this.writer.write(EOL);
    }
  }

  private void writeBuiltInDeclarations(DataTypeManager manager) throws IOException {
    try {
      this.write(DataType.DEFAULT, TaskMonitor.DUMMY);
      SourceArchive builtInArchive = manager.getSourceArchive(DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID);
      if (builtInArchive == null) {
        return;
      }

      Iterator var3 = manager.getDataTypes(builtInArchive).iterator();

      while (var3.hasNext()) {
        DataType dt = (DataType) var3.next();
        if (!(dt instanceof Pointer) && !(dt instanceof FactoryDataType) && !(dt instanceof Dynamic)) {
          this.write(dt, TaskMonitor.DUMMY);
        }
      }
    } catch (CancelledException var5) {
    }

    this.writer.flush();
  }

  private static String getArrayDimensions(Array arrayDt) {
    String dimensionString = "[" + arrayDt.getNumElements() + "]";
    DataType dataType = arrayDt.getDataType();
    if (dataType instanceof Array) {
      dimensionString = dimensionString + getArrayDimensions((Array) dataType);
    }

    return dimensionString;
  }

  private DataType getBaseDataType(DataType dt) {
    while (true) {
      if (dt != null) {
        if (dt instanceof Array) {
          Array array = (Array) dt;
          dt = array.getDataType();
          continue;
        }

        if (dt instanceof Pointer) {
          Pointer pointer = (Pointer) dt;
          dt = pointer.getDataType();
          continue;
        }

        if (dt instanceof BitFieldDataType) {
          BitFieldDataType bitfieldDt = (BitFieldDataType) dt;
          dt = bitfieldDt.getBaseDataType();
          continue;
        }
      }

      return dt;
    }
  }

  private DataType getArrayBaseType(Array arrayDt) {
    DataType dataType;
    for (dataType = arrayDt.getDataType(); dataType instanceof Array; dataType = ((Array) dataType).getDataType()) {
    }

    return dataType;
  }

  private DataType getPointerBaseDataType(Pointer p) {
    DataType dt;
    for (dt = p.getDataType(); dt instanceof Pointer; dt = ((Pointer) dt).getDataType()) {
    }

    return dt;
  }

  private int getPointerDepth(Pointer p) {
    int depth = 1;

    for (DataType dt = p.getDataType(); dt instanceof Pointer; dt = ((Pointer) dt).getDataType()) {
      ++depth;
    }

    return depth;
  }

  private String getFunctionPointerString(FunctionDefinition fd, String name, DataType functionPointerArrayType,
      boolean writeEnabled, TaskMonitor monitor) throws IOException, CancelledException {
    DataType originalType = functionPointerArrayType;
    StringBuilder sb = new StringBuilder();
    DataType returnType = fd.getReturnType();
    if (writeEnabled) {
      this.write(returnType, monitor);
    }

    sb.append("(");
    String arrayDecorations = "";
    if (functionPointerArrayType instanceof Array a) {
      functionPointerArrayType = this.getArrayBaseType(a);
      arrayDecorations = getArrayDimensions(a);
    }

    if (functionPointerArrayType instanceof Pointer p) {
      for (int i = 0; i < this.getPointerDepth(p); ++i) {
        sb.append('*');
      }

      if (name != null) {
        sb.append(' ');
      }

      functionPointerArrayType = this.getPointerBaseDataType(p);
    }

    if (!(functionPointerArrayType instanceof FunctionDefinition)) {
      this.writer.append(this
          .comment("Attempting output of invalid function pointer type declaration: " + originalType.getDisplayName()));
    }

    if (name != null) {
      sb.append(name);
    }

    if (arrayDecorations.length() != 0) {
      sb.append(arrayDecorations);
    }

    sb.append(")");
    sb.append(this.getParameterListString(fd, false, writeEnabled, monitor));
    DataType baseReturnType = this.getBaseDataType(returnType);
    if (baseReturnType instanceof FunctionDefinition) {
      return this.getFunctionPointerString((FunctionDefinition) baseReturnType, sb.toString(), returnType, writeEnabled,
          monitor);
    } else {
      String var10000 = returnType.getDisplayName();
      return var10000 + " " + sb.toString();
    }
  }

  private String getParameterListString(FunctionDefinition fd, boolean includeParamNames, boolean writeEnabled,
      TaskMonitor monitor) throws IOException, CancelledException {
    StringBuilder buf = new StringBuilder();
    buf.append("(");
    boolean hasVarArgs = fd.hasVarArgs();
    ParameterDefinition[] parameters = fd.getArguments();
    int n = parameters.length;

    for (int i = 0; i < n; ++i) {
      ParameterDefinition param = parameters[i];
      String paramName = includeParamNames ? param.getName() : null;
      DataType dataType = param.getDataType();
      if (writeEnabled) {
        this.write(dataType, monitor);
      }

      String argument = this.getTypeDeclaration(paramName, dataType, param.getLength(), writeEnabled, monitor);
      buf.append(argument);
      if (i < n - 1 || hasVarArgs) {
        buf.append(", ");
      }
    }

    if (hasVarArgs) {
      buf.append("...");
    }

    if (n == 0 && !hasVarArgs) {
      buf.append(VoidDataType.dataType.getName());
    }

    buf.append(")");
    return buf.toString();
  }
}
