"""
    Driver for PDB2PQR

    This module takes a PDB file as input and performs optimizations
    before yielding a new PDB-style file as output.

    Ported to Python by Todd Dolinsky (todd@ccb.wustl.edu)
    Washington University in St. Louis

    Parsing utilities provided by Nathan A. Baker (baker@biochem.wustl.edu)
    Washington University in St. Louis

	Copyright (c) 2002-2010, Jens Erik Nielsen, University College Dublin; 
	Nathan A. Baker, Washington University in St. Louis; Paul Czodrowski & 
	Gerhard Klebe, University of Marburg

	All rights reserved.

	Redistribution and use in source and binary forms, with or without modification, 
	are permitted provided that the following conditions are met:

		* Redistributions of source code must retain the above copyright notice, 
		  this list of conditions and the following disclaimer.
		* Redistributions in binary form must reproduce the above copyright notice, 
		  this list of conditions and the following disclaimer in the documentation 
		  and/or other materials provided with the distribution.
		* Neither the names of University College Dublin, Washington University in 
		  St. Louis, or University of Marburg nor the names of its contributors may 
		  be used to endorse or promote products derived from this software without 
		  specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
	IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
	BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
	OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
	OF THE POSSIBILITY OF SUCH DAMAGE.

"""

__date__  = "5 April 2010"
__author__ = "Todd Dolinsky, Nathan Baker, Jens Nielsen, Paul Czodrowski, Jan Jensen, Samir Unni, Yong Huang"
__version__ = "1.6"


import string
import sys
import getopt
import os
import time
import httplib
import re
import glob
import tempfile
from src import pdb
from src import utilities
from src import structures
from src import routines
from src import protein
#from src import server
from src.pdb import *
from src.utilities import *
from src.structures import *
from src.definitions import *
from src.forcefield import *
from src.routines import *
from src.protein import *
from src.server import *
from src.hydrogens import *
from src.aconf import *
from StringIO import *
from main import *

def printHeader(pagetitle,have_opal=None,jobid=None):
    """
        Function to print html headers
    """
    if jobid:
        if have_opal:
            print "Location: querystatus.cgi?jobid=%s&typeofjob=opal\n" % (jobid,typeOfJob)
        else:
            print "Location: querystatus.cgi?jobid=%s&typeofjob=local\n" & (jobid,typeOfJob)

    #print "Content-type: text/html\n"
    print "<HTML>"
    print "<HEAD>"
    print "\t<TITLE>%s</TITLE>" % pagetitle
    print "\t<link rel=\"stylesheet\" href=\"%s\" type=\"text/css\">\n" % STYLESHEET
    print "</HEAD>"
    return

def redirector(name):
    """
        Prints a page which redirects the user to querystatus.cgi and writes starting time to file
    """

    starttimefile = open('%s%s%s/pdb2pqr_start_time' % (INSTALLDIR, TMPDIR, name), 'w')
    starttimefile.write(str(time.time()))
    starttimefile.close()

    string = ""
    string+= "<html>\n"
    string+= "\t<head>\n"
    string+= "\t\t<meta http-equiv=\"Refresh\" content=\"0; url=%squerystatus.cgi?jobid=%s&calctype=pdb2pqr\">\n" % (WEBSITE, name)
    string+= "\t</head>\n"
    string+= "</html>\n"
    return string

