# -*- coding: ascii -*-
#
# Copyright 2007 - 2013
# Andr\xe9 Malo or his licensors, as applicable
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
=================
 Shell utilities
=================

Shell utilities.
"""
__author__ = "Andr\xe9 Malo"
__docformat__ = "restructuredtext en"

import errno as _errno
import fnmatch as _fnmatch
import os as _os
import shutil as _shutil
import subprocess as _subprocess
import sys as _sys
import tempfile as _tempfile

cwd = _os.path.dirname(_os.path.abspath(_sys.argv[0]))

class ExitError(RuntimeError):
    """ Exit error """
    def __init__(self, code):
        RuntimeError.__init__(self, code)
        self.code = code
        self.signal = None


class SignalError(ExitError):
    """ Signal error """
    def __init__(self, code, signal):
        ExitError.__init__(self, code)
        import signal as _signal
        self.signal = signal
        for key, val in vars(_signal).items():
            if key.startswith('SIG') and not key.startswith('SIG_'):
                if val == signal:
                    self.signalstr = key[3:]
                    break
        else:
            self.signalstr = '%04d' % signal


def native(path):
    """ Convert slash path to native """
    path = _os.path.sep.join(path.split('/'))
    return _os.path.normpath(_os.path.join(cwd, path))


def cp(src, dest):
    """ Copy src to dest """
    _shutil.copy2(native(src), native(dest))


def cp_r(src, dest):
    """ Copy -r src to dest """
    _shutil.copytree(native(src), native(dest))


def rm(dest):
    """ Remove a file """
    try:
        _os.unlink(native(dest))
    except OSError as e:
        if _errno.ENOENT != e.errno:
            raise

def rm_rf(dest):
    """ Remove a tree """
    dest = native(dest)
    if _os.path.exists(dest):
        for path in files(dest, '*'):
            _os.chmod(native(path), 0o644)
        _shutil.rmtree(dest)


mkstemp = _tempfile.mkstemp


def _pipespawn(argv, env):
    """ Pipe spawn """
    # pylint: disable = R0912
    import pickle as _pickle
    fd, name = mkstemp('.py')
    try:
        _os.write(fd, ((r"""
import os
import pickle
import subprocess
import sys

argv = pickle.loads(%(argv)s)
env = pickle.loads(%(env)s)
if 'X_JYTHON_WA_PATH' in env:
    env['PATH'] = env['X_JYTHON_WA_PATH']

p = subprocess.Popen(argv, env=env)
result = p.wait()
if result < 0:
    print("\n%%d 1" %% (-result))
    sys.exit(2)
if result == 0:
    sys.exit(0)
print("\n%%d" %% (result & 7,))
sys.exit(3)
        """.strip() + "\n") % {
            'argv': repr(_pickle.dumps(argv)),
            'env': repr(_pickle.dumps(dict(env))),
        }).encode('utf-8'))
        fd, _ = None, _os.close(fd)
        if _sys.platform == 'win32':
            argv = []
            for arg in [_sys.executable, name]:
                if ' ' in arg or arg.startswith('"'):
                    arg = '"%s"' % arg.replace('"', '\\"')
                argv.append(arg)
            argv = ' '.join(argv)
            shell = True
            close_fds = False
        else:
            argv = [_sys.executable, name]
            shell = False
            close_fds = True

        res = 0
        if 'X_JYTHON_WA_PATH' in env:
            env['PATH'] = env['X_JYTHON_WA_PATH']

        proc = _subprocess.Popen(argv,
            shell=shell,
            stdin=_subprocess.PIPE,
            stdout=_subprocess.PIPE,
            close_fds=close_fds,
            env=env,
        )
        try:
            proc.stdin.close()
            result = proc.stdout.read()
        finally:
            res = proc.wait()
        if res != 0:
            if res == 2:
                signal, code = list(map(int, result.splitlines()[-1].split()))
                raise SignalError(code, signal)
            elif res == 3:
                code = int(result.splitlines()[-1].strip())
                raise ExitError(code)
            raise ExitError(res)

        return result.decode('latin-1')
    finally:
        try:
            if fd is not None:
                _os.close(fd)
        finally:
            _os.unlink(name)


def _filepipespawn(infile, outfile, argv, env):
    """ File Pipe spawn """
    import pickle as _pickle
    fd, name = mkstemp('.py')
    try:
        _os.write(fd, (("""
import os
import pickle
import sys

infile = pickle.loads(%(infile)s)
outfile = pickle.loads(%(outfile)s)
argv = pickle.loads(%(argv)s)
env = pickle.loads(%(env)s)

