package ca.ualberta.stothard.cgview;

import java.awt.geom.*;
import java.util.*;
import org.apache.commons.lang.StringEscapeUtils;

/**
 * This class contains methods for constructing HTML pages that contain Cgview maps. Tags are added
 * to the document in the order that the various tag adding methods are called. The methods in this
 * class escape all supplied text to make it suitable for inclusion in an HTML document.
 *
 * @author Paul Stothard
 */
public class CgviewHTMLDocument implements CgviewConstants {

  private ArrayList contents;
  private String newline;

  /** Constructs a new CgviewHTMLDocument object. */
  public CgviewHTMLDocument() {
    contents = new ArrayList();
    newline = System.getProperty("line.separator");
  }

  /**
   * Adds header information to this CgviewHTMLDocument, including an html opening tag. This method
   * should be the first method called after a CgviewHTMLDocument is created.
   *
   * @param title the title in the header.
   * @param zoom the value to appear in the 'zoom' meta tag.
   * @param zoomCenter the value to appear in the 'zoomCenter' meta tag.
   * @param imageFormat the format of the image to which this CgviewHTMLDocument refers.
   * @param useOverlib whether or not to use the overlib.js JavaScript library for PNG and JPG image
   *     maps.
   * @param useExternalStyleSheet whether or not to use an external stylesheet.
   */
  public void addHeader(
      String title,
      String zoom,
      String zoomCenter,
      String imageFormat,
      Boolean useOverlib,
      Boolean useExternalStyleSheet) {
    contents.add(
        "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
            + newline);
    contents.add("<html lang=\"en\">" + newline);
    contents.add("<head>" + newline);
    contents.add("<title>" + StringEscapeUtils.escapeHtml(title) + "</title>" + newline);
    // contents.add("<meta name=\"mapInfo\" zoom=\"" + StringEscapeUtils.escapeHtml(zoom) + "\" />"
    // + newline);
    // contents.add("<meta name=\"mapInfo\" zoomCenter=\"" +
    // StringEscapeUtils.escapeHtml(zoomCenter) + "\" />" + newline);
    // contents.add("<meta name=\"mapInfo\" format=\"" + StringEscapeUtils.escapeHtml(imageFormat) +
    // "\" />" + newline);
    contents.add(
        "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\" />" + newline);
    if ((useExternalStyleSheet != null) && (useExternalStyleSheet.booleanValue())) {
      contents.add(
          "<link rel=\"stylesheet\" href=\"includes/stylesheet.css\" type=\"text/css\" />"
              + newline);
    } else {
      contents.add("<style type=\"text/css\">" + newline);
      contents.add(
          "body {background-color: #FFFFFF; font-family: arial, sans-serif; color: #000000}"
              + newline);
      contents.add("span.note {font-size: small; font-style: italic}" + newline);
      contents.add("span.validInfo {font-size: x-small}" + newline);
      contents.add("span.fileSize {font-size: x-small}" + newline);
      contents.add("span.warning {font-size: x-small; color: red}" + newline);
      contents.add(
          "div.heading {color: #FFFFFF; background-color: #6666FF; font-size: large; padding: 0.1cm}"
              + newline);
      contents.add("div.title {font-size: x-large; text-align: center}" + newline);
      contents.add("table.noBorder {border-style: none}" + newline);
      contents.add("table.border {border-style: solid}" + newline);
      contents.add("td.center {text-align: center; font-size: small}" + newline);
      contents.add("td.left {text-align: left; font-size: small}" + newline);
      contents.add("td.right {text-align: right; font-size: small}" + newline);
      contents.add("a:link {color: #000099; text-decoration: none}" + newline);
      contents.add("a:visited {color: #000099; text-decoration: none}" + newline);
      contents.add("a:active {color: #FF0000; text-decoration: underline}" + newline);
      contents.add("a:hover {color: #FF0000; text-decoration: underline}" + newline);
      contents.add("</style>" + newline);
    }

    if ((useOverlib != null)
        && ((useOverlib.booleanValue())
            && ((imageFormat.equalsIgnoreCase("png")) || (imageFormat.equalsIgnoreCase("jpg"))))) {
      contents.add(
          "<script type=\"text/javascript\" src=\"includes/overlib.js\"><!-- overLIB (c) Erik Bosrup --></script>"
              + newline);
    }
    contents.add("</head>" + newline);
    contents.add("<body>" + newline);
    if ((useOverlib != null)
        && ((useOverlib.booleanValue())
            && ((imageFormat.equalsIgnoreCase("png")) || (imageFormat.equalsIgnoreCase("jpg"))))) {
      contents.add(
          "<div id=\"overDiv\" style=\"position:absolute; visibility:hidden; z-index:1000;\"></div>"
              + newline);
    }
  }

