# -*- 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.

"""__init__.py
"""

import os
import sys
import time
import signal #for cli loop
import getopt #read shell arguments
import logging
from ccw_messages import version , str_syntax_message, str_h_for_help_message, list_verbosity, str_thisisccw_pt1, str_minihelp
import ccw_parser
import ccw_settingshandler

__version__ = version


def main(str_scriptpath, gui=None):
    """Takes a hardcoded dict of settings (from ccw_messages.py) and updates them with the settings read in from the ini file and the command line options.
   Then the CLI or GUI are started and given a parser instance."""
    print str_thisisccw_pt1
    print str_minihelp

    global initlog
    parser = None
    first_start = False
    initlog = logging.getLogger('initlogger')
    logging.basicConfig(level = logging.INFO, format = '%(levelname)s: %(message)s', stream = sys.stderr)
    
    str_basepath = os.path.split(str_scriptpath)[0] # base path is below the images folder, so ccwatcher in uninstalled or installed on windows; works also if scriptpath is os.getcwd from dummy
#    str_basepath = os.path.join(os.path.split(os.path.dirname(str_scriptpath))[0], 'share', 'ccwatcher') #case: script is in some bin/ dir -> base path is probably /usr or /usr/local
        
    try:

        str_settingsfile = ccw_settingshandler.getSettingsFileLocation()
        if os.path.exists(str_settingsfile):
            dict_settings = ccw_settingshandler.readSettings(str_settingsfile) #Remember: transform and write-back now happen automatically!
        else:
            dict_settings = {} #this is ONLY so the condition in the next line is fulfilled in the case of a non-existant settingsfile
        if dict_settings.get('configversion', None) != version:
            import ccw_setup_cli
            if dict_settings.get('configversion', None) == None:
                initlog.warning("No config file detected. Running ccw_setup.py ...") #issuing this message is the only pointof the first_start system actually
                first_start = True
                    
            else:
                initlog.warning("The config file version seems to be different from the program's. Starting ccw_setup.py non-interactively...")
                print(ccw_setup_cli.main(bool_silent=True))[0] #print all the basic info gathered in the setup
                print("\n")
        
            dict_settings = ccw_settingshandler.readSettings(str_settingsfile)
#            initlog.warning("The config file version seems to be different from the program's. CCWatcher might not work correctly until you execute ccw_setup.py!")
        if gui:
            dict_settings['use_gui'] = True #needs to be before 'getopts'  so we can e.g. do the start-gui-empty check there
            
        dict_settings['num_plot_mode'] = 5 #we need a std starting value
        
        dict_settings, tuple_inputnames = getopts(dict_settings)