def mainCGI():
    """
        Main driver for running PDB2PQR from a web page
    """
    print "Content-type: text/html\n"
    import cgi
    import cgitb

    cgitb.enable()
    form = cgi.FieldStorage()
    ff = form["FF"].value 
    input = 0

    apbs_input = form.has_key("INPUT")
    typemap = form.has_key("TYPEMAP")
    neutraln = form.has_key("NEUTRALN")
    neutralc = form.has_key("NEUTRALC")

    if HAVE_PDB2PQR_OPAL=="1":
        have_opal = True
        # Opal-specific import statments
        from AppService_client import AppServiceLocator, getAppMetadataRequest, launchJobRequest, launchJobBlockingRequest, getOutputAsBase64ByNameRequest
        from AppService_types import ns0
        from ZSI.TC import String
    else:
        have_opal = False

    if have_opal:
        options = {}
        options["ff"] = ff
        fffile = None
        namesfile = None
    else:
        options = {"extensions":{}}
 
  
    if form.has_key("DEBUMP"):
        options["debump"] = 1
    else:
        options["debump"] = 0
    if form.has_key("OPT"):
        options["opt"] = 1
    else:
        options["opt"] = 0
    if form.has_key("PROPKA"):
        try:
            ph = float(form["PH"].value)
            if ph < 0.0 or ph > 14.0: raise ValueError
            options["ph"] = ph
        except ValueError:
             text = "The entered pH of %.2f is invalid!  " % form["PH"].value
             text += "Please choose a pH between 0.0 and 14.0."
             #print "Content-type: text/html\n"
             print text
             sys.exit(2)
    if form.has_key("PDBID"):
        pdbfile = getPDBFile(form["PDBID"].value)
        pdbfilename = form["PDBID"].value
    elif form.has_key("PDB"):
        pdbfile = StringIO(form["PDB"].value)
        pdbfilename = form["PDB"].filename
        pdbfilename = re.split(r'[/\\]',pdbfilename)[-1]
    if form.has_key("INPUT"):
        input = 1
        options["apbs"] = 1
    if form.has_key("USERFF"):
        if have_opal:
            ffname = form["USERFF"].filename
            ffname = re.split(r'[/\\]',ffname)[-1]
            if ffname[-4:] == ".DAT":
               ffname = ffname[:-4]
            fffile = StringIO(form["USERFF"].value)
            namesfile = StringIO(form["USERNAMES"].value)
            options["ff"] = ffname
            options["userff"] = fffile
            options["usernames"] = namesfile
        else:
            userff = StringIO(form["USERFF"].value)
            usernames = StringIO(form["USERNAMES"].value)
            options["ff"] = "user-defined"
            options["userff"] = userff
            options["usernames"] = usernames
    if form.has_key("FFOUT"):
        if form["FFOUT"].value != "internal":
            options["ffout"] = form["FFOUT"].value
    if form.has_key("CHAIN"):
        options["chain"] = 1
    if form.has_key("WHITESPACE"):
        options["whitespace"] = 1
    if form.has_key("TYPEMAP"):
        options["typemap"] = 1
    if form.has_key("NEUTRALN"):
        options["neutraln"] = 1
    if form.has_key("NEUTRALC"):
        options["neutralc"] = 1
    if form.has_key("LIGAND"):
        if have_opal:
            ligandfilename=str(form["LIGAND"].filename)
            ligandfilename=re.split(r'[/\\]',ligandfilename)[-1]

        # for Windows-style newline compatibility
        templigandfilename = tempfile.mkstemp()[1]
        templigandfile = open(templigandfilename,'w')
        templigandfile.write(form["LIGAND"].value)
        templigandfile.close()
        templigandfile = open(templigandfilename,'rU')
        if have_opal:
            options["ligand"] = templigandfile.read()
        else:
            templigandstring = templigandfile.read() # this variable is used again later to write this file to output
            options["ligand"] = StringIO(templigandstring)
            
        templigandfile.close()
        
    if not have_opal:
        pdbfilestring = pdbfile.read()
        pdblist, errlist = readPDB(StringIO(pdbfilestring))
        dummydef = Definition()
        dummyprot = Protein(pdblist, dummydef)
        if len(pdblist) == 0 and len(errlist) == 0:
            text = "Unable to find PDB file - Please make sure this is "
            text += "a valid PDB file ID!"
            #print "Content-type: text/html\n"
            print text
            sys.exit(2)
        elif dummyprot.numAtoms() > MAXATOMS and "opt" in options:
            text = "<HTML><HEAD>"
            text += "<TITLE>PDB2PQR Error</title>"
            text += "<link rel=\"stylesheet\" href=\"%s\" type=\"text/css\">" % STYLESHEET
            text += "</HEAD><BODY><H2>PDB2PQR Error</H2><P>"
            text += "Due to server limits, we are currently unable to optimize "
            text += "proteins of greater than MAXATOMS atoms on the server (PDB2PQR "
            text += "found %s atoms in the selected PDB file).  If you " % dummyprot.numAtoms()
            text += "want to forgo optimization please try the server again.<P>"
            text += "Otherwise you may use the standalone version of PDB2PQR that "
            text += "is available from the <a href=\"http://pdb2pqr.sourceforge.net\">"
            text += "PDB2PQR SourceForge project page</a>."
            text += "<script type=\"text/javascript\">"
            text += "var gaJsHost = ((\"https:\" == document.location.protocol) ? \"https://ssl.\" : \"http://www.\");"
            text += "document.write(unescape(\"%3Cscript src=\'\" + gaJsHost + \"google-analytics.com/ga.js\' type=\'text/javascript\'%3E%3C/script%3E\"));"
            text += "</script>"
            text += "<script type=\"text/javascript\">"
            text += "try {"
            text += "var pageTracker = _gat._getTracker(\"UA-11026338-3\");"
            for key in options:
                text += "pageTracker._trackPageview(\"/main_cgi/has_%s_%s.html\");" % (key, options[key])
            text += "pageTracker._trackPageview();"
            text += "} catch(err) {}</script>"
            text += "</BODY></HTML>"
            #print "Content-type: text/html\n"
            print text
            sys.exit(2)

    try:
        if have_opal:
            ligandFile=None
            ffFile=None
            namesFile=None
        #else:
        starttime = time.time()
        name = setID(starttime)

        os.makedirs('%s%s%s' % (INSTALLDIR, TMPDIR, name))
        apbsInputFile = open('%s%s%s/apbs_input' % (INSTALLDIR, TMPDIR, name),'w')
        apbsInputFile.write(str(apbs_input))
        apbsInputFile.close()
        typemapInputFile = open('%s%s%s/typemap' % (INSTALLDIR, TMPDIR, name),'w')
        typemapInputFile.write(str(typemap))
        typemapInputFile.close()

        if have_opal:
            myopts=""
            for key in options:
                if key=="opt":
                    if options[key]==0:
                        # user does not want optimization
                        key="noopt"
                    else:
                        # pdb2pqr optimizes by default, don't bother with flag
                        continue
                elif key=="debump":
                    if options[key]==0:
                        # user does not want debumping
                        key="nodebump"
                    else:
                        # pdb2pqr debumps by default, so change this flag to --nodebump
                        continue
                elif key=="ph":
                    val=options[key]
                    key="with-ph=%s" % val
                elif key=="ffout":
                    val=options[key]
                    key="ffout=%s" % val
                elif key=="ligand":
                    val=ligandfilename
                    key="ligand=%s" % val
                    ligandFile = ns0.InputFileType_Def('inputFile')
                    ligandFile._name = val
                    ligandFile._contents = options["ligand"]
                elif key=="apbs":
                    key="apbs-input"
                elif key=="chain":
                    key="chain"
                elif key=="whitespace":
                    key="whitespace"
                elif key=="typemap":
                    key="typemap"
                elif key=="ff":
                    val=options[key]
                    key="ff=%s" % val
                    if fffile:
                      ffFile = ns0.InputFileType_Def('inputFile')
                      ffFile._name = val + ".DAT"
                      ffFileString = fffile.read()
                      ffFile._contents = ffFileString
                    if namesfile:
                      namesFile = ns0.InputFileType_Def('inputFile')
                      namesFile._name = val + ".names"
                      namesFileString = namesfile.read()
                      namesFile._contents = namesFileString
                if key not in ["userff", "usernames"]:
                    myopts+="--"+str(key)+" "
            myopts+=str(pdbfilename)+" "
            if pdbfilename[-4:]==".pdb":
                myopts+="%s.pqr" % str(pdbfilename[:-4])
            else:
                myopts+="%s.pqr" % str(pdbfilename)
            appLocator = AppServiceLocator()
            appServicePort = appLocator.getAppServicePort(PDB2PQR_OPAL_URL)
            # launch job
            req = launchJobRequest()
            req._argList = myopts
            inputFiles = []
            pdbOpalFile = ns0.InputFileType_Def('inputFile')
            pdbOpalFile._name = pdbfilename
            pdbOpalFile._contents = pdbfile.read()
            pdbfile.close()
            inputFiles.append(pdbOpalFile)
            if ligandFile:
              inputFiles.append(ligandFile)
            if ffFile:
              inputFiles.append(ffFile)
            if namesFile:
              inputFiles.append(namesFile)
            req._inputFile=inputFiles
            try:
                resp=appServicePort.launchJob(req)
            except Exception, e:
                printHeader("PDB2PQR Job Submission - Error")
                print "<BODY>\n<P>"
                print "There was an error with your job submission<br>"
                print "</P>"
                print "<script type=\"text/javascript\">"
                print "var gaJsHost = ((\"https:\" == document.location.protocol) ? \"https://ssl.\" : \"http://www.\");"
                print "document.write(unescape(\"%3Cscript src=\'\" + gaJsHost + \"google-analytics.com/ga.js\' type=\'text/javascript\'%3E%3C/script%3E\"));"
                print "</script>"
                print "<script type=\"text/javascript\">"
                print "try {"
                print "var pageTracker = _gat._getTracker(\"UA-11026338-3\");"
                for key in options:
                    print "pageTracker._trackPageview(\"/main_cgi/has_%s_%s.html\");" % (key, options[key])
                print "pageTracker._trackPageview();"
                print "} catch(err) {}</script>"
                print "</BODY>"
                print "</HTML>"
                sys.exit(2)
            #printHeader("PDB2PQR Job Submission",have_opal,jobid=resp._jobID)
            pdb2pqrOpalJobIDFile = open('%s%s%s/pdb2pqr_opal_job_id' % (INSTALLDIR, TMPDIR, name), 'w')
            pdb2pqrOpalJobIDFile.write(resp._jobID)
            pdb2pqrOpalJobIDFile.close()
            print redirector(name)
            if options.has_key("userff"):
                options["userff"] = ffFileString
            if options.has_key("usernames"):
                options["usernames"] = namesFileString
            # Recording CGI run information for PDB2PQR Opal
            pdb2pqrOpalLogFile = open('%s%s%s/pdb2pqr_opal_log' % (INSTALLDIR, TMPDIR, name), 'w')
            pdb2pqrOpalLogFile.write(str(options)+'\n'+str(ff)+'\n'+str(os.environ["REMOTE_ADDR"]))
            pdb2pqrOpalLogFile.close()

        else:
            #pqrpath = startServer(name)
            statusfile = open('%s%s%s/pdb2pqr_status' % (INSTALLDIR, TMPDIR, name), 'w')
            statusfile.write('running')
            statusfile.close()


            pid = os.fork()
            if pid:
                print redirector(name)
                sys.exit()
            else:
                currentdir = os.getcwd()
                os.chdir("/")
                os.setsid()
                os.umask(0)
                os.chdir(currentdir)
                os.close(1) # not sure if these
                os.close(2) # two lines are necessary
                pqrpath = '%s%s%s/%s.pqr' % (INSTALLDIR, TMPDIR, name, name)
                options["outname"] = pqrpath
                options["verbose"] = ""
                orig_stdout = sys.stdout
                orig_stderr = sys.stderr
                sys.stdout = open('%s%s%s/pdb2pqr_stdout.txt' % (INSTALLDIR, TMPDIR, name), 'w')
                sys.stderr = open('%s%s%s/pdb2pqr_stderr.txt' % (INSTALLDIR, TMPDIR, name), 'w')
                header, lines, missedligands = runPDB2PQR(pdblist, ff, options)
                sys.stdout.close()
                sys.stderr.close()
                sys.stdout = orig_stdout
                sys.stderr = orig_stderr

                endtimefile = open('%s%s%s/pdb2pqr_end_time' % (INSTALLDIR, TMPDIR, name), 'w')
                endtimefile.write(str(time.time()))
                endtimefile.close()

                pqrfile = open(pqrpath, "w")
                pqrfile.write(header)
                for line in lines:
                    # Adding whitespaces if --whitespace is in the options
                    if "whitespace" in options.keys() and options["whitespace"] == 1: 
                        if line[0:4] == 'ATOM':
                            newline = line[0:16] + ' ' + line[16:38] + ' ' + line[38:46] + ' ' + line[46:]
                            pqrfile.write("%s\n" % string.strip(newline))
                        elif line[0:6] == 'HETATM':
                            newline = line[0:16] + ' ' + line[16:38] + ' ' + line[38:46] + ' ' + line[46:]
                            pqrfile.write("%s\n" % string.strip(newline))
                    else: 
                        pqrfile.write("%s\n" % string.strip(line))
                pqrfile.close()
                        
                if input:
                    from src import inputgen
                    from src import psize
                    method = "mg-auto"
                    size = psize.Psize()
                    size.parseInput(pqrpath)
                    size.runPsize(pqrpath)
                    async = 0 # No async files here!
                    myinput = inputgen.Input(pqrpath, size, method, async)
                    myinput.printInputFiles()
                    myinput.dumpPickle()
                            
                endtime = time.time() - starttime
                #createResults(header, input, name, endtime, missedligands)
                logRun(options, endtime, len(lines), ff, os.environ["REMOTE_ADDR"])
                #printHeader("PDB2PQR Job Submission",have_opal,jobid=name)
                if form.has_key("LIGAND"):
                    outputligandfile = open('%s%s%s/%s.mol2' % (INSTALLDIR,TMPDIR, name, name),'w')
                    outputligandfile.write(templigandstring)
                    outputligandfile.close()
                outputpdbfile = open('%s%s%s/%s.pdb' % (INSTALLDIR,TMPDIR,name,name),'w')
                outputpdbfile.write(pdbfilestring)
                outputpdbfile.close()

                statusfile = open('%s%s%s/pdb2pqr_status' % (INSTALLDIR, TMPDIR, name), 'w')
                statusfile.write('complete\n')
                filelist = glob.glob('%s%s%s/%s*' % (INSTALLDIR, TMPDIR, name, name))
                for filename in filelist:
                    statusfile.write(filename+'\n')
                statusfile.close()


    except StandardError, details:
        print details
        createError(name, details)
