/*
** Module   : HtmlHelper.java
** Abstract : provides utility functions to write HTML output 
**
** Copyright (c) 2007-2021, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 GES 20070711 Created initial version which allows HTML output utility functions.
** 002 GES 20070720 Added cellspacing support for tables.
** 003 GES 20080309 Provide UTF-8 encoding specification.
** 004 CA  20210902 Added 'writeWrapStyle', to wrap the content of 'pre' tag. 
** 005 CA  20210906 The content for description and failure is wrapped only for web tests. 
*/

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.goldencode.html;

/**
 * Provides utility functions to write HTML output.
 *
 * @author GES
 */
public class HtmlHelper
implements HtmlConstants
{
   /** Standard encoding text for use in an HTML header. */
   private static String ENCODING = "<meta http-equiv=\"Content-type\" con" +
                                    "tent=\"text/html;charset=UTF-8\" />\n";
   
   /**
    * Return an output string based on the input string, where all unsafe
    * characters (in HTML) have been converted to the proper HTML entities.
    * <p>
    * The following conversions occur:
    * <p>
    * <pre>
    * Input Character         Output Character
    * -----------------       -----------------
    * &quot;                  &amp;quot;
    * &apos;                  &amp;apos;
    * &amp;                   &amp;amp;
    * &lt;                    &amp;lt;
    * &gt;                    &amp;gt;
    * </pre>
    * <p>
    * All other characters are simply written into the output without change.
    *
    * @param    input
    *           The text to filter.
    *
    * @return   The filtered text which is safe to embed in an HTML document.
    */
   public static String filter(String input)
   {
      StringBuilder sb = new StringBuilder();
      
      int len = input.length();
      
      for (int i = 0; i < len; i++)
      {
         char next = input.charAt(i);
         
         switch (next)
         {
            case '\"':
               sb.append("&quot;");
               break;
            case '\'':
               sb.append("&apos;");
               break;
            case '&':
               sb.append("&amp;");
               break;
            case '<':
               sb.append("&lt;");
               break;
            case '>':
               sb.append("&gt;");
               break;
            default:
               sb.append(next);
               break;
         }
      }
      
      return sb.toString();
   }
   
   /**
    * Starts an HTML document.
    *
    * @return   The created element.
    */
   public static String startHtml()
   {
      return "<html>\n";
   }
   
   /**
    * Ends an HTML document.
    *
    * @return   The created element.
    */
   public static String endHtml()
   {
      return "</html>";
   }
   
   /**
    * Starts the header portion of an HTML document.
    *
    * @return   The created element.
    */
   public static String startHead()
   {
      return "<head>\n";
   }
   
   /**
    * Defines the encoding of an HTML document. Should be output at or near
    * the top of the HEAD section.
    *
    * @return   The created element.
    */
   public static String getEncoding()
   {
      return ENCODING;
   }
   
   /**
    * Ends the header portion of an HTML document.
    *
    * @return   The created element.
    */
   public static String endHead()
   {
      return "</head>\n";
   }
   
   /**
    * Starts the body portion of an HTML document with a default background
    * color of '#FFFFFF'.
    *
    * @return   The created element.
    */
   public static String startBody()
   {
      return startBody("#FFFFFF");
   }
   
   /**
    * Starts the body portion of an HTML document with the given background
    * color.
    *
    * @param    bgcolor
    *           The background color for the document body.
    *
    * @return   The created element.
    */
   public static String startBody(String bgcolor)
   {
      return String.format("<body bgcolor='%s'>\n", bgcolor);
   }
   
   /**
    * Ends the body portion of an HTML document.
    *
    * @return   The created element.
    */
   public static String endBody()
   {
      return "</body>\n";
   }
   
   /**
    * Creates a title element.
    *
    * @param    txt
    *           The contents of the title element. This text will be filtered
    *           using {@link #filter} before being inserted.
    *
    * @return   The created element.
    */
   public static String title(String txt)
   {
      return String.format("<title>%s</title>\n", filter(txt));
   }
   
   /**
    * Creates a heading element.
    *
    * @param    level
    *           The heading element level to create.
    * @param    txt
    *           The contents of the heading element. This text will be
    *           filtered using {@link #filter} before being inserted.
    *
    * @return   The created element.
    */
   public static String heading(int level, String txt)
   {
      return String.format("<h%1$d>%2$s</h%1$d>\n", level, filter(txt));
   }
   
   /**
    * Starts a divison.
    *
    * @return   The created element.
    */
   public static String startDiv()
   {
      return "<div>\n";
   }
   
   /**
    * Ends a division.
    *
    * @return   The created element.
    */
   public static String endDiv()
   {
      return "</div>\n";
   }
   
   /**
    * Starts a preformatted section.
    *
    * @return   The created element.
    */
   public static String startPre()
   {
      return "<pre>";
   }
   
   /**
    * Ends a preformatted section.
    *
    * @return   The created element.
    */
   public static String endPre()
   {
      return "</pre>\n";
   }
   
   /**
    * Creates a preformatted element.
    *
    * @param    txt
    *           The contents of the element. This text will NOT be filtered
    *           using {@link #filter} before being inserted.
    *
    * @return   The created element.
    */
   public static String pre(String txt)
   {
      return String.format("<pre>%s</pre>\n", txt);
   }   
   
   /**
    * Creates a preformatted element, with its content wrapped to fit the screen.
    *
    * @param    txt
    *           The contents of the element. This text will NOT be filtered
    *           using {@link #filter} before being inserted.
    *
    * @return   The created element.
    */
   public static String preWrap(String txt)
   {
      return String.format("<pre class=\"wrap\">%s</pre>\n", txt);
   }
   
   /**
    * Creates a break element.
    *
    * @return   The created element.
    */
   public static String br()
   {
      return "<br>";
   }
   
   /**
    * Creates a paragraph element.
    *
    * @return   The created element.
    */
   public static String p()
   {
      return "<p>";
   }
   
   /**
    * Creates a horizontal rule element.
    *
    * @return   The created element.
    */
   public static String hr()
   {
      return "<hr>\n";
   }
   
   /**
    * Starts a center section.
    *
    * @return   The created element.
    */
   public static String startCenter()
   {
      return "<center>\n";
   }
   
   /**
    * Ends a center section.
    *
    * @return   The created element.
    */
   public static String endCenter()
   {
      return "</center>\n";
   }
   
   /**
    * Creates a named anchor element.
    *
    * @param    name
    *           The anchor's name.
    * @param    txt
    *           The contents of the element. This text will be filtered
    *           using {@link #filter} before being inserted.
    *
    * @return   The created element.
    */
   public static String namedAnchor(String name, String txt)
   {
      return namedAnchor(name, txt, true);
   }
   
   /**
    * Creates a named anchor element.
    *
    * @param    name
    *           The anchor's name.
    * @param    txt
    *           The contents of the element. This text will be filtered
    *           using {@link #filter} before being inserted.
    * @param    filter
    *           <code>true</code> to filter the content using {@link #filter}
    *           before being inserted.
    *
    * @return   The created element.
    */
   public static String namedAnchor(String name, String txt, boolean filter)
   {
      return String.format("<a name='%s'>%s</a>\n",
                           name,
                           filter ? filter(txt) : txt);
   }
   
   /**
    * Creates a hyperlink element.
    *
    * @param    link
    *           The hyperlink (URL).
    * @param    txt
    *           The contents of the element. This text will be filtered
    *           using {@link #filter} before being inserted.
    * @param    nl
    *           <code>true</code> to add a newline at the end of the element.
    *
    * @return   The created element.
    */
   public static String href(String link, String txt, boolean nl)
   {
      return String.format("<a href='%s'>%s</a>%s",
                           link,
                           filter(txt),
                           nl ? "\n" : "");
   }
   
   /**
    * Starts a table a default width of 100%, a border of 1 and a cell
    * spacing of 0.
    *
    * @return   The created element.
    */
   public static String startTable()
   {
      return startTable(100, 1, 0);
   }
   
   /**
    * Starts a table with user provided width and border and a cell
    * spacing of 0.
    *
    * @param    width
    *           The width as a percent of the page.
    * @param    border
    *           The border size.
    *
    * @return   The created element.
    */
   public static String startTable(int width, int border)
   {
      return startTable(width, border, 0);
   }
   
   /**
    * Starts a table.
    *
    * @param    width
    *           The width as a percent of the page.
    * @param    border
    *           The border size.
    * @param    cellSpacing
    *           The amount of per cell padding.
    *
    * @return   The created element.
    */
   public static String startTable(int width, int border, int cellSpacing)
   {
      String spec = "<table width='%d%%' border='%d' cellspacing='%d'>\n";
      return String.format(spec,
                           width,
                           border,
                           cellSpacing);
   }
   
   /**
    * Ends a table.
    *
    * @return   The created element.
    */
   public static String endTable()
   {
      return "</table>\n";
   }
   
   /**
    * Starts a row with no background color (uses the document's setting).
    *
    * @return   The created element.
    */
   public static String startRow()
   {
      return "<tr>\n";
   }
   
   /**
    * Starts a row.
    *
    * @param    bgcolor
    *           The background color for the row.
    *
    * @return   The created element.
    */
   public static String startRow(String bgcolor)
   {
      return String.format("<tr bgcolor='%s'>\n", bgcolor);
   }
   
   /**
    * Ends a row.
    *
    * @return   The created element.
    */
   public static String endRow()
   {
      return "</tr>\n";
   }
   
   /**
    * Creates a field element. Convenience method.
    *
    * @param    halign
    *           The horizontal alignment to use.
    * @param    valign
    *           The vertical alignment to use.
    * @param    txt
    *           The contents of the element.
    * @param    filter
    *           <code>true</code> to cause the contents to be filtered
    *           using {@link #filter} before being inserted.
    *
    * @return   The created element.
    */
   public static String field(String  halign,
                              String  valign,
                              String  txt,
                              boolean filter)
   {
      return String.format("%s%s%s",
                           startField(halign, valign, false),
                           filter ? filter(txt) : txt,
                           endField());
   }
   
   /**
    * Starts a field element.
    *
    * @param    halign
    *           The horizontal alignment to use.
    * @param    valign
    *           The vertical alignment to use.
    *
    * @return   The created element.
    */
   public static String startField(String halign, String valign)
   {
      return startField(halign, valign, true);
   }
   
   /**
    * Starts a field element.
    *
    * @param    halign
    *           The horizontal alignment to use.
    * @param    valign
    *           The vertical alignment to use.
    * @param    nl
    *           <code>true</code> to add a newline at the end of the element.
    *
    * @return   The created element.
    */
   public static String startField(String halign, String valign, boolean nl)
   {
      return String.format("<td align='%s' valign='%s'>%s",
                           halign,
                           valign,
                           nl ? "\n" : "");
   }
   
   /**
    * Ends a field element.
    *
    * @return   The created element.
    */
   public static String endField()
   {
      return "</td>\n";
   }
   
   /**
    * Creates a strong element.
    *
    * @param    txt
    *           The contents of the element. This text will be filtered
    *           using {@link #filter} before being inserted.
    *
    * @return   The created element.
    */
   public static String strong(String txt)
   {
      return String.format("<strong>%s</strong>", filter(txt));
   }

   /**
    * Get the CSS style so that the contents of the <code>pre</code> tag will be wrapped.
    * 
    * @return   See above.
    */
   public static String writeWrapStyle()
   {
      return "<style>\n"
            + "pre.wrap {\n"
            + "  display: block;\n"
            + "  font-family: monospace;\n"
            + "  white-space: pre-wrap;\n"
            + "  margin: 1em 0;\n"
            + "  word-break: break-all;\n"
            + "} \n"
            + "</style>";
   }
}