if infile is not None:
    infile = open(infile, 'rb')
    os.dup2(infile.fileno(), 0)
    infile.close()
if outfile is not None:
    outfile = open(outfile, 'wb')
    os.dup2(outfile.fileno(), 1)
    outfile.close()

pid = os.spawnve(os.P_NOWAIT, argv[0], argv, env)
result = os.waitpid(pid, 0)[1]
sys.exit(result & 7)
        """.strip() + "\n") % {
            'infile': repr(_pickle.dumps(_os.path.abspath(infile))),
            'outfile': repr(_pickle.dumps(_os.path.abspath(outfile))),
            'argv': repr(_pickle.dumps(argv)),
            'env': repr(_pickle.dumps(env)),
        }))
        fd, _ = None, _os.close(fd)
        if _sys.platform == 'win32':
            argv = []
            for arg in [_sys.executable, name]:
                if ' ' in arg or arg.startswith('"'):
                    arg = '"%s"' % arg.replace('"', '\\"')
                argv.append(arg)
            argv = ' '.join(argv)
            close_fds = False
            shell = True
        else:
            argv = [_sys.executable, name]
            close_fds = True
            shell = False

        p = _subprocess.Popen(
            argv, env=env, shell=shell, close_fds=close_fds
        )
        return p.wait()
    finally:
        try:
            if fd is not None:
                _os.close(fd)
        finally:
            _os.unlink(name)


def spawn(*argv, **kwargs):
    """ Spawn a process """
    if _sys.platform == 'win32':
        newargv = []
        for arg in argv:
            if not arg or ' ' in arg or arg.startswith('"'):
                arg = '"%s"' % arg.replace('"', '\\"')
            newargv.append(arg)
        argv = newargv
        close_fds = False
        shell = True
    else:
        close_fds = True
        shell = False

    env = kwargs.get('env')
    if env is None:
        env = dict(_os.environ)
    if 'X_JYTHON_WA_PATH' in env:
        env['PATH'] = env['X_JYTHON_WA_PATH']

    echo = kwargs.get('echo')
    if echo:
        print(' '.join(argv))
    filepipe = kwargs.get('filepipe')
    if filepipe:
        return _filepipespawn(
            kwargs.get('stdin'), kwargs.get('stdout'), argv, env
        )
    pipe = kwargs.get('stdout')
    if pipe:
        return _pipespawn(argv, env)

    p = _subprocess.Popen(argv, env=env, shell=shell, close_fds=close_fds)
    return p.wait()


walk = _os.walk


def files(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
    """ Determine a filelist """
    for dirpath, dirnames, filenames in walk(native(base)):
        for item in prune:
            if item in dirnames:
                dirnames.remove(item)

        filenames.sort()
        for name in _fnmatch.filter(filenames, wildcard):
            dest = _os.path.join(dirpath, name)
            if dest.startswith(cwd):
                dest = dest.replace(cwd, '', 1)
            aslist = []
            head, tail = _os.path.split(dest)
            while tail:
                aslist.append(tail)
                head, tail = _os.path.split(head)
            aslist.reverse()
            dest = '/'.join(aslist)
            yield dest

        if not recursive:
            break
        dirnames.sort()


def dirs(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
    """ Determine a filelist """
    for dirpath, dirnames, filenames in walk(native(base)):
        for item in prune:
            if item in dirnames:
                dirnames.remove(item)

        dirnames.sort()
        for name in _fnmatch.filter(dirnames, wildcard):
            dest = _os.path.join(dirpath, name)
            if dest.startswith(cwd):
                dest = dest.replace(cwd, '', 1)
            aslist = []
            head, tail = _os.path.split(dest)
            while tail:
                aslist.append(tail)
                head, tail = _os.path.split(head)
            aslist.reverse()
            dest = '/'.join(aslist)
            yield dest

        if not recursive:
            break


def frompath(executable):
    """ Find executable in PATH """
    # Based on distutils.spawn.find_executable.
    path = _os.environ.get('PATH', '')
    paths = [
        _os.path.expanduser(item)
        for item in path.split(_os.pathsep)
    ]
    ext = _os.path.splitext(executable)[1]
    exts = ['']
    if _sys.platform == 'win32' or _os.name == 'os2':
        eext = ['.exe', '.bat', '.py']
        if ext not in eext:
            exts.extend(eext)

    for ext in exts:
        if not _os.path.isfile(executable + ext):
            for path in paths:
                fname = _os.path.join(path, executable + ext)
                if _os.path.isfile(fname):
                    # the file exists, we have a shot at spawn working
                    return fname
        else:
            return executable + ext

    return None
