#!/usr/bin/env python

# 
# Subprocess class
#

#######################################################################

import os
import subprocess
import platform

from dm.common.utility import loggingManager
from dm.common.exceptions.commandFailed import CommandFailed

#######################################################################

class DmSubprocess(subprocess.Popen):

    def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=None, close_fds=False, shell=True, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, useExceptions=True, quietMode=False):
        """ Overrides Popen constructor with defaults more appropriate DM. """
        subprocess.Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
        self._logger = loggingManager.getLogger(self.__class__.__name__)
        self._stdout = None
        self._stderr = None
        self._args = args
        self._useExceptions = useExceptions
        self._quietMode = quietMode

    def _commandLog(self):
        # Not very useful to show the name of this file.
        # Walk up the stack to find the caller.
        import traceback
        stack =  traceback.extract_stack()
        for i in range(2, len(stack)):
            if stack[-i][0] != stack[-1][0]:
                fileName, lineNumber, functionName, text = stack[-i]
                break
            else:
                fileName = lineNumber = functionName = text = '?'

        self._logger.debug('From [%s:%s] Invoking: [%s]' % (os.path.basename(fileName), lineNumber, self._args))

    def run(self, input=None):
        """ Run subprocess. """
        if not self._quietMode:
            self._commandLog()
        (self._stdout, self._stderr) = subprocess.Popen.communicate(self, input)
        if not self._quietMode:
            self._logger.debug('Exit status: %s' % self.returncode)
        if self.returncode != 0 and self._useExceptions:
            if not self._quietMode:
                self._logger.debug('StdOut: %s' % self._stdout)
                self._logger.debug('StdErr: %s' % self._stderr)
            error = self._stderr.strip()
            if error == '':
                error = self._stdout.strip()
            raise CommandFailed('%s' % (error))
        return (self._stdout, self._stderr) 

    def getLogger(self):
        return self._logger

    def getArgs(self):
        return self._args

    def getStdOut(self):
        return self._stdout

    def getStdErr(self):
        return self._stderr

    def getExitStatus(self):
        return self.returncode

# Convenience function for getting subprocess.
def getSubprocess(command):
    if platform.system() != 'Windows':
        close_fds = True
    else:
        close_fds = False
    p = DmSubprocess(command, close_fds=close_fds)
    return p

# Convenience function for executing command.
def executeCommand(command):
    """ Create subprocess and run it, return subprocess object. """
    p = getSubprocess(command)
    p.run()
    return p

# Convenience function for executing command that may fail, and we do not
# care about the failure.
def executeCommandAndIgnoreFailure(command):
    """ Create subprocess, run it, igore any failures, and return subprocess object. """
    p = getSubprocess(command)
    try:
        p.run()
    except CommandFailed, ex:
        p.getLogger().debug('Command failed, stdout: %s, stderr: %s' % (p.getStdOut(), p.getStdErr()))
    return p

def executeCommandAndLogToStdOut(command):
    """ Execute command, display output to stdout, maintain log file and return subprocess object. """
    p = getSubprocess(command)
    p._commandLog()

    while True:
        outp = p.stdout.readline()
        if not outp:
            break
        print outp,

    retval = p.wait()

    p._logger.debug('Exit status: %s' % retval)

    if retval != 0:
        error = ''
        while True:
            err = p.stderr.readline()
            if not err:
                break
            error += err
        raise CommandFailed(error)
    return p

#######################################################################
# Testing.

if __name__ == '__main__':
    p = DmSubprocess('ls -l', useExceptions=False)
    p.run()
    print p.getStdOut()
    print p.getStdErr()
    print p.getExitStatus()