# -*- coding: utf-8 -*-
#
# ccwatcher (http://ccwatcher.sourceforge.net/)
# Copyright (C) 2009-2013 Xaver Wurzenberger <xaverxn at users.sourceforge.net>
#
# This program is free software; you can redistribute and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, 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
# General Public License for more details.

"""ccw_parser.py
"""


import os
import sys
import time
import logging
import tempfile


#I'll have to decide what goes into dict_settings and what doesn't. Proposal: What shouldn't be in the ini File doesn't go into the dict (because this might happen easily). Then str_basepath would e.g. be kicked out

class fileParser(object):
    """
    This class holds together all relevant information (=variables, open files), parses them and plots on demand.
    """

    def __init__(self, dict_settings):

        #START: these are not reset'ed (remember a reset() takes place now on EVERY open action)
        self.list_recent_filenames = [] #for recent files list in GUI
        self.int_current_file_index = -1 # also for CLI/GUI: Know when all files have been parsed #needed here already?
        self.filehandle = None

        from ccw_messages import list_verbosity, list_plot_mode
        self.list_verbosity = list_verbosity
        self.list_plot_mode = list_plot_mode

        self.log = logging.getLogger('parserlogger')
        if dict_settings.get('num_verbosity', None) is not None:
            self.log.setLevel(self.list_verbosity[dict_settings['num_verbosity']]) #doesn't make sense to call this on every reset()

        #the str_plotfilename should stay here and not in ccw_gnupy so the ccw_gui can ask its name
        
        self.str_plotfilename = os.path.join(tempfile.gettempdir(), str(os.getpid())+'plot.png') #has to be before self.openInputFile() and reset()
        # TODO: os.getpid might not work on MacOS systems; check and replace if needed
        if sys.platform.startswith('win'): #win32
            for c in ['\\', '\"']:
                self.str_plotfilename = self.str_plotfilename.replace(c, '\\' + c) # needed for gnuplot(.py?) output direction (?)
        
        self.forwardLogText = False
        #END: these are not reset'ed

        if sys.platform.startswith('win') and not dict_settings.get('use_gui', False):
            self.log.warning('Command-line (ASCII) plotting does not work on Windows systems due to Gnuplot limitations!')
            
        self.dict_settings = {}
        self.update_settingsdict(dict_settings) #seems like a clever way of defining initial (empty) variables to me

        if self.dict_settings['str_plot_system'] != 'gnuplot' and self.dict_settings.get('use_gui', False):
            from ccwatcher_gui import ccw_qwt #implement a fallback here?
            self.instance_plotter = ccw_qwt.CcwQwtPlotter() #THIS should probably NOT be reset()ed

        self.reset()
        #no automatic opening of input files: happens only on demand



    def update_settingsdict(self, dict_newsettings):
        """ATM Only needed for GUI:SettingsDialog"""
        
        self.dict_settings.update(dict_newsettings) #instead of replace so 'str_basepath' and such don't get lost
        
        if self.dict_settings.get('num_verbosity', None) is not None:
            self.log.setLevel(self.list_verbosity[self.dict_settings['num_verbosity']])        



    def reset(self): #as reset is now called on EVERY open action, think about if we really need all of the cleaning stuff
        """Reset everything that can be reseted during multifile-loads. The rest is reseted in setInputFiles (so ONLY when a completely new start happens) (or openNextFile for things that depend on multifile mode?)."""

        self.num_adf_subversion = 0

        self.str_curr_point_group = ""
        self.str_num_atoms = ""
        self.str_curr_step = "" #curr_step is the string with the info that Gaussian gives as e.g. "Step number  10 out of 11 on scan point 2 out of 16" 
        
        #cannot use setter here because there is no tuple_files yet?!
        self.logfile_input_lastsize = 0
        self.str_logfile_lasttime = 0
        self.str_joblast_lasttime = 0
        self.file_terminated = False
        self.filetype = ''
        self.lst_bestpoints = [[0]]
        
        self.cleanup()
        
        if self.dict_settings['str_plot_system'] == 'gnuplot' or not self.dict_settings.get('use_gui', False):
            import ccw_gnupy
            self.instance_plotter = ccw_gnupy.Gnuplot(self.dict_settings['str_gnuplot_path']) # a new instance is needed since the old one is closed in removePlotfile() (because of Windows issues?)
        else:
            self.instance_plotter.pltr.clear()
            self.instance_plotter.pltr.replot()


        self.instance_plotter.setTitle(self.list_plot_mode[self.dict_settings['num_plot_mode']])
#        if hasattr(self, 'use_myparser') and self.use_myparser == False:
        self.instance_plotter.setXAxisLabel("Cycles") #ORCA uses the word cycles, G09 mixes cycles+steps, others?

        if self.dict_settings['use_gui'] == True:
            self.instance_plotter.commands('set term png small size 600,350') # 'set output' command seems to create the file -> not called outside of plot()

        else:
            self.instance_plotter.commands('set term dumb 120 25') 



    def setInputFiles(self, tuple_files):
        """Store a tuple of input files in dict_settings. Then call openNextFile to open them consecutively. Don't call openInputFiles directly!
            This also resets the values which reset() doesn't (so multifile-loads aren't discontinued): preservedSCF,num_added_cycles,list_scf"""

        if self.dict_settings.get('use_gui', False) == True and self.dict_settings.get('no_guess_mode', False) == True:
            if not len(tuple_files):
                tuple_files = ("", ) #needed so we're not kicked out at the end of this function
        elif type(tuple_files) == str or len(tuple_files) == 0: #either pathname or nothing is given: run guessLogFile
            if type(tuple_files) == str and os.path.isdir(tuple_files):
                tuple_files = self.guessLogFile(tuple_files)
            elif len(tuple_files) == 0:
                if os.path.exists('ccwatcher.py') and self.dict_settings.get('str_lastpath',  '') and os.path.isdir(self.dict_settings.get('str_lastpath',  '')):
                    dirname = self.dict_settings['str_lastpath']
                else:
                    dirname = os.getcwd()
                tuple_files = self.guessLogFile(dirname)        #This gives a tuple with an empty string (len=1!!!) if nothing is found.        

            if len(tuple_files[0]): #Attention: len(("",)) is also 1!
                self.log.info("Log file(s) found  - Trying to proceed with:")
                for name in tuple_files:
                    print "\t",name
      
      
        if len(tuple_files[0]): #Attention: len(("",)) is also 1!
            self.tuple_files = tuple_files
            self.int_current_file_index = -1
            self.preservedSCF = []
            self.num_current_cycle = 0
            self.list_scf = []
            self.num_scf_entries_last_plotted = 0
        elif tuple_files[0] is '' and self.dict_settings.get('use_gui', False) == True: #Attention: len(("",)) is also 1!
            self.tuple_files = tuple_files #Attention: setting empty tuple
            self.log.warning("No file found! Starting GUI 'empty'...")
        else:
            self.tuple_files = tuple_files #Attention: setting empty tuple
            self.log.critical("No file found: Could not determine which file to load. Check your input. Exiting...")


    def guessLogFile(self, str_dirname):
        import glob

        os.chdir(str_dirname) #current_dir is NOT set back ATM. Does that matter?
        if os.path.exists('gradient'): #priority so far: 1)gradient file 2)out or log file 3)gradient in subdir
            tuple_recentfile = (os.path.abspath('gradient'), )
        else:
            list_files = [(os.path.getmtime(x), x) for x in glob.glob(os.path.join(os.getcwd(), "*.log*"))]
            list_files.extend([(os.path.getmtime(x), x) for x in glob.glob(os.path.join(os.getcwd(), "*.out"))])
            if list_files:
                list_files.sort()
                tuple_recentfile = (list_files[-1][1], )
            else:
                list_files=[]
                for dir in os.walk('.').next()[1]:
                    if os.path.exists(os.path.join(dir,'control')):
                        list_files.append(os.path.join(os.path.abspath(dir),'gradient'))
                list_files.sort()
                if len(list_files) > 0:
                    return tuple(list_files)
                else: # empty string is better than None so we can start the GUI empty (bc tuple_inputnames[0][-8:] does not break)
                    return ("", ) # old: None, )
        return tuple_recentfile


    def openNextFile(self):
        """Works with a tuple (so already parsed files are still available in case of a reload) and an index to know which is the current"""
        
        self.reset()

        if len(self.tuple_files) > self.int_current_file_index + 1:
            self.int_current_file_index += 1

            if self.dict_settings['num_multifile_opt'] == 1:# in (0, 1):
                self.num_scf_entries_last_plotted = 0
                #self.num_added_cycles = 0
                self.num_current_cycle = 0
                if self.list_scf:# and self.preservedSCF:
                    self.preservedSCF.append(self.list_scf)
                self.list_scf = []

            if self.dict_settings['num_multifile_opt'] in (0,  2):
                self.preservedSCF = []
    
            returnvalue = self.openInputFile(self.tuple_files[self.int_current_file_index])

            return returnvalue
        
        else:
            return 0


    def openInputFile(self, filename):
        """reset() happens in openNextFile already"""
        import fileinput #move this further down to where it is needed?
                
        self.filetype = ''
        try:

            if filename[-8:] == 'gradient':
                self.filetype = 'Turbomole'
