r""" A really roundabout way to issue a system call

    # FIXME: This function needs some work
    # It should work without a hitch on windows or unix.
    # It should be able to spit out stdout in realtime.
    # Should be able to configure detatchment, shell, and sudo.

    FIXME:
        on a mac ut.cmd('/Users/joncrall/Library/Application Support/ibeis/tomcat/bin/shutdown.sh') will fail due to spaces


    Kwargs:
        quiet (bool) :
        silence (bool) :
        verbose (bool) :
        detatch (bool) :
        shell (bool) :
        sudo (bool) :
        pad_stdout (bool) :
        dryrun (bool) :

    Returns:
        tuple: (None, None, None)

    CommandLine:
        python -m utool.util_cplat --test-cmd
        python -m utool.util_cplat --test-cmd:0
        python -m utool.util_cplat --test-cmd:1
        python -m utool.util_cplat --test-cmd:2
        python -m utool.util_cplat --test-cmd:1 --test-sudo
        python -m utool.util_cplat --test-cmd:2 --test-sudo

    Example0:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> (out, err, ret) = ut.cmd('echo', 'hello world')
        >>> result = ut.repr4(list(zip(('out', 'err', 'ret'), (out, err, ret))), nobraces=True)
        >>> print(result)
        ('out', 'hello world\n'),
        ('err', None),
        ('ret', 0),

    Example1:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> target = ut.codeblock(
        ...      r'''
                 ('out', 'hello world\n'),
                 ('err', None),
                 ('ret', 0),
                 ''')
        >>> varydict = {
        ...    'shell': [True, False],
        ...    'detatch': [False],
        ...    'sudo': [True, False] if ut.get_argflag('--test-sudo') else [False],
        ...    'args': ['echo hello world', ('echo', 'hello world')],
        ... }
        >>> for count, kw in enumerate(ut.all_dict_combinations(varydict), start=1):
        >>>     print('+ --- TEST CMD %d ---' % (count,))
        >>>     print('testing cmd with params ' + ut.repr4(kw))
        >>>     args = kw.pop('args')
        >>>     restup = ut.cmd(args, pad_stdout=False, **kw)
        >>>     tupfields = ('out', 'err', 'ret')
        >>>     output = ut.repr4(list(zip(tupfields, restup)), nobraces=True)
        >>>     ut.assert_eq(output, target)
        >>>     print('L ___ TEST CMD %d ___\n' % (count,))

    Example2:
        >>> # DISABLE_DOCTEST
        >>> # UNSTABLE_DOCTEST
        >>> # ping is not as universal of a command as I thought
        >>> from utool.util_cplat import *  # NOQA
        >>> import utool as ut
        >>> varydict = {
        ...    'shell': [True, False],
        ...    'detatch': [True],
        ...    'args': ['ping localhost', ('ping', 'localhost')],
        ... }
        >>> proc_list = []
        >>> for count, kw in enumerate(ut.all_dict_combinations(varydict), start=1):
        >>>     print('+ --- TEST CMD %d ---' % (count,))
        >>>     print('testing cmd with params ' + ut.repr4(kw))
        >>>     args = kw.pop('args')
        >>>     restup = ut.cmd(args, pad_stdout=False, **kw)
        >>>     out, err, proc = restup
        >>>     proc_list.append(proc)
        >>>     print(proc)
        >>>     print(proc)
        >>>     print(proc.poll())
        >>>     print('L ___ TEST CMD %d ___\n' % (count,))

def cmd(*args, **kwargs):
    r""" A really roundabout way to issue a system call

    # FIXME: This function needs some work
    # It should work without a hitch on windows or unix.
    # It should be able to spit out stdout in realtime.
    # Should be able to configure detatchment, shell, and sudo.

    FIXME:
        on a mac ut.cmd('/Users/joncrall/Library/Application Support/ibeis/tomcat/bin/shutdown.sh') will fail due to spaces


    Kwargs:
        quiet (bool) :
        silence (bool) :
        verbose (bool) :
        detatch (bool) :
        shell (bool) :
        sudo (bool) :
        pad_stdout (bool) :
        dryrun (bool) :

    Returns:
        tuple: (None, None, None)

    CommandLine:
        python -m utool.util_cplat --test-cmd
        python -m utool.util_cplat --test-cmd:0
        python -m utool.util_cplat --test-cmd:1
        python -m utool.util_cplat --test-cmd:2
        python -m utool.util_cplat --test-cmd:1 --test-sudo
        python -m utool.util_cplat --test-cmd:2 --test-sudo

    Example0:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> (out, err, ret) = ut.cmd('echo', 'hello world')
        >>> result = ut.repr4(list(zip(('out', 'err', 'ret'), (out, err, ret))), nobraces=True)
        >>> print(result)
        ('out', 'hello world\n'),
        ('err', None),
        ('ret', 0),

    Example1:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> target = ut.codeblock(
        ...      r'''
                 ('out', 'hello world\n'),
                 ('err', None),
                 ('ret', 0),
                 ''')
        >>> varydict = {
        ...    'shell': [True, False],
        ...    'detatch': [False],
        ...    'sudo': [True, False] if ut.get_argflag('--test-sudo') else [False],
        ...    'args': ['echo hello world', ('echo', 'hello world')],
        ... }
        >>> for count, kw in enumerate(ut.all_dict_combinations(varydict), start=1):
        >>>     print('+ --- TEST CMD %d ---' % (count,))
        >>>     print('testing cmd with params ' + ut.repr4(kw))
        >>>     args = kw.pop('args')
        >>>     restup = ut.cmd(args, pad_stdout=False, **kw)
        >>>     tupfields = ('out', 'err', 'ret')
        >>>     output = ut.repr4(list(zip(tupfields, restup)), nobraces=True)
        >>>     ut.assert_eq(output, target)
        >>>     print('L ___ TEST CMD %d ___\n' % (count,))

    Example2:
        >>> # DISABLE_DOCTEST
        >>> # UNSTABLE_DOCTEST
        >>> # ping is not as universal of a command as I thought
        >>> from utool.util_cplat import *  # NOQA
        >>> import utool as ut
        >>> varydict = {
        ...    'shell': [True, False],
        ...    'detatch': [True],
        ...    'args': ['ping localhost', ('ping', 'localhost')],
        ... }
        >>> proc_list = []
        >>> for count, kw in enumerate(ut.all_dict_combinations(varydict), start=1):
        >>>     print('+ --- TEST CMD %d ---' % (count,))
        >>>     print('testing cmd with params ' + ut.repr4(kw))
        >>>     args = kw.pop('args')
        >>>     restup = ut.cmd(args, pad_stdout=False, **kw)
        >>>     out, err, proc = restup
        >>>     proc_list.append(proc)
        >>>     print(proc)
        >>>     print(proc)
        >>>     print(proc.poll())
        >>>     print('L ___ TEST CMD %d ___\n' % (count,))
    """
    try:
        # Parse the keyword arguments
        verbose, detatch, shell, sudo, pad_stdout = __parse_cmd_kwargs(kwargs)
        quiet = kwargs.pop('quiet', False)
        silence = kwargs.pop('silence', False)
        if pad_stdout:
            sys.stdout.flush()
            print('\n+--------')
        args = __parse_cmd_args(args, sudo, shell)
        # Print what you are about to do
        if not quiet:
            print('[ut.cmd] RUNNING: %r' % (args,))
        # Open a subprocess with a pipe
        if kwargs.get('dryrun', False):
            print('[ut.cmd] Exiting because dryrun=True')
            return None, None, None
        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT, shell=shell,
                                universal_newlines=True
                                # universal_newlines=False
                                )
        hack_use_stdout = True
        if detatch:
            if not quiet:
                print('[ut.cmd] PROCESS DETATCHING. No stdoutput can be reported...')
            # There is no immediate confirmation as to whether or not the script
            # finished. It might still be running for all you know
            return None, None, proc
        else:
            if verbose and not detatch:
                if not quiet:
                    print('[ut.cmd] RUNNING WITH VERBOSE OUTPUT')
                logged_out = []
                for line in _run_process(proc):
                    #line_ = line if six.PY2 else line.decode('utf-8')
                    line_ = line if six.PY2 else line
                    if len(line_) > 0:
                        if not silence:
                            if hack_use_stdout:
                                sys.stdout.write(line_)
                                sys.stdout.flush()
                            else:
                                # TODO make this play nicely with loggers
                                print_(line_)
                        logged_out.append(line)
                try:
                    from utool import util_str  # NOQA
                    # logged_out = util_str.ensure_unicode_strlist(logged_out)
                    out = '\n'.join(logged_out)
                except UnicodeDecodeError:
                    from utool import util_str  # NOQA
                    logged_out = util_str.ensure_unicode_strlist(logged_out)
                    out = '\n'.join(logged_out)
                    # print('logged_out = %r' % (logged_out,))
                    # raise
                (out_, err) = proc.communicate()
                #print('[ut.cmd] out: %s' % (out,))
                if not quiet:
                    try:
                        print('[ut.cmd] stdout: %s' % (out_,))
                        print('[ut.cmd] stderr: %s' % (err,))
                    except UnicodeDecodeError:
                        from utool import util_str  # NOQA
                        print('[ut.cmd] stdout: %s' % (util_str.ensure_unicode(out_),))
                        print('[ut.cmd] stderr: %s' % (util_str.ensure_unicode(err),))

            else:
                # Surpress output
                #print('[ut.cmd] RUNNING WITH SUPRESSED OUTPUT')
                (out, err) = proc.communicate()
            # Make sure process if finished
            ret = proc.wait()
            if not quiet:
                print('[ut.cmd] PROCESS FINISHED')
            if pad_stdout:
                print('L________\n')
            return out, err, ret
    except Exception as ex:
        import utool as ut
        #if isinstance(args, tuple):
        #    print(ut.truepath(args[0]))
        #elif isinstance(args, six.string_types):
        #    print(ut.unixpath(args))
        ut.printex(ex, 'Exception running ut.cmd',
                   keys=['verbose', 'detatch', 'shell', 'sudo', 'pad_stdout'],
                   tb=True)