class NameFormatter {

  public static String buildName(ParsedName n,
                                 boolean hybridMarker,
                                 boolean rankMarker,
                                 boolean authorship,
                                 boolean genusForinfrageneric,
                                 boolean infrageneric,
                                 boolean decomposition,
                                 boolean asciiOnly,
                                 boolean showQualifier,
                                 boolean showIndet,
                                 boolean nomNote,
                                 boolean showSensu,
                                 boolean showCultivar,
                                 boolean showStrain,
                                 boolean html
  ) {
    StringBuilder sb = new StringBuilder();
    
    boolean candidateItalics = false;
    if (n.isCandidatus()) {
      sb.append("\"");
      if (html) {
        openItalics(sb);
        candidateItalics = true;
        // we turn off html here cause the entire name should be in italics!
        html = false;
      }
      sb.append("Candidatus ");
    }
    
    if (n.getUninomial() != null) {
      // higher rank names being just a uninomial!
      if (hybridMarker && NamePart.GENERIC == n.getNotho()) {
        sb.append(HYBRID_MARKER)
            .append(" ");
      }
      appendInItalics(sb, n.getUninomial(), html);
      
    } else {
      // bi- or trinomials or infrageneric names
      if (n.getInfragenericEpithet() != null) {
        if ((isUnknown(n.getRank()) && n.getSpecificEpithet() == null) || (n.getRank() != null && n.getRank().isInfragenericStrictly())) {
          boolean showInfraGen = true;
          // the infrageneric is the terminal rank. Always show it and wrap it with its genus if requested
          if (n.getGenus() != null && genusForinfrageneric) {
            appendGenus(sb, n, hybridMarker, showQualifier, html);
            sb.append(" ");
            // we show zoological infragenerics in brackets,
            // but use rank markers for botanical names (unless its no defined rank)
            if (NomCode.ZOOLOGICAL == n.getCode()) {
              sb.append("(");
              if (hybridMarker && NamePart.INFRAGENERIC == n.getNotho()) {
                sb.append(HYBRID_MARKER)
                    .append(' ');
              }
              appendInItalics(sb, n.getInfragenericEpithet(), html);
              sb.append(")");
              showInfraGen = false;
            }
          }
          if (showInfraGen) {
            if (rankMarker) {
              // If we know the rank we use explicit rank markers
              // this is how botanical infrageneric names are formed, see http://www.iapt-taxon.org/nomen/main.php?page=art21
              if (appendRankMarker(sb, n.getRank(), hybridMarker && NamePart.INFRAGENERIC == n.getNotho())) {
                sb.append(' ');
              }
            }
            appendInItalics(sb, n.getInfragenericEpithet(), html);
          }
          
        } else {
          if (n.getGenus() != null) {
            appendGenus(sb, n, hybridMarker, showQualifier, html);
          }
          if (infrageneric) {
            // additional subgenus shown for binomial. Always shown in brackets
            sb.append(" (");
            appendInItalics(sb, n.getInfragenericEpithet(), html);
            sb.append(")");
          }
        }
        
      } else if (n.getGenus() != null) {
        appendGenus(sb, n, hybridMarker, showQualifier, html);
      }
      
      if (n.getSpecificEpithet() == null) {
        if (showIndet && n.getGenus() != null && n.getCultivarEpithet() == null) {
          if (n.getRank() != null && n.getRank().isSpeciesOrBelow()) {
            // no species epithet given, indetermined!
            if (n.getRank().isInfraspecific()) {
              // maybe we have an infraspecific epithet? force to show the rank marker
              appendInfraspecific(sb, n, hybridMarker, showQualifier, rankMarker, true, html);
            } else {
              sb.append(" ");
              sb.append(n.getRank().getMarker());
            }
            authorship = false;
          }
        } else if (n.getInfraspecificEpithet() != null) {
          appendInfraspecific(sb, n, hybridMarker, showQualifier, rankMarker, false, html);
        }
        
      } else {
        // species part
        sb.append(' ');
        if (showQualifier && n.hasEpithetQualifier(NamePart.SPECIFIC )) {
          sb.append(n.getEpithetQualifier().get(NamePart.SPECIFIC))
              .append(" ");
        }
        if (hybridMarker && NamePart.SPECIFIC == n.getNotho()) {
          sb.append(HYBRID_MARKER)
              .append(" ");
        }
        appendInItalics(sb, n.getSpecificEpithet(), html);
        
        if (n.getInfraspecificEpithet() == null) {
          // Indetermined infraspecies? Only show indet cultivar marker if no cultivar epithet exists
          if (showIndet
              && n.getRank() != null
              && n.getRank().isInfraspecific()
              && (NomCode.CULTIVARS != n.getRank().isRestrictedToCode() || n.getCultivarEpithet() == null)
          ) {
            // no infraspecific epitheton given, but rank below species. Indetermined!
            // use ssp. for subspecies in case of indetermined names
            if (n.getRank() == Rank.SUBSPECIES) {
              sb.append(" ssp.");
            } else {
              sb.append(' ');
              sb.append(n.getRank().getMarker());
            }
            authorship = false;
          }
          
        } else {
          // infraspecific part
          appendInfraspecific(sb, n, hybridMarker, showQualifier, rankMarker, false, html);
          // non autonym authorship ?
          if (n.isAutonym()) {
            authorship = false;
          }
        }
      }
    }
    
    // closing quotes for Candidatus names
    if (n.isCandidatus()) {
      if (candidateItalics) {
        closeItalics(sb);
      }
      sb.append("\"");
    }
    
    // uninomial, genus, infragen, species or infraspecies authorship
    if (authorship && n.hasAuthorship()) {
      sb.append(" ");
      appendAuthorship(n, sb);
    }
    
    // add strain name
    if (showStrain && n.getStrain() != null) {
      sb.append(" ")
          .append(n.getStrain());
    }
    
    // add cultivar name
    if (showCultivar && n.getCultivarEpithet() != null) {
      if (Rank.CULTIVAR_GROUP == n.getRank()) {
        sb.append(" ")
            .append(n.getCultivarEpithet())
            .append(" Group");
        
      } else if (Rank.GREX == n.getRank()) {
        sb.append(" ")
            .append(n.getCultivarEpithet())
            .append(" gx");
        
      } else {
        sb.append(" '")
            .append(n.getCultivarEpithet())
            .append("'");
      }
    }
    
    // add sensu/sec reference
    if (showSensu && n.getTaxonomicNote() != null) {
      appendIfNotEmpty(sb, " ")
          .append(n.getTaxonomicNote());
    }
    
    // add nom status
    if (nomNote && n.getNomenclaturalNote() != null) {
      appendIfNotEmpty(sb, ", ")
          .append(n.getNomenclaturalNote());
    }
    
    // final char transformations
    String name = sb.toString().trim();
    if (decomposition) {
      name = UnicodeUtils.decompose(name);
    }
    if (asciiOnly) {
      name = UnicodeUtils.ascii(name);
    }
    return Strings.emptyToNull(name);
  }

  private  NameFormatter();

}