#        dict_settings.update(dict_opts)
        dict_settings['str_basepath'] = str_basepath
        initlog.setLevel(list_verbosity[dict_settings['num_verbosity']])


        if dict_settings.get('use_gui',False) == False: # CLI Mode
            if first_start:
                txtout, avopath, gnuplotpath, plotsystem = ccw_setup_cli.main()
                dict_settings['str_gnuplot_path'] = gnuplotpath
                dict_settings['str_plot_system'] = plotsystem #Its OK if 'qwt' is set here - CLI uses gnuplot anyway

            parser = ccw_parser.fileParser(dict_settings)
            parser.setInputFiles(tuple_inputnames)    #here: guess Log file if none is given; "None found" message
            if len(parser.tuple_files[0]):
                cli_mode_loop(parser)


        else: # GUI Mode
            try:
                import ccwatcher_gui
            except: #"except Exception:" would probably be better as it does not catch BaseExceptions like SystemExit
                initlog.critical("GUI not found: Check installation!")
            else:
                app = ccwatcher_gui.QtGui.QApplication(sys.argv)
                app.setOrganizationName("ccwatcher")  #Not "... team" or sth so qt uses the same config file folder ("ccwatcher")
                app.setOrganizationDomain("http://sourceforge.net/projects/ccwatcher/")
                app.setApplicationName("ccwatcher")
                
                if first_start:
                    from ccwatcher_gui import ccw_setup_gui
                    csmainwin = ccw_setup_gui.ccwsetup_mainwin() # assignment to a variable is necessary despite what eric5 says
                    app.exec_()
                    if csmainwin.result():
                        avopath = csmainwin.avopathEdit.text()
                        dict_settings['str_avogadro_path'] = avopath
                        gnuplotpath = csmainwin.gnupathEdit.text()
                        dict_settings['str_gnuplot_path'] = gnuplotpath
                        plotsystem = csmainwin.plotsystem
                        dict_settings['str_plot_system'] = plotsystem
                        messages = ccw_setup_cli.write_ini(avopath, gnuplotpath, plotsystem)
                        ccw_setup_gui.QtGui.QMessageBox.information(csmainwin,"Saving ccwatcher settings", messages)

                parser = ccw_parser.fileParser(dict_settings)
                parser.setInputFiles(tuple_inputnames) # "bigger than empty" check happens in getopts already        
                cmainwin = ccwatcher_gui.CCW_mainwin(parser) # assignment to a variable is necessary despite what eric5 says
                sys.exit(app.exec_()) #openfiles happens inside

    finally: #Save the current directory name for next start and do a parser.cleanup()
        if parser:
            if hasattr(parser, 'tuple_files') and parser.tuple_files[parser.int_current_file_index]:
                dirname = os.path.dirname(parser.tuple_files[parser.int_current_file_index])
                if not os.path.exists(os.path.join(dirname,'ccwatcher.py')): #meaning 'if we are in some user-chosen dir'
                    str_settingsfile = ccw_settingshandler.getSettingsFileLocation()
                    dict_settings = ccw_settingshandler.readSettings(str_settingsfile)
                    dict_settings['str_lastpath'] = dirname
                    print "Terminating: Saving current path, ", ccw_settingshandler.writeSettings(dict_settings, str_settingsfile) #do not use the parser's dict as this contains session vars like basepath as well
            parser.cleanup()



def getopts(dict_settings):
    """ Read the rest of the settings from the command line, using the getopt package """

    dict_opts = dict_settings

    try:
        options,arguments = getopt.getopt(sys.argv[1:], 'hgfos:v:m:', ['version'])
        for entry in options:
            
            if entry[0] == '-h': #help flag
                print str_syntax_message           
                sys.exit(0)
                
            elif entry[0] == '--version':
                print "\nccwatcher version: ", __version__, "\n"
                
            elif entry[0] == '-s': #scalemode
                if 0 <= int(entry[1]) <= 10:
                    if dict_opts['str_plot_system'] == 'qwt':
                        if int(entry[1]) == 0:
                            initlog.error("The current plotting system (Qwt) does not a mode 0; changed to mode 1...")
                            dict_opts['num_plot_mode'] = 1
                        elif int(entry[1]) >= 6:
                            initlog.error("The current plotting system (Qwt) does not have modes > 5; changed to mode 5...")
                            dict_opts['num_plot_mode'] = 5
                        else: 
                            dict_opts['num_plot_mode'] = int(entry[1])
                    else:
                        dict_opts['num_plot_mode'] = int(entry[1])
                        initlog.info("Plot mode set to "+entry[1])	#maybe no logger so message is surely given (else it would depend on -v/-s option order) ?
                else:
                    raise ValueError("Scalemode has to be a number between 0 and 10!")
                    
            elif entry[0] == '-v': #verbosity level
                if 0 <= int(entry[1]) <= 4:
                     dict_opts['num_verbosity'] = 4 - int(entry[1])	#reverse order of logger levels -> verbosity
                     initlog.info("Verbosity set to "+entry[1])
                else:
                    raise ValueError("Verbosity level has to be a number between 0 and 4!")
                    
            elif entry[0] == '-m': #multifile handling: 0 no multifile, 1 several plot lines, 2 one accumulated line
                int_entry = int(entry[1])
                if 0 <= int_entry <= 3:
                    dict_opts['num_multifile_opt'] = int_entry
                else:
                    raise ValueError("Multifile mode has to be a number between 0 and 3!")
                initlog.info("Multifile mode set to "+entry[1])
                
            elif entry[0] == '-g': #gui mode
                dict_opts['use_gui'] = True
                