#                self.filehandle = fileinput.input(filename, openhook=fileinput.hook_compressed)

#            elif self.dict_settings.get('force_gaussian', False) == True:
#                self.filetype = 'Gaussian'
#                self.filehandle = fileinput.input(filename, openhook=fileinput.hook_compressed)
                
            else:
                
                if os.path.splitext(filename)[1] == ".zip": #would we need a seek(0) / re-open approach here, too? seems to work withought though
                    import zipfile,  StringIO
                    zip = zipfile.ZipFile(filename, "r")
                    assert len(zip.namelist()) == 1, "ERROR: Zip file contains more than 1 file"
                    self.filehandle = StringIO.StringIO(zip.read(zip.namelist()[0]))
                    
                elif os.path.splitext(filename)[1] in (".gz", ".bz2"):
                    self.filehandle = fileinput.input(filename, openhook=fileinput.hook_compressed) #this does not really work when the file is updated->do not use for uncompressed files
                    
                else: #we need to raise an error in case it is not openable (permissions, http, 7zip,...)
                    self.filehandle = open(filename,'r') # 'fileinput' does not really work when the file is updated->do not use for uncompressed files

                num_line = 0
                for line in self.filehandle:
                    if num_line > 999:
                        self.log.error("No keyword found in the first 1000 lines!")
                        break #needed
                    else:
                        num_line +=1
