#
# html_util
#
# Utility functions to generate (basic) HTML output from CGI
# scripts.
#
# author:  Asfandyar Qureshi
# copying: MIT license

import sys

HTML_HEADER=\
"""
<html>
<head>

<title>
%s
</title>

<META HTTP-EQUIV="pragma" CONTENT="no-cache">

<style>
<!--
body { 
  background-color: #ffffff;
  font-family: verdana, arial, helvetica, sans-serif;
}

div { font-size: 13px; }
p { font-size: 13px; }
ol { font-size: 13px; }
ul { font-size: 13px; }

h1 { font-size: 24px; font-weight: bold; }
h2 { font-size: 18px; font-weight: bold; border-bottom: 2px solid #000000; }
h3 { font-size: 15px; font-weight: bold; }

pre { 
 font-family: "courier new", courier, mono; 
 color: #400040; 
 font-size: 13px;
 font-weight: bold;
}

li { list-style: square; }

div {
  padding-left:16px;
  padding-top:8px;
  padding-bottom:8px;
  padding-right:16px;
  text-align: left
}

div.white {
  border: 1px solid #bbbbbb;
  background: #ffffff;  
}

div.gray {
  border: 1px solid #bbbbbb;
  background: #eeeeee;  
}

div.yellow {
  border: 1px solid #bbbbbb;
  background: #ffffe4;  
}

div.green {
  border: 1px solid #bbbbbb;
  background: #ddffaa;
}

div.orange {
  border: 1px solid #bbbbbb;
  background: #ffe787;
}

div.purple {
  border: 1px solid #000000;
  color: #ffffff;
  background: #491c3d; 
}

div.darkred {
  border: 1px solid #000000;
  color: #ffffff;
  background: #8a0a0a;
}

div.darkgray {
  border: 1px solid #000000;
  color: #ffffff;
  background: #aaaaaa;  
}

div.black {
  color: #ffffff;
  background: #000000;
}

//-->
</style>
</head>

<body>
<div style="padding-right: 32px; padding-left: 32px;">
"""

HTML_FOOTER=\
"""
</div>
</body>
</html>
"""