  /**
   * Adds table and tbody opening tags to this CgviewHTMLDocument.
   *
   * @param width the width of the table in pixels.
   */
  public void addTableStart(int width) {
    addTableStart(width, 0);
  }

  /**
   * Adds table and tbody opening tags to this CgviewHTMLDocument.
   *
   * @param width the width of the table in pixels.
   * @param border the width of the border in pixels.
   */
  public void addTableStart(int width, int border) {
    if (border == 0) {
      contents.add(
          "<table class=\"noBorder\" width=\""
              + Integer.toString(width)
              + "\" cellspacing=\"0\" cellpadding=\"1\" align=\"left\">"
              + newline);
    } else {
      contents.add(
          "<table class=\"border\" width=\""
              + Integer.toString(width)
              + "\" border=\""
              + border
              + "\" cellspacing=\"0\" cellpadding=\"1\" align=\"left\">"
              + newline);
    }
    contents.add("<tbody>" + newline);
  }

  /** Adds table and tbody closing tags to this CgviewHTMLDocument. */
  public void addTableEnd() {
    contents.add("</tbody>" + newline);
    contents.add("</table>" + newline);
  }

  /** Adds tr opening tag to this CgviewHTMLDocument. */
  public void addTableRowStart() {
    contents.add("<tr>" + newline);
  }

  /** Adds tr closing tag to this CgviewHTMLDocument. */
  public void addTableRowEnd() {
    contents.add("</tr>" + newline);
  }

  /** Adds td opening tag to this CgviewHTMLDocument. */
  public void addTableColumnStart() {
    contents.add("<td>" + newline);
  }

  /** Adds td opening tag with center alignment to this CgviewHTMLDocument. */
  public void addTableColumnCenterStart() {
    contents.add("<td class=\"center\">" + newline);
  }

  /** Adds td opening tag with left alignment to this CgviewHTMLDocument. */
  public void addTableColumnLeftStart() {
    contents.add("<td class=\"left\">" + newline);
  }

  /** Adds td opening tag with right alignment to this CgviewHTMLDocument. */
  public void addTableColumnRightStart() {
    contents.add("<td class=\"right\">" + newline);
  }

  /** Adds td closing tag to this CgviewHTMLDocument. */
  public void addTableColumnEnd() {
    contents.add("</td>" + newline);
  }

  /**
   * Adds text to this CgviewHTMLDocument stating that the document is valid XHTML 1.0 and valid
   * CSS.
   */
  public void addValidationInfo() {
    contents.add("Valid XHTML 1.0;");
    contents.add("&nbsp;");
    contents.add("Valid CSS.");
  }

  /**
   * Adds body and html closing tags to this CgviewHTMLDocument. This method should be the last
   * method called before the contents of this document are retrieved using {@link #getContents()}.
   */
  public void addFooter() {
    contents.add("</body>" + newline);
    contents.add("</html>" + newline);
  }

  /** Adds a span opening tag with the 'note' style to this CgviewHTMLDocument. */
  public void addSpanNoteStart() {
    contents.add("<span class=\"note\">" + newline);
  }

  /** Adds a span opening tag with the 'fileSize' style to this CgviewHTMLDocument. */
  public void addSpanFileSizeStart() {
    contents.add("<span class=\"fileSize\">" + newline);
  }

