/**
  * Generates some preamble (i.e. comments, language declaration section etc.) and adds it to
  * this.code.
  *
  * @param _root - the diagram root element
  * @param _indent - the current indentation string
  * @param varNames - list of variable names introduced inside the body
  */
 @Override
 protected String generatePreamble(Root _root, String _indent, StringList varNames) {
   code.add("");
   // Variable declaration proposals (now with all used variables listed)
   insertComment("TODO: Declare local variables here:", _indent);
   for (int v = 0; v < varNames.count(); v++) {
     insertComment(varNames.get(v), _indent);
   }
   code.add("");
   return _indent;
 }
 /**
  * Creates the appropriate code for returning a required result and adds it (after the algorithm
  * code of the body) to this.code)
  *
  * @param _root - the diagram root element
  * @param _indent - the current indentation string
  * @param alwaysReturns - whether all paths of the body already force a return
  * @param varNames - names of all assigned variables
  */
 @Override
 protected String generateResult(
     Root _root, String _indent, boolean alwaysReturns, StringList varNames) {
   if ((returns || _root.getResultType() != null || isFunctionNameSet || isResultSet)
       && !alwaysReturns) {
     String result = "0";
     if (isFunctionNameSet) {
       result = _root.getMethodName();
     } else if (isResultSet) {
       int vx = varNames.indexOf("result", false);
       result = varNames.get(vx);
     }
     code.add(_indent);
     code.add(_indent + "return " + result + ";");
   }
   return _indent;
 }
  /**
   * Composes the heading for the program or function according to the C language specification.
   *
   * @param _root - The diagram root
   * @param _indent - the initial indentation string
   * @param _procName - the procedure name
   * @param paramNames - list of the argument names
   * @param paramTypes - list of corresponding type names (possibly null)
   * @param resultType - result type name (possibly null)
   * @return the default indentation string for the subsequent stuff
   */
  @Override
  protected String generateHeader(
      Root _root,
      String _indent,
      String _procName,
      StringList _paramNames,
      StringList _paramTypes,
      String _resultType) {
    // START KGU#178 2016-07-20: Enh. #160
    if (topLevel) {
      insertComment("Generated by Structorizer " + Element.E_VERSION, _indent);
      code.add("");
      subroutineInsertionLine = code.count(); // default position for subroutines
      subroutineIndent = _indent;
    } else {
      code.add("");
    }
    // END KGU#178 2016-07-20

    if (_root.isProgram == true) {
      code.add(_indent + "using System;");
      code.add(_indent + "");
      // START KGU 2015-10-18
      insertBlockComment(_root.getComment(), _indent, "/**", " * ", " */");
      // END KGU 2015-10-18

      insertBlockHeading(_root, "public class " + _procName, _indent);
      code.add(_indent);
      insertComment(
          "TODO: Declare and initialise class and member variables here",
          _indent + this.getIndent());
      code.add(_indent);
      code.add(_indent + this.getIndent() + "/**");
      code.add(_indent + this.getIndent() + " * @param args");
      code.add(_indent + this.getIndent() + " */");

      insertBlockHeading(
          _root, "public static void Main(string[] args)", _indent + this.getIndent());
      code.add("");
    } else {
      insertBlockComment(_root.getComment(), _indent + this.getIndent(), "/**", " * ", null);
      if (_resultType != null || this.returns || this.isFunctionNameSet || this.isResultSet) {
        insertBlockComment(_paramNames, _indent + this.getIndent(), null, " * @param ", null);
        code.add(_indent + this.getIndent() + " * @return ");
        code.add(_indent + this.getIndent() + " */");
        _resultType = transformType(_resultType, "int");
      } else {
        insertBlockComment(_paramNames, _indent + this.getIndent(), null, " * @param ", " */");
        _resultType = "void";
      }
      // START KGU#178 2016-07-20: Enh. #160 - insert called subroutines as private
      // String fnHeader = "public static " + _resultType + " " + _procName + "(";
      String fnHeader =
          (topLevel ? "public" : "private") + " static " + _resultType + " " + _procName + "(";
      // END KGU#178 2016-07-20
      for (int p = 0; p < _paramNames.count(); p++) {
        if (p > 0) fnHeader += ", ";
        fnHeader +=
            (transformType(_paramTypes.get(p), "/*type?*/") + " " + _paramNames.get(p)).trim();
      }
      fnHeader += ")";
      insertBlockHeading(_root, fnHeader, _indent + this.getIndent());
    }

    return _indent + this.getIndent() + this.getIndent();
  }
  /**
   * We try our very best to create a working loop from a FOR-IN construct This will only work,
   * however, if we can get reliable information about the size of the value list, which won't be
   * the case if we obtain it e.g. via a variable.
   *
   * @param _for - the element to be exported
   * @param _indent - the current indentation level
   * @return true iff the method created some loop code (sensible or not)
   */
  protected boolean generateForInCode(For _for, String _indent) {
    boolean isDisabled = _for.isDisabled();
    // We simply use the range-based loop of Java (as far as possible)
    String var = _for.getCounterVar();
    String valueList = _for.getValueList();
    StringList items = this.extractForInListItems(_for);
    String indent = _indent;
    String itemType = null;
    if (items != null) {
      valueList = "{" + items.concatenate(", ") + "}";
      // Good question is: how do we guess the element type and what do we
      // do if items are heterogenous? We will just try three types: int,
      // double and String, and if none of them match we add a TODO comment.
      int nItems = items.count();
      boolean allInt = true;
      boolean allDouble = true;
      boolean allString = true;
      for (int i = 0; i < nItems; i++) {
        String item = items.get(i);
        if (allInt) {
          try {
            Integer.parseInt(item);
          } catch (NumberFormatException ex) {
            allInt = false;
          }
        }
        if (allDouble) {
          try {
            Double.parseDouble(item);
          } catch (NumberFormatException ex) {
            allDouble = false;
          }
        }
        if (allString) {
          allString =
              item.startsWith("\"")
                  && item.endsWith("\"")
                  && !item.substring(1, item.length() - 1).contains("\"");
        }
      }
      if (allInt) itemType = "int";
      else if (allDouble) itemType = "double";
      else if (allString) itemType = "char*";
      String arrayName = "array20160322";

      // Extra block to encapsulate the additional variable declarations
      addCode("{", _indent, isDisabled);
      indent += this.getIndent();

      if (itemType == null) {
        itemType = "object";
        this.insertComment(
            "TODO: Find a more specific item type than object and/or prepare the elements of the array",
            indent);
      }
      addCode(
          itemType + "[] " + arrayName + " = " + transform(valueList, false) + ";",
          indent,
          isDisabled);

      valueList = arrayName;
    } else {
      itemType = "object";
      this.insertComment(
          "TODO: Find a more specific item type than object and/or prepare the elements of the array",
          indent);
      valueList = transform(valueList, false);
    }

    // Creation of the loop header
    insertBlockHeading(_for, "foreach (" + itemType + " " + var + " in " + valueList + ")", indent);

    // Add the loop body as is
    generateCode(_for.q, indent + this.getIndent());

    // Accomplish the loop
    insertBlockTail(_for, null, indent);

    if (items != null) {
      addCode("}", _indent, isDisabled);
    }

    return true;
  }