class HTMLDocument:
    """
    A HTML document.

    Provides functions for constructing a HTML documents with stylized text,
    forms, and images. Also provides functions for outputting that HTML
    from a CGI script.    
    """
    # body : list of strings (represents a HTML fragment)
    
    def __init__(self, title):
        """
        Creates an empty HTML document with the given title.
        """
        self.body = [HTML_HEADER % title]

    def print_html(self):
        """
        Outputs the HTML.

        requires:
           for valid HTML, all begun tags should be ended.
        """
        if not self.body[-1] == HTML_FOOTER:  # make sure HTML footer is there
            self.body.append(HTML_FOOTER)            
        
        #print 'Content-type: text/html\n\n'
        sys.stdout.write('Content-type: text/html\n\n')

        for fragment in self.body:
            sys.stdout.write(fragment)
            #print fragment,

    def _was_printed(self):
        """
        Returns True if print_html has been called at least once,
        False otherwise.
        """
        return (self.body[-1] == HTML_FOOTER)        

    #
    # helper functions
    #
    def _add_html_escapes(self, text):
        text = text.replace('&', '&amp;')
        text = text.replace('"', '&quot;')
        text = text.replace("'", '&#39;')
        text = text.replace('<', '&lt;')
        text = text.replace('>', '&gt;')
        return text

    #
    # basic text stuff
    #

    def write_text(self, text, color=None, size=None, bold=False, italic=False, underlined=False, typewriter=False):
        """
        Writes a fragment of formatted text.

        The following formatting options exist:
            color: string (e.g., 'red', 'blue', 'orange', or html hex '#ffffff'
            size: string (e.g., '12px', '10pt')
            bold: boolean
            italic: boolean
            typewriter: boolean
        """
        html=""
        
        # add format tags
        if bold: html += "<b>"
        if italic: html += "<i>"
        if underlined: html += "<u>"
        if typewriter: html += "<tt>"

        # style (size + color)
        if size or color:
            html += "<font style='"
            if size:  html += "font-size: %s;" % str(size)
            if color: html += "color: %s;" % str(color)
            html += "'>"        

        # HTML escapes
        text = self._add_html_escapes(text)

        # add text
        html += text

        # close style
        if size or color:
            html += "</font>"
        
        # close format tags
        if typewriter: html += "</tt>"
        if underlined: html += "</u>"        
        if italic: html += "</i>"        
        if bold: html += "</b>"

        # add to document
        self.body.append(html)            

    def write_pre_text(self, text):
        """
        Write pre-formatted text (e.g., python code).
        """
        html ="<pre>%s</pre>" % text
        self.body.append(html)

    def new_paragraph(self):
        """
        Starts a new paragraph in the HTML.
        """
        self.body.append("\n<p>\n")

    def newline(self):
        self.body.append("<br>\n")

    def new_section(self, section_title):
        """
        Starts a new section in the HTML.
        """
        txt = self._add_html_escapes(section_title)
        self.body.append("\n<h1>%s</h1>\n" % txt)        

    def new_subsection(self, section_title):
        """
        Starts a new sub-section in the HTML.
        """
        txt = self._add_html_escapes(section_title)
        self.body.append("\n<h2>%s</h2>\n" % txt)
        
    def new_subsubsection(self, section_title):
        """
        Starts a new sub-sub-section in the HTML.
        """
        txt = self._add_html_escapes(section_title)
        self.body.append("\n<h3>%s</h3>\n" % txt)

    #
    # divs and align
    #
    def begin_center(self):
        """
        Begin a block horizontally centered.

        You will need to call end_center.
        """
        self.body.append("\n<center>\n")

    def end_center(self):
        """
        Begin a block horizontally centered.

        You will need to call end_center.
        """
        self.body.append("\n</center>\n")        

    def begin_div_block(self, type=None, width=None):
        """
        Begin a division with some specific formatting style
        and width.

        valid values for type:
             None:   white background, no borders
            'white':   white backround box
            'gray':    light gray background box
            'yellow':  pale yellow background box
            'green':   green background box
            'orange':  orange background box
            'purple':  dark purple box with white text
            'darkred': dark red background box, white foreground
            'darkgray':dark gray background box, white foreground
            'black':   black background box, white foreground

        width: in percent or pixels (string), e.g., 100% or 100px 
        """
        if type:
            type = str(type)
            if not type in ['white', 'gray', 'yellow', 'green', 'orange', 'purple', 'darkred', 'darkgray', 'black']:
                raise ValueError(type)

        html = "\n<div"
        if type: html += " class='%s'" % type
        if width: html += " style='width:%s'" % str(width)
        html += ">\n"
        
        self.body.append(html)
        
    def end_div_block(self):
        self.body.append("\n</div>\n")

    #
    # lists
    #
    def write_numbered_list(self, list):
        """
        Adds the elements of the list as a numbered list,
        with each element on a new line.
        """
        html="\n<ol>\n"
        for item in list:
            txt = self._add_html_escapes(str(item))
            html += "<li>%s</li>\n" % txt
        html += "</ol>\n"
        
        self.body.append(html)

    def write_bullet_list(self, list):
        """
        Adds the elements of the list as a bullet-ed list,
        with each element on a new line.
        """
        html="\n<ul>\n"
        for item in list:
            txt = self._add_html_escapes(str(item))
            html += "<li>%s</li>\n" % txt
        html += "</ul>\n"
        
        self.body.append(html)        

    #
    # links
    #
    
    def begin_linked_part(self, link_url):
        """
        Starts a part of the HTML that links to another document.

        You will need to call end_linked_part.
        """
        self.body.append("<a href='%s'>" % link_url)
        
    def end_linked_part(self):
        """
        Ends a link.
        """
        self.body.append("</a>")

    #
    # images
    #

    def add_image(self, image_url, image_text=None, image_width=None, image_height=None):
        """
        Adds an image with some descriptive text,
        in case the image cannot be loaded.

        image_width, image_height: (string) pixels or percent, e.g. 150px or 70%
        """
        html = "\n<img src='%s'" % image_url
        if image_text: html += " alt='%s'" % str(image_text)
        if image_width: html += " width='%s'" % str(image_width)
        if image_height: html += " height='%s'" % str(image_height)
        html += ">\n"
        
        self.body.append(html)

    #
    # forms
    #
    def begin_form(self, action):
        """
        Begins a form. You can add as many inputs to the form as you
        want, but you must eventually end the form

        action:
           the action to undertake when the user submits the form.
        
           action can either be 'mailto:' followed by an email address,
           or it can be the name of a script that will be invoked
           with a HTTP post.
        """
        if action[:7].lower() == 'mailto:':
            self.body.append("\n<form action='%s' method='post' enctype='text/plain'>\n" % action)
        else:
            self.body.append("\n<form action='%s' method='post' enctype='multipart/form-data'>\n" % action)

    def end_form(self):
        self.body.append("\n</form>")
        
    def form_add_button_submit(self, button_caption="Submit"):
        html = "<input type='submit' value='%s'>\n" % button_caption
        self.body.append(html)

    def form_add_button_reset(self, button_caption="Reset"):
        html = "<input type='reset' value='%s'>" % button_caption
        self.body.append(html)

    def form_add_field_text(self, id, default_value="", size="60"):
        """
        A text field accepts a single-line text input from the user. This
        is useful for asking the user for things like email and names, etc.

        default_value: initial text
        size: width of text box, in characters
        """
        default_value = self._add_html_escapes(default_value)
        html="<input type='text' name='%s' value='%s'>\n" % (id, str(default_value))
        self.body.append(html)

    def form_add_field_checkbox(self, id, checked=False):
        """
        A checkbox field is either true or false.
        """
        html="<input type='checkbox' name='%s'" % id
        if checked: html += " checked"
        html += ">\n"
        self.body.append(html)


    def form_add_field_textarea(self, id, rows=5, cols=60, default_value=""):
        """
        A text-area field accepts a multi-line text input. This is useful
        for asking more open-ended questions, e.g., comments.
        """
        default_value = self._add_html_escapes(default_value)        
        html="<textarea name='%s' rows='%s' cols='%s'>\n%s\n</textarea>\n" % (id, str(rows), str(cols), str(default_value))
        self.body.append(html)
        
    def form_add_field_multichoice(self, id, choice_id=[], choices=[], selected=None, newlines=True):
        """
        Adds a multiple choice field, as a set of radio inputs.

        requires: len(choice_id) == len(choices)

        choice_id:
            the internal identifier for each option. this indentifier
            will show up in the submitted form.
            
        choices:
            the text displayed for each choice

        selected:
            None
            or the choice_id that should be initially selected

        newlines:
            if this is True, each option is on a new line.
        """
        html=""
        for i in range(len(choice_id)):
            txt = self._add_html_escapes(choices[i])
            #html += "<input type='radio' name='%s' value='%s'> %s" % (id, str(choice_id[i]), txt)
            html += "<input type='radio' name='%s' value='%s'" % (id, str(choice_id[i]))
            if choice_id[i] == selected: html += " checked"
            html += "> %s" % txt
            
            if newlines: html += "<br>\n"
        self.body.append(html)
    
    def form_add_field_dropdown_choice(self, id, choice_id=[], choices=[]):
        """
        Adds a multiple choice field, as a drop-down list.

        requires: len(choice_id) == len(choices)

        choice_id:
            the internal identifier for each option. this indentifier
            will show up in the submitted form.
            
        choices:
            the text displayed for each choice           
        """
        html="<select name='%s'>" % id
        for i in range(len(choice_id)):
            txt = self._add_html_escapes(choices[i])
            html += "<option value='%s'>%s</option>\n" % (str(choice_id[i]), txt)
        html += "</select>\n"
        self.body.append(html)

    def form_add_field_file(self, id, size="40"):
        """
        Adds a field that the user can use to upload a file.
        """
        html="<input type='file' name='%s' size='%s'" % (id, str(size))
        self.body.append(html)