#            elif entry[0] == '-f': #force gaussian mode (myparser)
#                dict_opts['force_gaussian'] = True

            elif entry[0] == '-o':
                dict_opts['single_run'] = True
                
            else:
                initlog.error('Unknown option: "'+entry[0]+'".'+str_h_for_help_message)
                sys.exit(1)


        tuple_inputnames = tuple(map(os.path.abspath, arguments)) #Do not check file accessibility here; parser.openInputFile produces much more informative errors (?)
        if len(tuple_inputnames) == 0:
            if dict_opts['no_guess_mode'] == True and dict_opts['use_gui'] == True:
                    initlog.info("No input file given and guess mode is deactivated: Starting GUI empty...")
            else:
                initlog.warning("No input file given: Guess mode..."+str_h_for_help_message)

            #tuple_inputnames = guessLogFile() #now in parser

        if len(tuple_inputnames) > 1: #No elif so we can e.g. test the multifile variable on the newly found tuple from guessLogFile
            if tuple_inputnames[0][-8:] == 'gradient' and dict_opts.get('num_multifile_opt', None) != 3:
                initlog.warning("Turbomole scan mode: Multifile mode set to 3 instead of "+str(dict_opts.get('num_multifile_opt', '(none)')))
                dict_opts['num_multifile_opt'] = 3 #Turbomole scan setting
            elif dict_opts.get('num_multifile_opt', None) not in (1, 2, 3): #None is needed here instead of False because "False in (0,1)" evaluates to True
                initlog.warning("Illegal multifile mode: Changing it to 1 instead of "+str(dict_opts.get('num_multifile_opt', '(none)')))
                dict_opts['num_multifile_opt'] = 1
                
        elif len(tuple_inputnames) == 1:
            if dict_opts.get('num_multifile_opt', None) in (1, 2, 3):
                dict_opts['num_multifile_opt'] = 0
                initlog.warning("Not enough files given! Multifile mode turned off.")
            if os.path.isdir(tuple_inputnames[0]):
#           ... tuple_inputnames = guessLogFile ... has been moved to parser
                tuple_inputnames = os.path.abspath(tuple_inputnames[0])
            


    except getopt.GetoptError, x:
        initlog.critical("Option error: "+str(x)+".\n"+str_h_for_help_message)
        sys.exit(1)

    except ValueError,x:
        initlog.critical('Value Error: A value given to an option was probably not a valid number '+str_h_for_help_message)
        if str(x).strip():
            initlog.critical('\tInterpreter message: "'+str(x)+'"')
        sys.exit(1)
        
    initlog.debug("Scaling mode is: "+str(dict_opts['num_plot_mode']))
    return dict_opts, tuple_inputnames



