/**
  * generate a HTML page
  *
  * @param empty if this is true an empty output is generated
  * @return String with HTML data
  */
 private String getEmptyHtmlPage() {
   final HtmlExporter exporter = createHtmlTemplate(ExportUtils.createHtmlExporter());
   return exporter.toString(
       new MacroResolver() {
         @Override
         public Object resolve(final String aMacro, final Element aParent) {
           if ("date-now".equals(aMacro)) {
             final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
             return df.format(new Date());
           } else if ("decoded-bytes".equals(aMacro)
               || "detected-bus-errors".equals(aMacro)
               || "baudrate".equals(aMacro)) {
             return "-";
           } else if ("decoded-data".equals(aMacro)) {
             return null;
           }
           return null;
         }
       });
 }
  /**
   * generate a HTML page
   *
   * @param empty if this is true an empty output is generated
   * @return String with HTML data
   */
  private String toHtmlPage(final File aFile, final UARTDataSet aDataSet) throws IOException {
    final int bitCount = Integer.parseInt((String) this.bits.getSelectedItem());
    final int bitAdder = ((bitCount % 4) != 0) ? 1 : 0;

    final MacroResolver macroResolver =
        new MacroResolver() {
          @Override
          public Object resolve(final String aMacro, final Element aParent) {
            if ("date-now".equals(aMacro)) {
              final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
              return df.format(new Date());
            } else if ("decoded-bytes".equals(aMacro)) {
              return Integer.valueOf(aDataSet.getDecodedSymbols());
            } else if ("detected-bus-errors".equals(aMacro)) {
              return Integer.valueOf(aDataSet.getDetectedErrors());
            } else if ("baudrate".equals(aMacro)) {
              final String baudrate;
              if (aDataSet.getBaudRate() <= 0) {
                baudrate = "<span class='error'>Baudrate calculation failed!</span>";
              } else {
                baudrate =
                    String.format(
                        "%d (exact: %d)",
                        Integer.valueOf(aDataSet.getBaudRate()),
                        Integer.valueOf(aDataSet.getBaudRateExact()));
                if (!aDataSet.isBitLengthUsable()) {
                  return baudrate.concat(
                      " <span class='warning'>The baudrate may be wrong, use a higher samplerate to avoid this!</span>");
                }

                return baudrate;
              }
            } else if ("decoded-data".equals(aMacro)) {
              final List<UARTData> decodedData = aDataSet.getData();
              Element tr;

              for (int i = 0; i < decodedData.size(); i++) {
                final UARTData ds = decodedData.get(i);

                if (ds.isEvent()) {
                  String rxEventData = "";
                  String txEventData = "";

                  String bgColor;
                  if (UARTData.UART_TYPE_EVENT == ds.getType()) {
                    rxEventData = txEventData = ds.getEventName();
                    bgColor = "#e0e0e0";
                  } else if (UARTData.UART_TYPE_RXEVENT == ds.getType()) {
                    rxEventData = ds.getEventName();
                    bgColor = "#c0ffc0";
                  } else if (UARTData.UART_TYPE_TXEVENT == ds.getType()) {
                    txEventData = ds.getEventName();
                    bgColor = "#c0ffc0";
                  } else {
                    // unknown event
                    bgColor = "#ff8000";
                  }

                  if (txEventData.endsWith("_ERR") || rxEventData.endsWith("_ERR")) {
                    bgColor = "#ff8000";
                  }

                  tr =
                      aParent
                          .addChild(TR)
                          .addAttribute("style", "background-color: " + bgColor + ";");
                  tr.addChild(TD).addContent(String.valueOf(i));
                  tr.addChild(TD)
                      .addContent(Unit.Time.format(aDataSet.getTime(ds.getStartSampleIndex())));
                  tr.addChild(TD).addContent(rxEventData);
                  tr.addChild(TD);
                  tr.addChild(TD);
                  tr.addChild(TD);
                  tr.addChild(TD).addContent(txEventData);
                  tr.addChild(TD);
                  tr.addChild(TD);
                  tr.addChild(TD);
                } else {
                  String rxDataHex = "", rxDataBin = "", rxDataDec = "", rxDataASCII = "";
                  String txDataHex = "", txDataBin = "", txDataDec = "", txDataASCII = "";

                  // Normal data...
                  if (UARTData.UART_TYPE_RXDATA == ds.getType()) {
                    final int rxData = ds.getData();

                    rxDataHex = integerToHexString(rxData, (bitCount / 4) + bitAdder);
                    rxDataBin = integerToBinString(rxData, bitCount);
                    rxDataDec = String.valueOf(rxData);
                    rxDataASCII = toASCII((char) rxData);
                  } else
                  /* if ( UARTData.UART_TYPE_TXDATA == ds.getType() ) */
                  {
                    final int txData = ds.getData();

                    txDataHex = integerToHexString(txData, (bitCount / 4) + bitAdder);
                    txDataBin = integerToBinString(txData, bitCount);
                    txDataDec = String.valueOf(txData);
                    txDataASCII = toASCII(txData);
                  }

                  tr = aParent.addChild(TR);
                  tr.addChild(TD).addContent(String.valueOf(i));
                  tr.addChild(TD)
                      .addContent(Unit.Time.format(aDataSet.getTime(ds.getStartSampleIndex())));
                  tr.addChild(TD).addContent("0x", rxDataHex);
                  tr.addChild(TD).addContent("0b", rxDataBin);
                  tr.addChild(TD).addContent(rxDataDec);
                  tr.addChild(TD).addContent(rxDataASCII);
                  tr.addChild(TD).addContent("0x", txDataHex);
                  tr.addChild(TD).addContent("0b", txDataBin);
                  tr.addChild(TD).addContent(txDataDec);
                  tr.addChild(TD).addContent(txDataASCII);
                }
              }
            }
            return null;
          }
        };

    if (aFile == null) {
      final HtmlExporter exporter = createHtmlTemplate(ExportUtils.createHtmlExporter());
      return exporter.toString(macroResolver);
    } else {
      final HtmlFileExporter exporter =
          (HtmlFileExporter) createHtmlTemplate(ExportUtils.createHtmlExporter(aFile));
      exporter.write(macroResolver);
      exporter.close();
    }

    return null;
  }
  /**
   * Creates the HTML template for exports to HTML.
   *
   * @param aExporter the HTML exporter instance to use, cannot be <code>null</code>.
   * @return a HTML exporter filled with the template, never <code>null</code>.
   */
  private HtmlExporter createHtmlTemplate(final HtmlExporter aExporter) {
    aExporter.addCssStyle("body { font-family: sans-serif; } ");
    aExporter.addCssStyle(
        "table { border-width: 1px; border-spacing: 0px; border-color: gray;"
            + " border-collapse: collapse; border-style: solid; margin-bottom: 15px; } ");
    aExporter.addCssStyle(
        "table th { border-width: 1px; padding: 2px; border-style: solid; border-color: gray;"
            + " background-color: #C0C0FF; text-align: left; font-weight: bold; font-family: sans-serif; } ");
    aExporter.addCssStyle(
        "table td { border-width: 1px; padding: 2px; border-style: solid; border-color: gray;"
            + " font-family: monospace; } ");
    aExporter.addCssStyle(".error { color: red; } ");
    aExporter.addCssStyle(".warning { color: orange; } ");
    aExporter.addCssStyle(".date { text-align: right; font-size: x-small; margin-bottom: 15px; } ");
    aExporter.addCssStyle(".w100 { width: 100%; } ");
    aExporter.addCssStyle(".w35 { width: 35%; } ");
    aExporter.addCssStyle(".w30 { width: 30%; } ");
    aExporter.addCssStyle(".w15 { width: 15%; } ");
    aExporter.addCssStyle(".w10 { width: 10%; } ");
    aExporter.addCssStyle(".w8 { width: 8%; } ");
    aExporter.addCssStyle(".w7 { width: 7%; } ");

    final Element body = aExporter.getBody();
    body.addChild(H1).addContent("UART Analysis results");
    body.addChild(HR);
    body.addChild(DIV).addAttribute("class", "date").addContent("Generated: ", "{date-now}");

    Element table, tr, thead, tbody;

    table = body.addChild(TABLE).addAttribute("class", "w100");

    tbody = table.addChild(TBODY);
    tr = tbody.addChild(TR);
    tr.addChild(TH).addAttribute("colspan", "2").addContent("Statistics");
    tr = tbody.addChild(TR);
    tr.addChild(TD).addAttribute("class", "w30").addContent("Decoded bytes");
    tr.addChild(TD).addContent("{decoded-bytes}");
    tr = tbody.addChild(TR);
    tr.addChild(TD).addAttribute("class", "w30").addContent("Detected bus errors");
    tr.addChild(TD).addContent("{detected-bus-errors}");
    tr = tbody.addChild(TR);
    tr.addChild(TD).addAttribute("class", "w30").addContent("Baudrate");
    tr.addChild(TD).addContent("{baudrate}");

    table = body.addChild(TABLE).addAttribute("class", "w100");
    thead = table.addChild(THEAD);
    tr = thead.addChild(TR);
    tr.addChild(TH).addAttribute("class", "w30").addAttribute("colspan", "2");
    tr.addChild(TH).addAttribute("class", "w35").addAttribute("colspan", "4").addContent("RxD");
    tr.addChild(TH).addAttribute("class", "w35").addAttribute("colspan", "4").addContent("TxD");
    tr = thead.addChild(TR);
    tr.addChild(TH).addAttribute("class", "w15").addContent("Index");
    tr.addChild(TH).addAttribute("class", "w15").addContent("Time");
    tr.addChild(TH).addAttribute("class", "w10").addContent("Hex");
    tr.addChild(TH).addAttribute("class", "w10").addContent("Bin");
    tr.addChild(TH).addAttribute("class", "w8").addContent("Dec");
    tr.addChild(TH).addAttribute("class", "w7").addContent("ASCII");
    tr.addChild(TH).addAttribute("class", "w10").addContent("Hex");
    tr.addChild(TH).addAttribute("class", "w10").addContent("Bin");
    tr.addChild(TH).addAttribute("class", "w8").addContent("Dec");
    tr.addChild(TH).addAttribute("class", "w7").addContent("ASCII");
    tbody = table.addChild(TBODY);
    tbody.addContent("{decoded-data}");

    return aExporter;
  }