import os
from subprocess import Popen, PIPE
from io import open

class Command:
    """Contains the needed data to run a command"""
    def __init__(self, args, stdin=None, stdout=None, shell=False):
        self.arguments = args
        self.stdin = stdin
        self.stdout = stdout
        self.shell = shell

class CommandRunner:
    """
    Execute the differents registered commands in the specified order,
    either independantly or as a pipe process chain if required
    """
    def __init__(self, module_name="", log=None):
        self.module_name = module_name
        self.commands = []
        self.processes = []
        self.log = log

    def info(self, text):
        if self.log: self.log.info(text)

    def set_name(self, module_name):
        self.module_name = module_name

    def add_command(self, args, stdin=None, stdout=None, shell=False):
        command = Command(args, stdin, stdout, shell)
        self.commands.append(command)
        return command

    def shcmd(self, args):
        nargs = []
        for arg in args:
            if len(arg.split()) > 1: arg = '"%s"' % arg
            nargs.append(arg)
        return " ".join(nargs)

    def run(self, kw=None):
        if not(self.commands):
            return
        if not(kw): kw = {}
        pipe_top = False
        rc = 0
        for cmd in self.commands:
            if rc != 0:
                break
            stdin, stdout = None, None
            prev_pipe = None
            if cmd.stdin == "PIPE":
                if self.processes:
                    prev_pipe = self.processes[-1]
                    stdin = prev_pipe.stdout
                else:
                    pipe_top = True
                    stdin = PIPE
            if cmd.stdout == "PIPE":
                stdout = PIPE
            elif cmd.stdout:
                stdout = open(cmd.stdout % kw, "wb")

            if kw: args = [a % kw for a in cmd.arguments]
            else: args = cmd.arguments
            
            self.info(" ".join(args))
            # Some commands work only in shell env (e.g. links), so take care
            if cmd.shell:
                p = Popen(self.shcmd(args), stdin=stdin, stdout=stdout,
                          shell=True)
            else:
                p = Popen(args, stdin=stdin, stdout=stdout)
            self.processes.append(p)

            if stdin and prev_pipe:
                # Close stdout to allow <prev_pipe> receiving SIGPIPE
                prev_pipe.stdout.close()
            if stdout != PIPE:
                # Wait until the process is finished if not in a pipe chain
                rc = p.wait()
                if stdout: stdout.close()

        return rc