def cli_mode_loop(parser):
#    lst_o_lst_output = []
    try:
        initlog.debug("Starting main loop...")
        timeoutfunction_readtty = TimeoutFunction(func_readttyloop, 3)
        print "\n"

        int_successreturnvalue = parser.openNextFile()
        while len(parser.tuple_files) > parser.int_current_file_index + 1: # when there are more files to parse after this one
            
            if int_successreturnvalue:

                if parser.dict_settings.get('num_multifile_opt', None) == 3: #do the same readttyloop as in normal parsing - mainly for turbomole scans
                    
                    handle_parser_output(parser)
                    while parser.file_terminated == False:
                                handle_parser_output(parser)
                                if  sys.platform.startswith('win'):
                                    print "\rWaiting for new log entries...",
                                    time.sleep(2)
                                else:
                                    try:
                                        num_plot_mode_new = timeoutfunction_readtty()
                                        if num_plot_mode_new == parser.dict_settings['num_plot_mode']:
                                            print "\rPlot mode unchanged (already set to %d).                          " % num_plot_mode_new
                                        else:
                                            parser.set_plot_mode(num_plot_mode_new)
                                            print "\rPlot mode set to %d (%s).                                           " %(num_plot_mode_new,parser.list_plot_mode[num_plot_mode_new])
                                    except TimeoutFunctionException:
                                        pass
                                    
                else:
                    parser.parse() #but don't handle the text output

            else: 
                initlog.error("An error occurred while reading the file. See above for further details.")
                
            int_successreturnvalue = parser.openNextFile()
        
        if int_successreturnvalue: #at this point, this is the last file to parse (see while loop above)
            

            handle_parser_output(parser) #like this there are no 2 seconds readttyloop even though file is terminated

            while parser.file_terminated == False and parser.dict_settings.get('single_run', False) == False:
                        handle_parser_output(parser)
                       
                        if  sys.platform.startswith('win'):
                            print "\rWaiting for new log entries...",
                            time.sleep(2)
                        else:
                            try:
                                num_plot_mode_new = timeoutfunction_readtty()
                                if num_plot_mode_new == parser.dict_settings['num_plot_mode']:
                                    print "\rPlot mode unchanged (already set to %d).                          " % num_plot_mode_new
                                else:
                                    parser.set_plot_mode(num_plot_mode_new)
                                    print "\rPlot mode set to %d (%s).                                           " %(num_plot_mode_new,parser.list_plot_mode[num_plot_mode_new])
                            except TimeoutFunctionException:
                                pass
                           
#                        handle_parser_output(parser) #why again parsing?
            print "\n"
            
        else: 
            initlog.error("An error occurred while reading the file. See above for further details.")

    except ValueError,x: #needed for readttyloop
        if str(x).strip():
            initlog.critical(str(x))
        print '\t', str_h_for_help_message
        
    except KeyboardInterrupt:
        initlog.critical("\rReceived an interrupt command from the keyboard: Exiting...                                   ")



def handle_parser_output(parser, self = None):

    lstolst_output = parser.parse()[0]
    
    for lst_line in lstolst_output:
        if self == None:
            if lst_line[0] == 0:
                print lst_line[1]+40*" "
            elif lst_line[0] == 1:
                print "\t"+lst_line[1]+36*" "
            elif lst_line[0] == 2:
                print "\t\t"+lst_line[1]+32*" "
            else:
                initlog.error("FIXME: Illegal Output code identified!")
        else:
            initlog.error("No valid parser output identified")



def func_readttyloop():
    """Reads (and interprets) from stdin in an endless loop"""
    while 1:
        try:
            time.sleep(0.5)
            string_rawinput_value = raw_input("\rWaiting for new log entries (Enter q to quit or 0-10 to rescale)...")
            if string_rawinput_value.strip() == 'q':
                raise KeyboardInterrupt
            elif len(string_rawinput_value) > 0 and 0 <= int(string_rawinput_value) <= 10:
                return int(string_rawinput_value)
            else:
                raise ValueError

        except ValueError:
            print "\rInvalid input.",70*" "


    

def dummy_startgui():
    main(os.getcwd(), gui=True)



class TimeoutFunctionException(Exception):
    """Exception to raise on a timeout"""
    pass


class TimeoutFunction: #inspired by http://www.pycs.net/users/0000231/weblog/2004/10/
    def __init__(self, function, timeout):
        self.timeout = timeout
        self.function = function

    def handle_timeout(self, signum, frame):
        raise TimeoutFunctionException()

    def __call__(self, *args):
        old = signal.signal(signal.SIGALRM, self.handle_timeout)                
        signal.alarm(self.timeout)                                              
        try:                                                                    
            result = self.function(*args)                                       
        finally:                                                                
            signal.signal(signal.SIGALRM, old)                                  
        signal.alarm(0)                                                         
        return result 

   
if __name__ == '__main__':
        main(__file__)