  /** Adds a span opening tag with the 'validInfo' style to this CgviewHTMLDocument. */
  public void addSpanValidStart() {
    contents.add("<span class=\"validInfo\">" + newline);
  }

  /** Adds a span opening tag with the 'warning' style to this CgviewHTMLDocument. */
  public void addSpanWarningStart() {
    contents.add("<span class=\"warning\">" + newline);
  }

  /** Adds a span closing tag to this CgviewHTMLDocument. */
  public void addSpanEnd() {
    contents.add("</span>" + newline);
  }

  /**
   * Adds an image with an image map to this CgviewHTMLDocument, to implement mouseovers and
   * hyperlinks associated with Cgview Feature objects and FeatureRange objects. Image maps are used
   * for PNG and JPG maps. SVG maps contain the mouseover and hyperlink information internally.
   *
   * @param imageFile the image URL that the image map refers to.
   * @param width the width of the image.
   * @param height the height of the image.
   * @param labelBounds an ArrayList of LabelBounds objects, obtained from a previously drawn Cgview
   *     object using the {@link Cgview#getLabelBounds()} method.
   * @param useOverlib whether or not to use the overlib.js JavaScript library for PNG and JPG image
   *     maps.
   */
  public void addImageMap(
      String imageFile, int width, int height, ArrayList labelBounds, Boolean useOverlib) {
    contents.add(
        "<img style=\"border:0\" src=\""
            + StringEscapeUtils.escapeHtml(imageFile)
            + "\" width=\""
            + Integer.toString(width)
            + "\" height=\""
            + Integer.toString(height)
            + "\" usemap=\"#cgviewmap\" />"
            + newline);
    contents.add("<map id=\"cgviewmap\" name=\"cgviewmap\">" + newline);

    // add areas
    Iterator i;
    i = labelBounds.iterator();
    while (i.hasNext()) {

      LabelBounds currentLabelBounds = (LabelBounds) i.next();
      Rectangle2D bounds = currentLabelBounds.getBounds();
      if ((currentLabelBounds.getUse() == true)
          && ((currentLabelBounds.getMouseover() != null)
              || (currentLabelBounds.getHyperlink() != null))) {

        contents.add(
            "<area shape=\"rect\" coords=\""
                + Integer.toString((int) Math.floor(bounds.getX() + 0.5d))
                + ","
                + Integer.toString((int) Math.floor(bounds.getY() + 0.5d))
                + ","
                + Integer.toString(
                    (int) Math.floor(bounds.getX() + 0.5d)
                        + (int) Math.floor(bounds.getWidth() + 0.5d))
                + ","
                + Integer.toString(
                    (int) Math.floor(bounds.getY() + 0.5d)
                        + (int) Math.floor(bounds.getHeight() + 0.5d))
                + "\" ");

        if (currentLabelBounds.getHyperlink() != null) {
          contents.add("href=\"" + currentLabelBounds.getHyperlink() + "\" ");
        }

        if ((currentLabelBounds.getMouseover() != null)
            && (!(currentLabelBounds.getMouseover().matches("\\S*")))) {
          if ((useOverlib != null) && (useOverlib.booleanValue())) {
            contents.add(
                "onmouseover=\"return overlib('"
                    + StringEscapeUtils.escapeJavaScript(currentLabelBounds.getMouseover())
                    + "');\" ");
            contents.add("onmouseout=\"return nd();\" ");
          } else {
            contents.add(
                "onmouseover=\"self.status='"
                    + StringEscapeUtils.escapeJavaScript(currentLabelBounds.getMouseover())
                    + "'; return true;\" ");
            contents.add("onmouseout=\"self.status=' '; return true;\" ");
          }
        }
        contents.add("/>" + newline);
      }
    }
    contents.add("</map>" + newline);
  }

  /**
   * Adds an image to this CgviewHTMLDocument. Use this method for adding a JPG or PNG image to a
   * CgviewHTMLDocument. To add an SVG image use {@link #addSVG}.
   *
   * @param imageFile the image URL.
   * @param width the width of the image.
   * @param height the height of the image.
   */
  public void addImage(String imageFile, int width, int height) {
    addImage(imageFile, width, height, "map");
  }

