#!/usr/bin/env python

# 
# Subprocess class
#

import os
import subprocess
import platform

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

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

class DmSubprocess(subprocess.Popen):

    # Get subprocess instance.
    @classmethod
    def getSubprocess(cls, command, cwd=None):
        close_fds = True
        if platform.system() != 'Windows':
            close_fds = False
        p = DmSubprocess(command, close_fds=close_fds, cwd=cwd)
        return p

    # Execute command
    @classmethod
    def executeCommand(cls, command, cwd=None):
        """ Create subprocess and run it, return subprocess object. """
        p = cls.getSubprocess(command, cwd)
        p.run()
        return p

    # Execute command, ignore errors.
    @classmethod
    def executeCommandAndIgnoreFailure(cls, command, cwd=None):
        """ Create subprocess, run it, igore any failures, and return subprocess object. """
        p = cls.getSubprocess(command, cwd)
        try:
            p.run()
        except CommandFailed, ex:
            p.getLogger().debug('Command failed, stdout: %s, stderr: %s' % (p.getStdOut(), p.getStdErr()))
        return p

    @classmethod
    def executeCommandAndLogToStdOut(cls, command, cwd=None):
        """ Execute command, display output to stdout, maintain log file and return subprocess object. """
        p = cls.getSubprocess(command, cwd)
        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

    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 more appropriate defaults. """
        subprocess.Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
        self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
        self._stdout = None
        self._stderr = None
        self._args = args
        self._cwd = cwd
        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

    def toDict(self):
        result = { 
            'command' : self._args,
            'exitStatus' : self.returncode,
            'stdErr' : self._stderr,
            'stdOut' : self._stdout,
            'workingDir' : self._cwd
        }
        return result

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

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