#                        if line.find("Amsterdam Density Functional") >= 0:
                        if line.find("ADF") >= 0:
                            self.filetype = 'ADF'
                            if line.find("2012.") >= 0:
                                self.num_adf_subversion = 2012
                            elif line.find("2007.") >= 0:
                                self.num_adf_subversion = 2007
                            elif line.find("2006.") >= 0:
                                self.num_adf_subversion = 2006
                            break #are the breaks really necessary?
                        elif line.find("GAMESS VERSION") >= 0:
                            self.filetype = 'GAMESS_US'
                            break
                        elif line.find("Gaussian, Inc.") >= 0:
                            self.filetype = 'Gaussian'
                            break
                        elif line.find("Jaguar") >= 0:
                            self.filetype = 'Jaguar'
                            break
                        elif line.find("PROGRAM SYSTEM MOLPRO") >= 0:
                            self.filetype = 'Molpro'
                            break
                        elif line.find("Northwest Computational Chemistry Package") >= 0:
                            self.filetype = 'NWChem'
                            break
                        elif line.find("* O   R   C   A *") >= 0:
                            self.filetype = 'ORCA'
                            break
                        elif line.find("*Firefly") >= 0:
                            self.filetype = 'Firefly'
                            break
                            
                
                if not self.filetype: 
                    self.filehandle.close() #as we dont know what type it is, we quit
                    self.log.error("ccwatcher can't determine the type of the file, so it isn't able to parse that file correctly.")# Try the -f (force gaussian log) switch if this is surely a Gaussian log file.")
                    return 0 # this is also a "break" so nothing more gets executed in this function

                elif os.path.splitext(filename)[1] in (".gz", ".bz2"): #that is, if we opened it using the 'fileinput' module
                    self.filehandle.close()
                    self.filehandle = fileinput.input(filename, openhook=fileinput.hook_compressed) #close & re-open because there is no seek(0) in fileinput
                    

            if self.filetype == 'Turbomole':
                returnvalue = self.waitForGradientFile(filename)
                if returnvalue:
                    raise IOError,  "No 'gradient' file was created within 15 minutes!"
                    
                    
            if self.filetype == 'ADF': #ADF seems to be a bit of a mess considering logfile types. As .logfile seems newer than .adfout and this newer than .out, use this routine:
                if self.num_adf_subversion >= 2007 and os.path.splitext(filename)[1] not in (".logfile", ".adfout"):                    
                    if os.path.exists(os.path.splitext(filename)[0]+".logfile"):
                        self.log.info("Found a '.logfile' file to your input name. As newer versions of ADF use this,  I'll try to use it.")
                        self.filehandle.close()
                        filename = os.path.splitext(filename)[0]+".logfile"
                        self.filehandle = open(filename)
                    elif os.path.exists(os.path.splitext(filename)[0]+".adfout"):
                        self.log.info("Found no '.logfile' file to your input name, but a '.adfout' file. As some versions of ADF use this,  I'll try to use it.")
                        self.filehandle.close()
                        os.path.splitext(filename)[0]+".adfout"
                        self.filehandle = open(filename,'r')
                    else:
                        self.log.info("ADF filetype was recognized, however no .logfile or .adfout files were found. Trying to use your input file")
                    

            self.logfile_input_lastsize = 0
            self.log.info("Input file opened: "+str(filename)+"\n\ttype seems to be: "+str(self.filetype))
            self.updateRecentFiles(filename)
            if hasattr(self.filehandle, 'seek'): #fileinput objects (compressed or not, here used for GZ and BZIP2) have no seek() method
                self.filehandle.seek(0) #neccessary because else we can't readline() after we have next()ed
            else:
                self.log.error("Couldn't rewind the file?! Contact the author and check file integrity.") # no 'sys.exit(1)' because of gui


        except (OSError,IOError), x:
            print x
            self.log.error("Couldn't open the file! Check filename and read permissions.") # no 'sys.exit(1)' because of gui
            return 0

        else:
            return 1 #needed as feedback for GUI (and CLI?)


    def waitForGradientFile(self, filename): # esp. in Turbomole scans we need to wait for gradient files sometimes
        for i in range(15):
            if os.path.exists(filename):
                self.filehandle = open(filename,'r') #no need to close any old handle before as the Turbomole file recognition is filename-based (no open files!)
                return 0
            else:
                    self.log.warning('No gradient file found - Waiting ('+str((15*60-i*60)/60)+' minutes left - Press Ctrl+C to exit)')
                    time.sleep(60)


    def updateRecentFiles(self, filename): # called and feeded at GUI startup and in openInputFile
    
        while len(self.list_recent_filenames) > 5: #so list is always 5 (old) + 1 (current) items long
            self.list_recent_filenames.pop(-1) #has to be before 'contains' clause
        if filename:
            abs_filename = os.path.abspath(filename)
            if not abs_filename in self.list_recent_filenames and os.access(abs_filename, os.R_OK):
                self.list_recent_filenames.insert(0, abs_filename) #only place where this variable is updated - saved only in QSettings



    def set_plot_mode(self,plot_mode_new):
        self.dict_settings['num_plot_mode'] = plot_mode_new
        self.instance_plotter.setTitle(self.list_plot_mode[self.dict_settings['num_plot_mode']])

        if self.list_scf:
            self.plot(newmode = True)



    def set_logfile_size(self):
        self.logfile_input_lastsize = os.path.getsize(self.tuple_files[self.int_current_file_index])



    def get_logfile_size(self):
        return os.path.getsize(self.tuple_files[self.int_current_file_index])



    def get_logfile_time(self):  #returns a tuple now: (logfile_mtime, job.last_mtime)
        str_joblast_time = 0
        if os.path.exists(self.tuple_files[self.int_current_file_index][:-8]+"job.last"):
            str_joblast_time = os.path.getmtime(self.tuple_files[self.int_current_file_index][:-8]+"job.last")
        return (os.path.getmtime(self.tuple_files[self.int_current_file_index]), str_joblast_time)



    def get_logfile_lasttime(self):  #returns a tuple now: (logfile_mtime, job.last_mtime)
        return (self.str_logfile_lasttime, self.str_joblast_lasttime)
        
        
        
    def set_logfile_lasttime(self):  #str_logfile_lasttime is NOT USED for now
        str_joblast_time = 0
        if os.path.exists(self.tuple_files[self.int_current_file_index][:-8]+"job.last"):
            str_joblast_time = os.path.getmtime(self.tuple_files[self.int_current_file_index][:-8]+"job.last")
        self.str_logfile_lasttime = os.path.getmtime(self.tuple_files[self.int_current_file_index])
        self.str_joblast_lasttime = str_joblast_time
        
        
        
    def grep_store_print_gaussian(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_gaussian']:
                if str_testword in str_line_strip:

                    if str_testword == 'Step number  ':
                        self.str_curr_step = str_line_strip

                    elif str_testword == 'Full point group':
                        if self.str_curr_point_group:
                            lst_output.append([0,"\r---"+self.str_curr_point_group+"                            "])
                        self.str_curr_point_group = str_line_strip
                        
                    elif str_testword == 'SCF Done':
                        if self.str_curr_point_group:
                            lst_output.append([0,"\r---"+str_line_strip+";     "+self.str_curr_point_group])
                            self.str_curr_point_group = ""
                        else:
                            lst_output.append([0,"\r---"+str_line_strip])
                        list_line = str_line_strip.split()	#split the line with 'SCF Done' into a list of strings (at every space)
#                        self.num_added_cycles = self.num_added_cycles+int(list_line[7])
                        self.num_current_cycle = self.num_current_cycle+1#new method: Just use current cycle (=last one+1 for gaussian)
                        self.list_scf.append([self.num_current_cycle, 0, float(list_line[4]), round(627.509*4.184*float(list_line[4]), 4), 0, 0])
                        #Remember that the old added-cycles system is deprecated now: the second value (=list_scf[0]) is never used
                        if float(list_line[4]) < self.lst_bestpoints[-1][-1]:
                            self.lst_bestpoints[-1][-1] = float(list_line[4])                        

                    elif str_testword == "termination": #Gaussian may proceed after this line - so wait a little and check if it does

                        if self.str_curr_point_group:
                            lst_output.append([0,"\r---"+self.str_curr_point_group])
                            self.str_curr_point_group = ""
                        lst_output.append([1,"\r---"+str_line_strip])
                        if time.time() <= os.path.getmtime(self.tuple_files[self.int_current_file_index])+10: #only sleep if the file is fresh 
                            time.sleep(1)
                        str_line_parse = self.filehandle.readline()
                        str_line_strip = str_line_parse.strip()
                        if self.forwardLogText == True:
                            lst_text.append(str_line_strip)

                        if "(Enter" in str_line_parse: #sometimes there is a line in between
                            str_line_parse = self.filehandle.readline()
                            str_line_strip = str_line_parse.strip()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_strip)
                        if "Proceeding" in str_line_parse:
                            lst_output.append([0,"\r---"+str_line_strip])
                            continue
                        else:
                            self.file_terminated = True

                        
                    elif str_testword == "Converged?":
                        if hasattr(self.filehandle, 'tell'):
                                position= self.filehandle.tell()
                        num_yes_count = 0
                        for i in range(4):
                            str_line_parse = self.filehandle.readline()
                            while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                time.sleep(1)
                                str_line_parse = self.filehandle.readline()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_parse.strip())
                            if "YES" in str_line_parse:
                                num_yes_count += 1
                        if self.dict_settings['no_zero_convergence'] == False or (self.dict_settings['no_zero_convergence'] == True and num_yes_count > 0):
                            lst_output.append([0,"\r--- Convergence: "+str(num_yes_count*25)+"% ("+str(num_yes_count)+" of 4 convergence criteria met)."])
                        if hasattr(self.filehandle, 'seek'):
                                self.filehandle.seek(position)


                    elif str_testword in ("Number of steps exceeded", "The SCF is confused.","Gaussian 09:","Gaussian 03:","Gaussian 98:","Rerun with SCF=IntRep.","Warning:","found in this molecule","Atomic number out of range","Stationary point","run aborted","run terminated","NtrErr Called from FileIO.","Error on Z-matrix"):
                        if self.str_curr_point_group:
                            lst_output.append([0,"\r---"+self.str_curr_point_group])
                            self.str_curr_point_group = ""

                        if str_testword in ("Number of steps exceeded", "Stationary point"):
                            lst_output.append([1,"\r---"+str_line_strip+" ("+self.str_curr_step+")"])
                            self.lst_bestpoints.append([0])
                        else:
                            lst_output.append([1,"\r---"+str_line_strip])


                    else: #all other keywords
                        if self.str_curr_point_group:
                            lst_output.append([0,"\r---"+self.str_curr_point_group])
                            self.str_curr_point_group = ""
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text
    
    
    
    def grep_store_print_orca(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []
        #self.num_current_cycle = 0 #needed for orca because its in another line than the energy
        
        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_orca']:

                if str_testword in str_line_strip:
                    
                        
                    #maybe write the current point group here too?
                    if str_testword == 'Total Energy': #maybe it would be better to use 'Total Energy       :' than this double-clausing?
                        if "correction" not in str_line_strip: #seperate condition so we can have a different (or no) else-clause
                            lst_output.append([0,"\r---"+str_line_strip])
                            list_line = str_line_strip.split()
                            self.num_current_cycle = self.num_current_cycle+1
                            self.list_scf.append([self.num_current_cycle, 0, float(list_line[3]), round(627.509*4.184*float(list_line[3]), 4), 0, 0])
                                #list_scf contains 4 entries per line: current cycle, #cumulated cycles--deleted--, E(Hartree), E(kJ/mol). Entries 5 und 6 (=#4 und #5) follow when plotting
                            if float(list_line[3]) < self.lst_bestpoints[-1][-1]:
                                self.lst_bestpoints[-1][-1] = float(list_line[3]) 

                    elif str_testword == 'WARNING':
                    #elif "!!!!!!!" in str_line_strip: #There are several types of "WARNING" messages in ORCA. For the one with the exclamation marks we need the two following lines as well
                        lst_output.append([0,"\r---"+str_line_strip])
                        
                        if "Gradients are needed" in str_line_strip: #So far the only "WARNING" situation in which we just need one line
                            pass
                            
                        else: # "Geometry Optimization" in str_line_strip or "!!!!!!!" in str_line_strip or "your system is" in str_line_strip or "Hessian Matrix ISN'T COMPLETE":
                            str_line_parse = self.filehandle.readline()
                            while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                time.sleep(1)
                                str_line_parse = self.filehandle.readline()
                            str_line_strip = str_line_parse.strip()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_strip)
                            lst_output.append([0,"\r---"+str_line_strip])
                            
                            str_line_parse = self.filehandle.readline()
                            str_line_strip = str_line_parse.strip()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_strip)
                            lst_output.append([0,"\r---"+str_line_strip])
                        
                        if "wavefunction will be stored" in str_line_strip: #"wavefunction IS NOT YET CONVERGED" message
                            str_line_parse = self.filehandle.readline()
                            while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                time.sleep(1)
                                str_line_parse = self.filehandle.readline()
                            str_line_strip = str_line_parse.strip()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_strip)
                            lst_output.append([0,"\r---"+str_line_strip])
                            
                            str_line_parse = self.filehandle.readline()
                            str_line_strip = str_line_parse.strip()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_strip)
                            lst_output.append([0,"\r---"+str_line_strip])
                            
                    elif str_testword == "Number of atoms":
                        self.str_num_atoms = int(str_line_strip.split()[4])
                            
                    elif str_testword == "Displacing":
                        if "3 (-)" in str_line_strip:
                            lst_output.append([0,"\r---Displaced coordinate "+str(int(str_line_strip[14:17]))+" of "+str(self.str_num_atoms)])
                        
                    elif str_testword in ("ORCA TERMINATED", "ORCA finished", "ABORTING THE RUN"): #TODO: Implement multi-step behaviour here if necessary
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.file_terminated = True
                        
                    elif str_testword == "Converged":
                        if hasattr(self.filehandle, 'tell'):
                            position= self.filehandle.tell()
                        num_yes_count = 0
                        num_criteria_count = 0
                        for i in range(7):
                            str_line_parse = self.filehandle.readline()
                            while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                time.sleep(1)
                                str_line_parse = self.filehandle.readline()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_parse.strip())
                            if "YES" in str_line_parse or "NO" in str_line_parse:
                                num_criteria_count  +=1
                                if "YES" in str_line_parse:
                                    num_yes_count += 1
                        if self.dict_settings['no_zero_convergence'] == False or self.dict_settings['no_zero_convergence'] == True and num_yes_count > 0:
                            lst_output.append([0,"\r--- Convergence: "+str(num_yes_count*(100/num_criteria_count))+"% ("+str(num_yes_count)+" of "+str(num_criteria_count)+" convergence criteria met)."])
                        if hasattr(self.filehandle, 'seek'):
                                self.filehandle.seek(position)

                    elif str_testword in ("OPTIMIZATION RUN DONE"):
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.lst_bestpoints.append([0])
                        
                    elif str_testword in ("O   R   C   A"):                        
                        lst_output.append([1,"\r---"+str_line_strip])

                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text
    


    def grep_store_print_gamess_us(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_gamess_us']:

                if str_testword in str_line_strip:
                    if str_testword == "ENERGY IS":#maybe write the current point group here too?
                        if "FINAL" in str_line_strip:  #FINAL needed because we search for "FINAL *** ENERGY" only
                            lst_output.append([0,"\r---"+str_line_strip])
                            list_line = str_line_strip.split()	#split the line with 'SCF Done' into a list of strings (at every space)
                            #self.num_added_cycles = self.num_added_cycles+int(list_line[6])
                            self.num_current_cycle = self.num_current_cycle+1
                            self.list_scf.append([self.num_current_cycle, 0, float(list_line[4]), round(627.509*4.184*float(list_line[4]), 4), 0, 0])
                                #list_scf contains 4 entries per line: current cycle, #cumulated cycles--deleted--, E(Hartree), E(kJ/mol). Entries 5 und 6 (=#4 und #5) follow when plotting

                    elif str_testword == "GAMESS TERMINATED": #TODO: Implement multi-step behaviour here if necessary
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.file_terminated = True

                    elif str_testword in ("EQUILIBRIUM GEOMETRY","GAMESS VERSION"):
                        lst_output.append([1,"\r---"+str_line_strip])

                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text


    def grep_store_print_jaguar(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_jaguar']:

                if str_testword in str_line_strip:
                    if str_testword == "SCF energy":#maybe write the current point group here too?
                        lst_output.append([0,"\r---"+str_line_strip])
                        list_line = str_line_strip.split()	#split the line with 'SCF Done' into a list of strings (at every space)
                        #self.num_added_cycles = self.num_added_cycles+int(list_line[-1])
                        self.num_current_cycle = self.num_current_cycle+1
                        self.list_scf.append([self.num_current_cycle, 0, float(list_line[4]), round(627.509*4.184*float(list_line[4]), 4), 0, 0])
                            #list_scf contains 4 entries per line: current cycle, #cumulated cycles--deleted--, E(Hartree), E(kJ/mol). Entries 5 und 6 (=#4 und #5) follow when plotting

                    elif str_testword == "completed":
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.file_terminated = True

                    elif str_testword in ("Jaguar version",): 
                        lst_output.append([1,"\r---"+str_line_strip])

                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text




    def grep_store_print_adf2012(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_adf2012']:
                
                if str_testword in str_line_strip:
                    if str_testword == "current energy":#maybe write the current point group here too?
                        str_line_strip = str_line_parse.strip()
                        if self.forwardLogText == True:
                            lst_text.append(str_line_strip)
                        list_line = str_line_strip.split()
                        lst_output.append([0,"\r---"+str_line_strip])
                        self.num_current_cycle = self.num_current_cycle+1
                        self.list_scf.append([self.num_current_cycle, 0, float(list_line[4]), round(627.509*4.184*float(list_line[4]), 4), 0, 0])
                        #list_scf contains 4 entries per line: current cycle, #cumulated cycles--deleted--, E(Hartree), E(kJ/mol). Entries 5 und 6 (=#4 und #5) follow when plotting
                        if float(list_line[4]) < self.lst_bestpoints[-1][-1]:
                            self.lst_bestpoints[-1][-1] = float(list_line[4])                        

                    elif str_testword == "energy change":
                        lst_output.append([0,"\r--- Convergence: "])
                        if hasattr(self.filehandle, 'tell'):
                            position= self.filehandle.tell()
                        for i in range(6):
                            str_line_parse = self.filehandle.readline()
                            while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                time.sleep(1)
                                str_line_parse = self.filehandle.readline()
                            str_line_strip = str_line_parse.strip()
                            lst_output.append([0,"\r---\t"+str_line_strip[24:]])
                            if self.forwardLogText == True:
                                lst_text.append(str_line_parse.strip())
                        if hasattr(self.filehandle, 'seek'):
                            self.filehandle.seek(position)

                    elif str_testword in ("GEOMETRY CONVERGED"," ADF "):
                        lst_output.append([1,"\r---"+str_line_strip])
                        
                    elif str_testword == "LINEAR TRANSIT point":
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.lst_bestpoints.append([0])
                        
                    elif str_testword == "has finished":
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.file_terminated = True


                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text


    def grep_store_print_adf(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_adf']:

                if str_testword in str_line_strip:
                    if str_testword == "Geometry Convergence Tests":#maybe write the current point group here too?
                        if hasattr(self.filehandle, 'tell'):
                            position= self.filehandle.tell()
                        for count in range(4):
                            str_line_parse = self.filehandle.readline()
                            while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                time.sleep(1)
                                str_line_parse = self.filehandle.readline()
                        str_line_strip = str_line_parse.strip()
                        if self.forwardLogText == True:
                            lst_text.append(str_line_strip)
                        list_line = str_line_strip.split()
                        lst_output.append([0,"\r---"+str_line_strip])
                        #self.num_added_cycles = self.num_added_cycles+1
                        self.num_current_cycle = self.num_current_cycle+1
                        self.list_scf.append([self.num_current_cycle, 0, float(list_line[-1]), round(627.509*4.184*float(list_line[-1]), 4), 0, 0])
                            #list_scf contains 4 entries per line: current cycle, #cumulated cycles--deleted--, E(Hartree), E(kJ/mol). Entries 5 und 6 (=#4 und #5) follow when plotting
                        if hasattr(self.filehandle, 'seek'):
                                self.filehandle.seek(position)

#TODO: find a reasonable termination criterion for adf<2012 - TERMINATION and END are ubiquitious

                    elif str_testword == "Criterion    Conv.":
                        num_yes_count = 0
                        for i in range(7):
                            str_line_parse = self.filehandle.readline()
                            while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                time.sleep(1)
                                str_line_parse = self.filehandle.readline()
                            if self.forwardLogText == True:
                                lst_text.append(str_line_parse.strip())
                            if "YES" in str_line_parse:
                                num_yes_count += 1
                        if self.dict_settings['no_zero_convergence'] == False or self.dict_settings['no_zero_convergence'] == True and num_yes_count > 0:
                            lst_output.append([0,"\r--- Convergence: "+str(num_yes_count*(100/7))+"% ("+str(num_yes_count)+" of 7 convergence criteria met)."])


                    elif str_testword in ("Geometry CONVERGED","Amsterdam Density Functional"):
                        lst_output.append([1,"\r---"+str_line_strip])


                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text


    def grep_store_print_molpro(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_molpro']:

                if str_testword in str_line_strip:
                    if str_testword in ("!RHF", "!UHF", "!RKS", "MP2", "!MP2", "MP3", "MP4"):
                        if "ENERGY" in str_line_parse: #cannot write this in the line above because other "RHF" lines need not fall in the 'else' clause
                            lst_output.append([0,"\r---"+str_line_strip])
                            list_line = str_line_strip.split()
                            #self.num_added_cycles = self.num_added_cycles+1
                            self.num_current_cycle = self.num_current_cycle+1
                            self.list_scf.append([self.num_current_cycle, 0, float(list_line[4]), round(627.509*4.184*float(list_line[4]), 4), 0, 0])
                                #list_scf contains 4 entries per line: current cycle, #cumulated cycles--deleted--, E(Hartree), E(kJ/mol). Entries 5 und 6 (=#4 und #5) follow when plotting


                    elif str_testword in ("SYSTEM MOLPRO",): #why 'Label'?: "LABEL"): #Catch the next four lines as well
                        lst_output.append([1,"\r---"+str_line_strip])
                        str_line_parse = self.filehandle.readline()
                        while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                            time.sleep(1)
                            str_line_parse = self.filehandle.readline()
                        str_line_strip = str_line_parse.strip()
                        if self.forwardLogText == True:
                            lst_text.append(str_line_strip)
                        lst_output.append([0,"\r---"+str_line_strip])
                        str_line_parse = self.filehandle.readline()
                        str_line_strip = str_line_parse.strip()
                        if self.forwardLogText == True:
                            lst_text.append(str_line_strip)
                        lst_output.append([0,"\r---"+str_line_strip])
                        str_line_parse = self.filehandle.readline()
                        str_line_strip = str_line_parse.strip()
                        if self.forwardLogText == True:
                            lst_text.append(str_line_strip)
                        lst_output.append([0,"\r---"+str_line_strip])
                        str_line_parse = self.filehandle.readline()
                        str_line_strip = str_line_parse.strip()
                        if self.forwardLogText == True:
                            lst_text.append(str_line_strip)
                        lst_output.append([0,"\r---"+str_line_strip])

                    elif str_testword == "Variable memory released":
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.file_terminated = True                        

                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed
            
        return lst_output,  lst_text


    def grep_store_print_turbomole(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []
        str_lastprinted = ''

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?
            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_turbomole']:

                if str_testword in str_line_strip:
                    if str_testword == "SCF energy":#maybe write the current point group here too?
                        list_line = str_line_strip.split()
                        #self.num_added_cycles = self.num_added_cycles+1
                        self.num_current_cycle = self.num_current_cycle+1
                        self.list_scf.append([self.num_current_cycle, 0, float(list_line[6]), round(627.509*4.184*float(list_line[6]), 4), 0, 0])
                        lst_output.append([0,"\r---"+str_line_strip])
                    
                    else: #all other keywords
                        if str_line_strip != str_lastprinted:
                            lst_output.append([0,"\r---"+str_line_strip])
                            str_lastprinted = str_line_strip
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed


        if self.get_logfile_time()[1] > self.get_logfile_lasttime()[1]:
            self.log.debug("Updated 'job.last found - parsing...")
            fhandle_joblast = open(self.tuple_files[self.int_current_file_index][:-8]+"job.last",  'r') #works because log text gets overwritten in job.last, not appended
            
#            for str_line_parse in fhandle_joblast: #does not really work as there can be no "readline" afterwards
            str_line_parse = fhandle_joblast.readline()
            while len(str_line_parse): #could we use a simple 'for line in file' approach here?
                str_line_strip = str_line_parse.strip()
                if self.forwardLogText == True:
                    lst_text.append(str_line_strip)
                
                for str_testword in self.dict_settings['list_keywords_turbomole']:
                    if str_testword in str_line_strip:
                        
                        if str_testword == "CONVERGENCE INFORMATION":
                            if hasattr(fhandle_joblast, 'tell'):
                                position= fhandle_joblast.tell()
                            num_yes_count = 0
                            for i in range(7):
                                str_line_parse = fhandle_joblast.readline()
                                while len(str_line_parse) == 0: #instead of "sleep"ing here to make sure the next line gets written before parsing: just check if it's non-existant and sleep if so
                                    time.sleep(1)
                                    str_line_parse = fhandle_joblast.readline()
                                if self.forwardLogText == True:
                                    lst_text.append(str_line_parse.strip())
                                if "yes" in str_line_parse:
                                    num_yes_count += 1
                            if self.dict_settings['no_zero_convergence'] == False or self.dict_settings['no_zero_convergence'] == True and num_yes_count > 0:
                                lst_output.append([0,"\r--- Convergence: "+str(num_yes_count*20)+"% ("+str(num_yes_count)+" of 5 convergence criteria met)."])
                            if hasattr(fhandle_joblast, 'seek'):
                                fhandle_joblast.seek(position)
                            
                        elif str_testword == "satisfied":
                            lst_output.append([1,"\r---"+str_line_strip])
                        
                        else:
                            if str_line_strip != str_lastprinted:
                                lst_output.append([0,"\r---"+str_line_strip])
                                str_lastprinted = str_line_strip
                        
                str_line_parse = fhandle_joblast.readline()
                    
                self.set_logfile_lasttime()
                
#Here: GEO_OPT termination has been moved out so it can be checked even if the gradient file was not updated (needed!)
            
        return lst_output,  lst_text


    def grep_store_print_nwchem(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_nwchem']:

                if str_testword in str_line_strip:
                    if str_testword == "energy =": #maybe write the current point group here too?
                        if str_line_strip.split()[2] == "energy" and "Total" in str_line_strip:
                            list_line = str_line_strip.split()
                            #self.num_added_cycles = self.num_added_cycles+1
                            self.num_current_cycle = self.num_current_cycle+1
                            self.list_scf.append([self.num_current_cycle, 0, float(list_line[4]), round(627.509*4.184*float(list_line[4]), 4), 0, 0])
                            lst_output.append([0,"\r---"+str_line_strip])

                    elif str_testword == "Total times":
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.file_terminated = True                       

                    elif str_testword in ("Optimization converged","(NWChem)"):
                        lst_output.append([1,"\r---"+str_line_strip])
                        
                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text
        

    def grep_store_print_firefly(self):
        """Checks for keywords in the read line and prints or interpretes it"""
        
        lst_output = []			#lst_output format: see parser()
        lst_text = []

        str_line_parse = self.filehandle.readline()
        while len(str_line_parse): #could we use a simple 'for line in file' approach here?

            str_line_strip = str_line_parse.strip()
            if self.forwardLogText == True:
                lst_text.append(str_line_strip)

            for str_testword in self.dict_settings['list_keywords_firefly']:

                if str_testword in str_line_strip:
                    if str_testword == "FINAL ENERGY": #maybe write the current point group here too?
                        list_line = str_line_strip.split()
                        self.num_current_cycle = self.num_current_cycle+1
                        self.list_scf.append([self.num_current_cycle, 0, float(list_line[3]), round(627.509*4.184*float(list_line[3]), 4), 0, 0])
                        lst_output.append([0,"\r---"+str_line_strip])

                    elif str_testword == "FIREFLY TERMINATED":
                        lst_output.append([1,"\r---"+str_line_strip])
                        self.file_terminated = True
            
                    elif str_testword in ("*Firefly","LOCATED"):
                        lst_output.append([1,"\r---"+str_line_strip])

                    else: #all other keywords
                        lst_output.append([0,"\r---"+str_line_strip])
            
            str_line_parse = self.filehandle.readline()  #read in next line and then a new 'while'-run, if needed

        return lst_output,  lst_text


    def plot(self,  newmode = False):
        """Calculates columns 4 and 5 of scf list (zero-point reset) and plots the energy values according to the rules of the chosen method (0-5)
            Mind that this function has the ABSOLUTE control of the plot data, so it e.g. has to remove the old gnupy stuff (via gnupy.flushdata) itself, too
        """
        
        self.instance_plotter.flushdata()
        
        str_title_inside = os.path.basename(self.tuple_files[self.int_current_file_index])
#        self.instance_plotter('show term')   self.instance_plotter('show output')
        if self.dict_settings['use_gui'] == True:
            self.instance_plotter.commands('set output "'+self.str_plotfilename+'"') #this seems to be necessary every time plot is called...

        if 0 <= self.dict_settings['num_plot_mode'] <= 2:
            self.instance_plotter.commands('set format y "%.9f"') #sadly, there is no such function in qwtplot
            self.instance_plotter.setYAxisLabel("Energy/Hartree")
#REM            self.instance_plotter.commands('set ylabel "Energy/Hartree"')
            
            if self.dict_settings['num_plot_mode'] == 0:                #Gnuplot autoscale
                self.instance_plotter.commands('set autoscale y') #qwtplot is ALWAYS autoscaled -> ignore this
                self.instance_plotter.commands('set autoscale x')
#REM                self.instance_plotter.data(.listolists, int_xcolumn, int_ycolumn, str_legendtitle)

                self.instance_plotter.data(self.list_scf, 1, 3, str_title_inside)
                for list in self.preservedSCF: #add every preserved list singlely so they don't get connected to one line
                    self.instance_plotter.data(list, 1, 3, "Reference")
                    
            elif self.dict_settings['num_plot_mode'] == 1:              #My own autoscale (more margin space)
                self.rescale(2, newmode)
                self.instance_plotter.data(self.list_scf, 1, 3, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 3, "Reference")
                    
            elif self.dict_settings['num_plot_mode'] == 2:              #Set highest point zero
                if self.preservedSCF:
                    max_preserved = max(max(map(lambda x:x[2], file)) for file in self.preservedSCF) #read this as: max(generator)
                    num_maxvalue = max(max(map(lambda x:x[2], self.list_scf)), max_preserved) #find the biggest scf value in actual or preserved lists
                else: 
                    num_maxvalue = max(map(lambda x:x[2], self.list_scf))

                for list_pres_scf in self.preservedSCF:
                    for scfvalues in list_pres_scf:
                        scfvalues[4] = scfvalues[2] - num_maxvalue #put in every fifth column the hartree difference to the highest point
                for list_scf_entry in self.list_scf:
                    list_scf_entry[4] = list_scf_entry[2] - num_maxvalue #"map(,)" erzeugt neue liste mit nur den 3. einträgen der ersten
                self.rescale(4, newmode)
                self.instance_plotter.data(self.list_scf, 1, 5, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 5,  "Reference")
            self.instance_plotter.plot()
    
        elif 3 <= self.dict_settings['num_plot_mode'] <= 10:
            self.instance_plotter.commands('set format y "%.3f"')
            self.instance_plotter.setYAxisLabel("Energy/kJ per mole")
            
            if self.dict_settings['num_plot_mode'] == 3:                #in kJ/mole
                self.rescale(3, newmode)
                self.instance_plotter.data(self.list_scf, 1, 4, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 4,  "Reference")

            elif self.dict_settings['num_plot_mode'] == 4:              #in kJ/mole with highest point zero
                if self.preservedSCF:
                    max_preserved = max(max(map(lambda x:x[3], file)) for file in self.preservedSCF)
                    num_maxvalue = max(max(map(lambda x:x[3], self.list_scf)), max_preserved) #find the biggest scf value in actual or preserved lists
                else: 
                    num_maxvalue = max(map(lambda x:x[3], self.list_scf))
                for list_pres_scf in self.preservedSCF:
                    for scfvalues in list_pres_scf:
                        scfvalues[5] = scfvalues[3] - num_maxvalue
                for list_scf_entry in self.list_scf:
                    list_scf_entry[5] = list_scf_entry[3] - num_maxvalue
                self.rescale(5, newmode)
                self.instance_plotter.data(self.list_scf, 1, 6, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 6, "Reference")

            elif self.dict_settings['num_plot_mode'] == 5:              #in kJ/mole with first non-preserved point zero
#                list_scf_copy = self.list_scf[:]
                num_firstvalue = self.list_scf[0][3] #old:list_scf_copy[0][3]
                for list_pres_scf in self.preservedSCF:
                    for list_scfvalues in list_pres_scf:
                        list_scfvalues[5] = list_scfvalues[3] - num_firstvalue
                for list_scf_entry in self.list_scf:
                    list_scf_entry[5] = list_scf_entry[3] - num_firstvalue
                self.rescale(5, newmode)
                self.instance_plotter.data(self.list_scf, 1, 6, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 6, "Reference")

            elif self.dict_settings['num_plot_mode'] == 6:              #in kJ/mole, lowest-points scale
                num_firstvalue = self.list_scf[0][3] #old:list_scf_copy[0][3]
                for list_pres_scf in self.preservedSCF:
                    for list_scf in list_pres_scf: 
                        list_scf[5] = list_scf[3] - num_firstvalue #old:num_maxvalue
                for list_scf_entry in self.list_scf:
                    list_scf_entry[5] = list_scf_entry[3] - num_firstvalue #old:num_maxvalue
                self.rescale(5, newmode, num_perc_plot_zoom = 0.2)
                self.instance_plotter.data(self.list_scf, 1, 6, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 6,  "Reference")

            elif self.dict_settings['num_plot_mode'] == 7:              #in kJ/mole, lowest-points scale
                num_firstvalue = self.list_scf[0][3] #old:list_scf_copy[0][3]
                for list_pres_scf in self.preservedSCF:
                    for list_scf in list_pres_scf:
                        list_scf[5] = list_scf[3] - num_firstvalue #old:num_maxvalue
                for list_scf_entry in self.list_scf:
                    list_scf_entry[5] = list_scf_entry[3] - num_firstvalue #old:num_maxvalue
                self.rescale(5, newmode, num_perc_plot_zoom = 0.03)
                self.instance_plotter.data(self.list_scf, 1, 6,  str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 6, "Reference")

            elif self.dict_settings['num_plot_mode'] == 8:              #in kJ/mole, lowest-points scale
                num_firstvalue = self.list_scf[0][3] #old:list_scf_copy[0][3]
                for list_pres_scf in self.preservedSCF:
                    for list_scf in list_pres_scf:
                        list_scf[5] = list_scf[3] - num_firstvalue #old:num_maxvalue
                for list_scf_entry in self.list_scf:
                    list_scf_entry[5] = list_scf_entry[3] - num_firstvalue #old:num_maxvalue
                self.rescale(5, newmode, num_perc_plot_zoom = 0.005)
                self.instance_plotter.data(self.list_scf, 1, 6, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 6, "Reference")
                    
            elif self.dict_settings['num_plot_mode'] == 9:              #in kJ/mole, Recent points zoom
                num_firstvalue = self.list_scf[0][3] 
                for list_pres_scf in self.preservedSCF:
                    for list_scf in list_pres_scf:
                        list_scf[5] = list_scf[3] - num_firstvalue #reference old lists to first point of the current list
                for list_scf_entry in self.list_scf:
                    list_scf_entry[5] = list_scf_entry[3] - num_firstvalue #reference current list to first point
                self.rescale(5, newmode, num_perc_plot_zoom = 0.01)
                self.instance_plotter.data(self.list_scf, 1, 6, str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 6, "Reference")
                
            elif self.dict_settings['num_plot_mode'] == 10:              #in kJ/mole, Recent points more zoom
                num_firstvalue = self.list_scf[0][3] 
                for list_pres_scf in self.preservedSCF:
                    for list_scf in list_pres_scf:
                        list_scf[5] = list_scf[3] - num_firstvalue #reference old lists to first point of the current list
                for list_scf_entry in self.list_scf:
                    list_scf_entry[5] = list_scf_entry[3] - num_firstvalue #reference current list to first point
                self.rescale(5, newmode, num_perc_plot_zoom = 1)
                self.instance_plotter.data(self.list_scf, 1, 6,  str_title_inside)
                for list in self.preservedSCF:
                    self.instance_plotter.data(list, 1, 6, "Reference")
                
            self.instance_plotter.plot()


        else:
            self.log.warning("No valid y scaling mode. Falling back to 'Customized scaling' (Mode 1).")
            self.dict_settings['num_plot_mode'] = 1
            self.plot()
    
    
    
    def rescale(self, col, newmode, num_perc_plot_zoom = 1):
        """Re-sets the Gnuplot Y and X scale according to num_x&yrange_surplus (Additional space above and below the max/min values, resp.) and num_perc_plot_zoom setting (View percentage beginning from lowest value) """        

        if self.dict_settings['str_plot_system'] == 'gnuplot' or not self.dict_settings.get('use_gui', False): #any Gnuplot case
            list_scf_copy = self.list_scf[:]
            if self.preservedSCF:
                for preservedlist in self.preservedSCF: # We need one big list anyway, right (so no calling max() and min() on all lists seperately like we do now in plot)
                    list_scf_copy.extend(preservedlist) #make a big list of all lists and look for the highest and lowest scf value (does this make sense?)
                    
            if len(list_scf_copy) == 1: #just one data point
                self.instance_plotter.commands('set autoscale y') #don't care about the chosen plot mode
                self.instance_plotter.commands('set autoscale x')
                
            else:
                if self.dict_settings['num_plot_mode'] == 9: #maybe also restrict to the last 10-20 points?
                    list_sorted_scf = sorted(map(lambda x : x[col], list_scf_copy))
                    num_max_scf = list_sorted_scf[-1]
                    if num_max_scf == list_scf_copy[0][col]: # dirty workaround: if first point is the highest of all it makes a plot-mode-9-plot unpretty -> kill it
                        num_max_scf = list_sorted_scf[-2]
                    num_min_scf = min(self.list_scf[-1][col], self.list_scf[-2][col]) #use the original list_scf because the copy has been modified
                    num_2nd_min_scf = max(self.list_scf[-1][col], self.list_scf[-2][col])
                    num_min_scf = num_min_scf - ((num_max_scf - num_min_scf) * num_perc_plot_zoom * 0.1) # show a little more below the min_scf (should look better)

                elif self.dict_settings['num_plot_mode'] == 10: #last ten point considered only
                    list_scf_last8points = self.list_scf[-8:]
                    list_sorted_scf = sorted(map(lambda x : x[col], list_scf_last8points)) #is it OK to re-use list_sorted_scf here?
                    num_max_scf = list_sorted_scf[-1]
                    num_min_scf = list_sorted_scf[0]
                    num_2nd_min_scf = list_sorted_scf[1] 

                else:
                    list_sorted_scf = sorted(map(lambda x : x[col], list_scf_copy))
                    num_max_scf = list_sorted_scf[-1]
                    num_min_scf = list_sorted_scf[0]
                    num_2nd_min_scf = list_sorted_scf[1] #we need an assignment to num_2nd_min_scf even if plot_mode!=6-10


                #y rescale
                num_yrange_max_no_surplus = num_min_scf + (num_max_scf - num_min_scf) * num_perc_plot_zoom #we always keep the lower scf value while zooming => max=min+(positive dif-value)*zoom
                
                if num_2nd_min_scf >= num_yrange_max_no_surplus: #if only one point would be in the new plot; should work for plot modes 9&10 as well
                        self.log.info("Only one point in chosen y interval => Rescaling to fit at least two points.") #mind: this is _without_ the surplus so there's in the end always a nice surplus-frame around the plot line
                        num_yrange_max_no_surplus =  num_2nd_min_scf #re-define the upper end to fit both lowest points

                num_yrange_surplus = 0.15 * (num_yrange_max_no_surplus - num_min_scf)
                if  num_yrange_surplus < 0.00000001 and self.dict_settings['num_plot_mode'] in (0, 1, 2): # For very small numbers, gnuplot refuses to plot
                    num_yrange_surplus = 0.00000001 #set this value as small as possible without gnuplot refusing to plot
                if  num_yrange_surplus < 0.00001 and self.dict_settings['num_plot_mode'] in (3, 4, 5, 6, 7, 8, 9, 10): # For kJ/mol, it's different
                    num_yrange_surplus = 0.00001                    
                yrange_max = num_yrange_max_no_surplus + num_yrange_surplus
                yrange_min = num_min_scf - num_yrange_surplus                    

                self.log.debug("Y-Range should be "+str(yrange_min)+' to '+str(yrange_max)+' (surplus: '+str(num_yrange_surplus)+') (GP scaling)')
                self.instance_plotter.commands('set yrange ['+str(yrange_min)+':'+str(yrange_max)+']')


                #x rescale
                if self.dict_settings['num_plot_mode'] in (6, 7, 8, 9, 10): # for every plot mode that does not show every point
                    list_cycles_viewable = [] #all the 'cycle' values from the viewable points. i. e. that have scf values below yrange_max
                    for entry in list_scf_copy:  #remember, the list includes preservedSCF
                        if entry[col] <= yrange_max and entry[col] >= yrange_min:
                            if self.dict_settings['num_plot_mode'] == 10:
                                if entry[0] in map(lambda x : x[0], list_scf_last8points): #entry[col] in ... does not really work because of rounding issues or so
                                    list_cycles_viewable.append(entry[0])
                            else:
                                list_cycles_viewable.append(entry[0])
                    num_max_cycle = max(list_cycles_viewable)#_sorted_ doesn't help because we can't find the matching 'cycles' number easily
                    num_min_cycle = min(list_cycles_viewable)
                else:
                    list_sorted_cycles = sorted(map(lambda list_scf:list_scf[0], list_scf_copy))
                    num_max_cycle = list_sorted_cycles[-1]
                    num_min_cycle = list_sorted_cycles[0]
                
                
                num_xrange_surplus = (num_max_cycle - num_min_cycle) * 0.05 #instead of num_perc_plot_ysurplus because smaller is better?
                if num_xrange_surplus < 0.1: #basically, "if theres just one data point"=>x1-x2=0
                    num_xrange_surplus = 0.1
                xrange_max = num_max_cycle + num_xrange_surplus
                xrange_min = num_min_cycle - num_xrange_surplus
                self.log.debug("X-Range should be: "+str(xrange_min)+" to "+str(xrange_max)+" (surplus: "+str(num_xrange_surplus)+") (GP scaling)")
                self.instance_plotter.commands('set xrange ['+str(xrange_min)+':'+str(xrange_max)+']')


        else: #Qwt
        
            list_scf_copy = self.list_scf[:]
            if self.preservedSCF:
                for preservedlist in self.preservedSCF: # We need one big list anyway, right (so no calling max() and min() on all lists seperately like we do now in plot)
                    list_scf_copy.extend(preservedlist) #make a big list of all lists and look for the highest and lowest scf value (does this make sense?)

            if len(list_scf_copy) == 1:
                x00 = 0.5 
                x11 = 1.5 
                y00 = list_scf_copy[0][col] - 0.5
                y11 = list_scf_copy[0][col] + 0.5
                x0 = x00
                x1 =x11
                y0 = y00
                y1 = y11
                self.log.debug("Zoombase should be: "+str(x00)+" to "+str(x11)+" and "+str(y00)+" to "+str(y11)+" (Qwt single point)")
                self.log.debug("Range should be: "+str(x0)+" (X0) to "+str(x1)+" (X1) and "+str(y0)+" (Y0) to "+str(y1)+" (Y1) (Qwt single point)")              
                xmargin = 1.5 #"+1" so its surely big enough
                ymargin =  1.5

            else: #For more than one available points: calculate zoombase first (n00) then zoom (n0)
                #x Scaling
                list_sorted_cycles = sorted(map(lambda x : x[0], list_scf_copy))
                num_max_cycle = list_sorted_cycles[-1]
                num_xrange_surplus = (num_max_cycle - 0) * 0.04
                x11 = num_max_cycle + num_xrange_surplus
                x00 = 1 - num_xrange_surplus
                #y Scaling
                list_sorted_scf = sorted(map(lambda x : x[col], list_scf_copy))
                num_max_scf = list_sorted_scf[-1]
                num_min_scf = list_sorted_scf[0]
                if not num_max_scf - num_min_scf: #no energy difference between points
                    y00 = list_scf_copy[0][col] - 0.5
                    y11 = list_scf_copy[0][col] + 0.5
                    num_yrange_surplus = 0.5
                    #num_xrange_surplus = 0.5 #why tht, actually?
                else:
                    num_yrange_surplus = 0.05 * (num_max_scf - num_min_scf)
                    y00 = num_min_scf - num_yrange_surplus
                    y11 = num_max_scf + num_yrange_surplus
                self.log.debug("Zoombase should be: "+str(x00)+" to "+str(x11)+" and "+str(y00)+" to "+str(y11)+" (ysurplus "+str(num_yrange_surplus)+", xsurplus "+str(num_xrange_surplus)+")")

                if not newmode:
                    list_visiblepoints = []
                    for point in list_scf_copy:
                        if self.instance_plotter.currentY0 < point[col] < self.instance_plotter.currentY1 and self.instance_plotter.currentX0 < point[0] < self.instance_plotter.currentX1:
                            list_visiblepoints.append(point)
                    list_visiblepoints.append(self.list_scf[-1])
                    max_visible_x = self.list_scf[-1][0]
                    min_visible_x = min(map(lambda x : x[0], list_visiblepoints))
                    max_visible_y = max(map(lambda x : x[col], list_visiblepoints))
                    min_visible_y = min(map(lambda x : x[col], list_visiblepoints))
                    if not max_visible_y - min_visible_y: #no energy difference between points
                        num_yrange_surplus = 0.5
                    else:
                        num_yrange_surplus = (max_visible_y - min_visible_y) * 0.05
                    num_xrange_surplus = (max_visible_x - min_visible_x) * 0.04
                    y0 = min_visible_y - num_yrange_surplus
                    y1 = max_visible_y + num_yrange_surplus
                    x0 = min_visible_x - num_xrange_surplus
                    x1 = self.list_scf[-1][0] + num_xrange_surplus
                    self.log.debug("Range should be: "+str(x0)+" (X0) to "+str(x1)+" (X1) (surplus: "+str(num_xrange_surplus)+") and "+str(y0)+" (Y0) to "+str(y1)+" (Y1) (surplus: "+str(num_yrange_surplus)+") (Qwt custom scaling)")

                else: #use zoombase as zoom
                    x0 = x00
                    x1 = x11
                    y0 = y00
                    y1 = y11
                        
                x0margin = 1 - x0 # x of first point is always 1
                x1margin = x1 - num_max_cycle
                #y0margin =  num_min_scf - y0
                #y1margin = y1 - num_max_scf
                xmargin = max(x0margin, x1margin)+1 #+1 so its surely bigger
                ymargin = max(x0margin, x1margin)+1
            
            self.instance_plotter.setProposedScale(x00, x11, y00, y11, x0, x1, y0, y1, xmargin, ymargin)
            self.log.debug("XMargin should be "+str(xmargin)+", YMargin should be "+str(ymargin))


    def parse(self):
        lst_output = [] #list of 2-element lists: [code,string] where code is 0: normal output, 1: parser message, 2: (Qt) SIGNAL
        lst_text = []
        
        if self.get_logfile_size() < self.logfile_input_lastsize: #if the file is smaller, stop after a sleep and re-check
            time.sleep(1)
            if self.get_logfile_size() < self.logfile_input_lastsize:
                if self.str_curr_point_group: #do this more clean for the case of non-Gaussian parsing
                    lst_output.append([0,"\r---"+self.str_curr_point_group])
                    self.str_curr_point_group = '' #needed?
                lst_output.append([2,"\nFile truncated! Exiting."])
                self.file_terminated = True
        else:
            if self.get_logfile_size() > self.logfile_input_lastsize: #only start parsing when the file was xtended
                self.log.debug("Logfile updated => parsing")
                self.set_logfile_size()

                #grep-store-print needs to be before "too-old-check" I guess - so it gets parsed at least once

                if self.filetype == 'ORCA':
                    output = self.grep_store_print_orca()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'GAMESS_US':
                    output = self.grep_store_print_gamess_us()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'Molpro':
                    output = self.grep_store_print_molpro()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'NWChem':
                    output = self.grep_store_print_nwchem()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'Turbomole':
                    output = self.grep_store_print_turbomole()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'Jaguar':
                    output = self.grep_store_print_jaguar()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'ADF':
                    if self.num_adf_subversion >= 2012:
                        output = self.grep_store_print_adf2012()
                    else:
                        output = self.grep_store_print_adf()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'Firefly':
                    output = self.grep_store_print_firefly()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                elif self.filetype == 'Gaussian':# or self.dict_settings.get('force_gaussian', False):
                    output = self.grep_store_print_gaussian()
                    lst_output.extend(output[0])
                    lst_text.extend(output[1])
                else:
                    print "FIXME: Unknown filetype in 'parse()' function!"
                    

                if len(self.list_scf) > self.num_scf_entries_last_plotted:
                    self.log.debug("New SCF energies found => renewing plot")
                    if self.num_scf_entries_last_plotted == 0:
                        self.plot(newmode = True) #needed for Qwt to determine if we need a full rescale
                    else:
                        self.plot()
                    self.num_scf_entries_last_plotted = len(self.list_scf)
                    
            if int(time.time()) >= int(self.get_logfile_time()[0])+60*self.dict_settings['num_max_file_age_minutes'] and self.file_terminated is not True: #this check needs to be in the "inner if-clause" so it can set self.terminated - the additional check ("not terminated?") is so no "too old" message pops up falsely
                   self.log.debug("File is too old ("+str(int(time.time()))+" >= "+str(int(self.get_logfile_time()[0]))+" + 60*"+str(self.dict_settings['num_max_file_age_minutes'])+")")
                   if self.str_curr_point_group:
                        lst_output.append([0,"\r---"+self.str_curr_point_group])
                        self.str_curr_point_group = '' #needed?
                   lst_output.append([2,"\nFile is older than chosen maximum age => Stopping"])
                   self.file_terminated = True

            if self.filetype == 'Turbomole' and not os.path.exists(self.tuple_files[self.int_current_file_index][:-8]+"GEO_OPT_RUNNING"): # GEO_OPT_RUNNING can be removed without updating gradient -> check needed here (outside GSP_turbomole)
                returnvalue = self.check_geo_files(self.tuple_files[self.int_current_file_index][:-8])
                if returnvalue:
                    lst_output.append([2,"\nNo GEO_OPT_RUNNING found => Stopping"])
                    self.file_terminated = True
                else:
                    time.sleep(5) #it can take a few seconds for RUNNING/FAILED/CONVERGED to get created => check again after 5 seconds
                    if not os.path.exists(self.tuple_files[self.int_current_file_index][:-8]+"GEO_OPT_RUNNING"):
                        self.check_geo_files(self.tuple_files[self.int_current_file_index][:-8]) #we do not need the returnvalue, as we terminate here anyway (just parse FAILED before)
                        lst_output.append([2,"\nNo GEO_OPT_RUNNING found => Stopping"])
                        self.file_terminated = True

        if self.file_terminated == True: #this needs to be outside the big "else" clause because of the truncation-'if'
            lst_output.append([2,"\nParser termination due to end of calculation, given conditions or previous problems (see above)."])
        #print lst_output
        return lst_output, lst_text


    def check_geo_files(self, basename):
        if os.path.exists(basename+"GEO_OPT_FAILED"):
            handle_geofailed = open(basename+"GEO_OPT_FAILED", 'r')
            for line in handle_geofailed:
                lst_output.append([1,"\r---"+line.strip()])
            handle_geofailed.close()
            return 1

        elif os.path.exists(basename+"GEO_OPT_CONVERGED"):
            return 1
            


    def cleanup(self):
        """Calls the functions for closing input file(s) and removing temp files. Also closes Gnuplot instance!"""
        
        self.log.debug("Cleaning up...")
        if hasattr(self, 'instance_plotter'):
            self.instance_plotter.flushdata() #does everything the old __del__ function did


#    closeInputfiles
        if self.filehandle: 
            self.filehandle.close()
            self.filehandle = None #needed for "if filehandle" clauses
            self.log.debug("Input file closed")#+str(self.tuple_files[self.int_current_file_index])) currentFileIndex mostly returns the new instead of the old file 

#    removePlotfile
        if os.access(self.str_plotfilename, os.W_OK):
            try:
                os.remove(self.str_plotfilename)
            except WindowsError,  e: #seems to only be a problem in windows (e. g., gnuplot.close() not executed, file opened somewhere else(?))
                self.log.warning("Warning: Could not remove temp plot file images/plot.png.")
                self.log.warning(e)
            else:
                self.log.debug("Deleted temporary file 'images/plot.png'")

