#! /usr/bin/env python


# todo: need to check file size and make sure content looks valid, so the site
# isn't abused.


import re
import cgi
import cgitb
import os.path
import stat
import md5

from time import time


#cgitb.enable(display=1, logdir="/tmp")

# To separate HTML from code, we will include a few files.
HTML_HEADER = 'header'
HTML_BODY = 'main_content'
HTML_FOOTER = 'footer'
HTML_SUBMIT = 'submit'
HTML_FAQ = 'faq'
HTML_ABOUT = 'about'

# dev
#OUTPUT_ROOT_DIRECTORY = "/tmp/pplacer/"
# prod
OUTPUT_ROOT_DIRECTORY = "/extra/data/matsen/pplacer/jobs/"


# Functions

def read_html_file(file_name):
    try: 
        f = open(file_name, 'r')
        html = f.read()
        f.close()
    except IOError:
        print 'Error attempting to read file: ' + file_name
        
    return html


# Print out content type so browser knows what it will be receiving.
# Also print out some tags to wrap the html body.
def print_header():
    print "Content-Type: text/html\n\n"
    html = read_html_file(HTML_HEADER)
    print html
    
# Print out some closing html tags to the browser. 
def print_footer():
    html = read_html_file(HTML_FOOTER)
    print html

# Check to see that the submit button was clicked.  We should see
# at least one key get posted back.
def post_check(form):
    if (len(form) > 0):
        return True
    else:
        return False


# Instead of reading potentially huge files into memory at this point,
# simply check to see if any values are null.
def validate_input(form):
    required = set()

    if not (form.has_key("reftree")):
        required.add('refTreeRequired')
    if not (form.has_key("refalign")):
        required.add('refAlignRequired')
    if not (form.has_key("refstats")):
        required.add('refStatsRequired')        
    if not (form.has_key("fasta")):
        required.add('fastaRequired')
    if not (form.has_key("email")):
        # Basic regex check here
        required.add('emailRequired')

    return required
    


# If even one field is empty or not valid, we will need to repopulate all fields
# for which we received input.  I've discovered this function won't work for
# upload input fields, given it is a browser security risk.  
# .value reads the whole file into memory when it is a file, so it would only
# make sense for non-file fields 
#
def invalid_repopulate(form):
    input_fields = dict()
    
    if (form.has_key("reftree")):
        input_fields["reftree"] = form["reftree"].value
    if (form.has_key("refalign")):
        input_fields["refalign"] = form["refalign"].value       
    if (form.has_key("refstats")):
        input_fields["refstats"] = form["refstats"].value
    if (form.has_key("fasta")):
        input_fields["fasta"] = form["fasta"].value 
    if (form.has_key("email")):
        input_fields["email"] = form["email"].value       

    return input_fields

def do_write_file(form, field, filename):
    out = open(filename, 'w')
    out.write(form[field].value)
    out.close()


# Upload files and create a control file with job information.
def do_upload(form, unique_id):

    directory = OUTPUT_ROOT_DIRECTORY + "/" + unique_id 
    if not os.path.isdir(directory):
        os.mkdir(directory)

    # Create a control file with information about this job.
    control_file = open(directory + '/' + 'CONTROL', 'w')
    control_text = get_control_file(form)
    control_file.write(control_text)
    control_file.close()

    # Write out files
    do_write_file(form, 'reftree', directory + '/' + form["reftree"].filename)
    do_write_file(form, 'refalign', directory + '/' + form["refalign"].filename)
    do_write_file(form, 'refstats', directory + '/' + form["refstats"].filename)
    do_write_file(form, 'fasta', directory + '/' + form["fasta"].filename)
    


# Create text that will be written out to a control file.
def get_control_file(form):
    control = "tree_file=" + form["reftree"].filename + "\n"
    control += "align_file=" + form["refalign"].filename + "\n"
    control += "stats_file=" + form["refstats"].filename + "\n"
    control += "fasta_file=" + form["fasta"].filename + "\n"
    control += "calculate_probabilities=" + form.getfirst("postprob", "False") + "\n"
    control += "uniform_prior=" + form.getfirst("uniprior", "False") + "\n"
    control += "model_frequencies=" + form.getfirst("modelfreqs", "False") + "\n"
    control += "maximum_strikes=" + form["maxstrikes"].value + "\n"
    control += "strike_box_size=" + form["strikebox"].value + "\n"
    control += "maximum_pitches=" + form["maxpitches"].value + "\n"
    control += "email=" + form["email"].value + "\n"
    control += "time_queued=" + str(time()) + "\n"
    control += "time_started=\n"
    control += "time_finished=\n"
    control += "status=Queued\n"
    control += "pid=\n"

    return control


    
# Print the form out, with content-display decisions made based on form values.
def print_form():
    os.umask(003)
    form = cgi.FieldStorage()
    if (form.has_key("page")):
        if (form["page"].value == "about"):
            html = read_html_file(HTML_ABOUT)
            print html
        elif (form["page"].value == "faq"):
            html = read_html_file(HTML_FAQ)
            print html
        else:
            print "A catastrophic error occurred."
    elif (post_check(form) == True):
        required = validate_input(form)
        html = read_html_file(HTML_BODY)
        if (len(required) > 0):
            for i in set(required):
                # Set required to red css class
                replace = re.compile(i, re.M)
                html = replace.sub('red', html)
                
##            input_fields = invalid_repopulate(form)
##            for key, value in input_fields.iteritems():
##                value_regex = "name=\"" + key + "\" value=" + "\"\""
##                value_replaced = "name=\"" + key + "\" value=" + "\"" + value  + "\""
##                replace_input = re.compile(value_regex, re.M)
##                # This won't actually work as far as most browsers are concerned,
##                # because allowing the server to tell you what files you will upload
##                # is a security risk.  Client-side Javascript is really the only way
##                # to do this.
##                html = replace_input.sub(value_replaced, html)          

            print html
        else:
            # Form seems valid, do some stuff.

            #Create a unique job id
            unixTime = time()
            unique_id = str(unixTime) + form["email"].value
            unique_id = md5.new(unique_id).hexdigest()
            directory = OUTPUT_ROOT_DIRECTORY + "/" + unique_id
            replace_regex = "##ID##"
            replace_id = re.compile(replace_regex, re.M)
            try:
                do_upload(form, unique_id)
                html_submit = read_html_file(HTML_SUBMIT)
                html_submit = replace_id.sub(unique_id, html_submit)
                print html_submit
                #os.chmod(directory, stat.S_IRGRP | stat.S_IXGRP | stat.S_IWGRP | stat.S_ISGID | stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
            except:
                print "An error occurred. Use your browser's back button and try resubmitting the form."
                
    else:
        html = read_html_file(HTML_BODY)        
        print html



# Our entry point
def main():
    print_header()
    print_form()
    print_footer()

if __name__ == '__main__':
    main()