  /**
   * Adds an image to this CgviewHTMLDocument. Use this method for adding a JPG or PNG image to a
   * CgviewHTMLDocument. To add an SVG image use {@link #addSVG}.
   *
   * @param imageFile the image URL.
   * @param width the width of the image.
   * @param height the height of the image.
   * @param alt alternate text for the image
   */
  public void addImage(String imageFile, int width, int height, String alt) {
    contents.add(
        "<img style=\"border:0\" src=\""
            + StringEscapeUtils.escapeHtml(imageFile)
            + "\" width=\""
            + Integer.toString(width)
            + "\" height=\""
            + Integer.toString(height)
            + "\" alt=\""
            + StringEscapeUtils.escapeHtml(alt)
            + "\" />"
            + newline);
  }

  /**
   * Adds an image of a button to this CgviewHTMLDocument and links the button to another file.
   *
   * @param imageFile the button image URL.
   * @param linkFile the URL to be linked to the button.
   * @param altText a short description of the image.
   */
  public void addButton(String imageFile, String linkFile, String altText) {
    contents.add("<a href=\"" + StringEscapeUtils.escapeHtml(linkFile) + "\">");
    contents.add(
        "<img style=\"border:0\" src=\""
            + StringEscapeUtils.escapeHtml(imageFile)
            + "\" alt=\""
            + StringEscapeUtils.escapeHtml(altText)
            + "\" />");
    contents.add("</a>" + newline);
  }

  /**
   * Adds an image of a button to this CgviewHTMLDocument.
   *
   * @param imageFile the button image URL.
   * @param altText a short description of the image.
   */
  public void addButtonNoLink(String imageFile, String altText) {
    contents.add(
        "<img style=\"border:0\" src=\""
            + StringEscapeUtils.escapeHtml(imageFile)
            + "\" alt=\""
            + StringEscapeUtils.escapeHtml(altText)
            + "\" />"
            + newline);
  }

  /**
   * Adds an SVG image to this CgviewHTMLDocument.
   *
   * @param imageFile the SVG image URL.
   * @param width the width of the SVG image.
   * @param height the height of the SVG image.
   */
  public void addSVG(String imageFile, int width, int height) {
    // contents.add("<embed src=\"" + StringEscapeUtils.escapeHtml(imageFile) + "\"
    // type=\"image/svg+xml\" pluginspage=\"http://www.adobe.com/svg/viewer/install/\" width=\"" +
    // Integer.toString(width) + "\" height=\"" + Integer.toString(height) + "\" />" + newline);

    contents.add(
        "<object data=\""
            + StringEscapeUtils.escapeHtml(imageFile)
            + "\" type=\"image/svg+xml\" width=\""
            + Integer.toString(width)
            + "\" height=\""
            + Integer.toString(height)
            + "\" />"
            + newline);
  }

  /** Adds a br tag to this CgviewHTMLDocument. */
  public void addBreak() {
    contents.add("<br />" + newline);
  }

  /** Adds a non-breaking space to this CgviewHTMLDocument. */
  public void addSpace() {
    contents.add("&nbsp;");
  }

  /**
   * Adds a hyperlink to this CgviewHTMLDocument.
   *
   * @param name the text to appear in the link.
   * @param link the link URL.
   */
  public void addLink(String name, String link) {
    contents.add(
        "<a href=\""
            + StringEscapeUtils.escapeHtml(link)
            + "\">"
            + StringEscapeUtils.escapeHtml(name)
            + "</a>"
            + newline);
  }

  /**
   * Adds text to this CgviewHTMLDocument.
   *
   * @param text
   */
  public void addText(String text) {
    contents.add(StringEscapeUtils.escapeHtml(text) + "" + newline);
  }

  /**
   * Returns the contents of this CgviewHTMLDocument.
   *
   * @return an ArrayList of the entries in this CgviewHTMLDocument.
   */
  public ArrayList getContents() {
    return contents;
  }
}
