"""
 * ProcessInterface.py - SettingsGUI - 
 *   Interface to interactive userspace processes
 *
 * Based on code from Jens Diemer <- ToDo - License
 *
 * Using libgsm_tool until there is a python bindung available
 *
 * (C) 2007 by Kristian Mueller <kristian-m@kristian-m.de>
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, 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.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""

import os 
import subprocess
import threading
import signal
import time

class error_poller(threading.Thread):
    def __init__(self, error_stream):
        self.error_stream = error_stream
        threading.Thread.__init__(self)
        ## init
        self.out_data = []
        self.keep_going = True
        self.start()
        
    def run(self):
        while self.keep_going:
            self.error_stream.flush()
            line = self.error_stream.readline()
            if line == "": break
            self.out_data.append(line)

    def stop(self):
        """
        kills process if still running
        """
        if self.process.poll() != None:
            print " already killed"
            return

        self.killed = True
        os.kill(self.process.pid, signal.SIGQUIT)
        ## gice it a second
        time.sleep(1)
        ## have to kill - as readline is blocking.
        os.kill( self.process.pid, signal.SIGKILL )



class async_process(threading.Thread):
    """
    asynchronous subprocess handler

    used to read the output - access through out_data

    not working with Windows - as there is no os.kill() available
    command - the command to be started
    cwd - directory for execution
    timeout - timeout for stop - 
            0 to execution until self.keep_going is set to False
    """
    def __init__(self, command, options, cwd, timeout):
        self.command    = command
        self.options    = options        
        self.cwd        = cwd
        self.timeout    = timeout
        self.process_created = False    ## find out when subprocess() finished
        self.keep_going = True
        
        self.killed = False
        self.out_data = [] # output buffer <- ToDo use a list - for now '\n's
        self.events = []

        threading.Thread.__init__(self)
        
#        print "COMMAND was >>%s<<" % self.command
        self.start()
        
        """
        if timeout > 0:
            self.join( self.timeout )
        else:
            self.keep_going = True
            while self.keep_going == True:
                time.sleep(1)
                
        self.join()
        self.stop()
        
        """
        # provide return code
        #self.returncode = self.process.returncode

    def run(self):
        print "Executes subprocess [%s]" % " ".join([self.command] + self.options)
        self.process = subprocess.Popen(
#                ["libgsmd-tool", "-mshell"], cwd="/usr/bin/",
                [self.command] + self.options,
                cwd = self.cwd,
                bufsize = 1, # no (line) buffer 
                shell = False,
                stdout = subprocess.PIPE,
                stderr = subprocess.PIPE,
                stdin = subprocess.PIPE
            )
        self.process_created = True
        # save output
        while self.keep_going:
            self.process.stdout.flush()
            line = self.process.stdout.readline()
            if line == "": break
            self.out_data.append(line)
            for event in self.events:
                if line.find(event[0]) >= 0:
                    event[1](line)
            
        print "Subprocess terminated"

    ## event_name <- string to search for (e.g. EVENT: blah changed...)
    ## function <- function to call
    def register_event_handler(self, event_name, function, args, kwargs):
        self.events.append([event_name, function, args, kwargs])

    def stop( self ):
        """
        kills process if still running
        """
        if self.process.poll() != None:
            print " already killed"
            return

        self.killed = True
        os.kill( self.process.pid, signal.SIGQUIT )
        ## gice it a second
        time.sleep(1)
        ## have to kill - as readline is blocking.
        os.kill( self.process.pid, signal.SIGKILL )

class ProcessInterface:
    def __init__(self, command):
        self.command = command.split()[0]
        self.options = [command.split()[x] for x in range(1,len(command.split()))]
        self.process = async_process(self.command, self.options, "/", timeout = 0)
#        print "starting <%s>" %self.command
        while self.process.process_created == False:
            time.sleep(0.1)
        self.error_poller = error_poller(self.process.process.stderr)

    ## write string to buffer - new line is attached string
    def write_to_process(self, string):
        ## find out if process is still running before wire
        if self.process.process.poll() == None:
            self.process.process.stdin.write("%s\n" % string)

    ## read back string with all output lines currently in buffer    
    def read_from_process(self):
        out_string = ""
        for x in self.process.out_data:
            out_string += x
        return out_string
    
    ## read back string with all error lines currently in buffer    
    def read_error_from_process(self):
        out_string = ""
        for x in self.error_poller.out_data:
            out_string += x
        return out_string
        
    ## removes first error in list
    def get_error(self):
        if len(self.error_poller.out_data) > 0:
            out_string = self.error_poller.out_data[0]
            self.error_poller.out_data.remove(out_string)
            out_string = out_string.split("\n")[0]
            return out_string
        else:
            return ""

    ## removes first output in list
    def get_output(self):
        if len(self.process.out_data) > 0:
            out_string = self.process.out_data[0]
            self.process.out_data.remove(out_string)
            out_string = out_string.split("\n")[0]
            return out_string
        else:
            return ""

    def register_event_handler(self, event_name, function, args = [], kwargs = {}):
        self.process.register_event_handler(event_name, function, args, kwargs)

    def close_process(self):
        print "trying to kill process"
        self.process.keep_going = False
        self.process.join(1)
        self.process.stop()


class test:
    def do_test(self):
        print "Test interface"
        bash = ProcessInterface("/bin/sh")
        print "subprocess created"

        time_running = 0
        while time_running < 5:
            print "\nEntering while loop"
            time_running = time_running + 1
            bash.write_to_process("echo hello")
            bash.write_to_process("echo error > /dev/stderr")
            time.sleep(0.2)
            print "next output<%s>" % bash.get_output()
#            print "===\nstdout: <%s> \n===" % bash.read_from_process()
            print "next error<%s>" % bash.get_error()
#            print "===\nstderr: <%s> \n===" % bash.read_error_from_process()
#            time.sleep(0.2)

        bash.close_process()
        print "halllooo"

        # ToDo remove    
        ##         self.process = subprocess.Popen(self.command, cwd = self.cwd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = subprocess.PIPE)

#test = test()
#test.do_test()
